@btc-vision/btc-runtime 1.11.0-rc.4 → 1.11.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/advanced/quantum-resistance.md +15 -13
- package/docs/advanced/signature-verification.md +144 -41
- package/docs/api-reference/blockchain.md +95 -12
- package/docs/contracts/op20s-signatures.md +1 -1
- package/docs/core-concepts/blockchain-environment.md +57 -8
- package/package.json +6 -5
- package/runtime/contracts/OP20.ts +9 -7
- package/runtime/env/BlockchainEnvironment.ts +196 -6
- package/runtime/env/consensus/Signatures.ts +33 -2
- package/runtime/hashing/keccak256.ts +321 -0
- package/runtime/index.ts +3 -0
- package/runtime/math/abi.ts +4 -2
- package/runtime/storage/StoredString.ts +2 -2
- package/runtime/types/ExtendedAddress.ts +4 -3
|
@@ -8,7 +8,7 @@ Quantum computers pose a threat to traditional cryptographic schemes:
|
|
|
8
8
|
|
|
9
9
|
| Algorithm | Quantum Threat | OPNet Status |
|
|
10
10
|
|-----------|---------------|--------------|
|
|
11
|
-
| ECDSA | Vulnerable (Shor's algorithm) |
|
|
11
|
+
| ECDSA | Vulnerable (Shor's algorithm) | Supported (deprecated), transition to ML-DSA |
|
|
12
12
|
| Schnorr | Vulnerable (Shor's algorithm) | Supported, with transition plan |
|
|
13
13
|
| ML-DSA | Quantum-resistant | **Fully supported** |
|
|
14
14
|
|
|
@@ -22,12 +22,12 @@ const message = new BytesWriter(32);
|
|
|
22
22
|
message.writeString('Sign this message');
|
|
23
23
|
const messageHash = sha256(message.getBuffer());
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// SignaturesMethods.MLDSA ensures quantum-resistant verification
|
|
26
26
|
const isValid = Blockchain.verifySignature(
|
|
27
27
|
Blockchain.tx.origin, // Signer's address
|
|
28
28
|
signature, // Signature bytes
|
|
29
29
|
messageHash, // Message hash
|
|
30
|
-
|
|
30
|
+
SignaturesMethods.MLDSA // Force ML-DSA (quantum-resistant)
|
|
31
31
|
);
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -264,12 +264,12 @@ public verifySignature(calldata: Calldata): BytesWriter {
|
|
|
264
264
|
message.writeString('Hello, world! This is a test message for MLDSA signing.');
|
|
265
265
|
const messageHash = sha256(message.getBuffer());
|
|
266
266
|
|
|
267
|
-
// Verify with quantum resistance (
|
|
267
|
+
// Verify with quantum resistance (MLDSA)
|
|
268
268
|
const isValid = Blockchain.verifySignature(
|
|
269
269
|
Blockchain.tx.origin,
|
|
270
270
|
signature,
|
|
271
271
|
messageHash,
|
|
272
|
-
|
|
272
|
+
SignaturesMethods.MLDSA // Force ML-DSA
|
|
273
273
|
);
|
|
274
274
|
|
|
275
275
|
const writer = new BytesWriter(1);
|
|
@@ -289,7 +289,7 @@ const isValid = Blockchain.verifySignature(
|
|
|
289
289
|
signer,
|
|
290
290
|
signature,
|
|
291
291
|
messageHash,
|
|
292
|
-
|
|
292
|
+
SignaturesMethods.Schnorr // Let consensus decide (falls back to ML-DSA if Schnorr not allowed)
|
|
293
293
|
);
|
|
294
294
|
```
|
|
295
295
|
|
|
@@ -382,7 +382,7 @@ class QuantumSecureContract extends OP_NET {
|
|
|
382
382
|
Blockchain.tx.origin,
|
|
383
383
|
signature,
|
|
384
384
|
messageHash,
|
|
385
|
-
|
|
385
|
+
SignaturesMethods.MLDSA // Force ML-DSA
|
|
386
386
|
);
|
|
387
387
|
|
|
388
388
|
const writer = new BytesWriter(1);
|
|
@@ -408,7 +408,7 @@ class QuantumSecureContract extends OP_NET {
|
|
|
408
408
|
signer,
|
|
409
409
|
signature,
|
|
410
410
|
messageHash,
|
|
411
|
-
|
|
411
|
+
SignaturesMethods.MLDSA
|
|
412
412
|
);
|
|
413
413
|
|
|
414
414
|
const writer = new BytesWriter(1);
|
|
@@ -451,7 +451,8 @@ OPNet is the first smart contract platform with built-in quantum-resistant crypt
|
|
|
451
451
|
|
|
452
452
|
| Algorithm | Shor's Algorithm Impact | Grover's Algorithm Impact | Status in OPNet |
|
|
453
453
|
|-----------|------------------------|---------------------------|-----------------|
|
|
454
|
-
| ECDSA (Solidity) | **Broken** (polynomial time) | Weakened |
|
|
454
|
+
| ECDSA (Solidity) | **Broken** (polynomial time) | Weakened | N/A |
|
|
455
|
+
| ECDSA (OPNet) | **Broken** (polynomial time) | Weakened | Deprecated |
|
|
455
456
|
| Schnorr (OPNet) | **Broken** (polynomial time) | Weakened | Transition only |
|
|
456
457
|
| ML-DSA (OPNet) | **Secure** | Minimal impact | **Recommended** |
|
|
457
458
|
|
|
@@ -476,7 +477,8 @@ OPNet is the first smart contract platform with built-in quantum-resistant crypt
|
|
|
476
477
|
|
|
477
478
|
| Capability | Solidity | OPNet |
|
|
478
479
|
|------------|:--------:|:-----:|
|
|
479
|
-
| ECDSA verification | Yes |
|
|
480
|
+
| ECDSA verification (Ethereum) | Yes | Yes (deprecated) |
|
|
481
|
+
| ECDSA verification (Bitcoin) | No | Yes (deprecated) |
|
|
480
482
|
| Schnorr verification | No | Yes |
|
|
481
483
|
| ML-DSA-44 verification | No | Yes |
|
|
482
484
|
| ML-DSA-65 verification | No | Yes |
|
|
@@ -558,7 +560,7 @@ public verify(calldata: Calldata): BytesWriter {
|
|
|
558
560
|
this.expectedSigner.value,
|
|
559
561
|
signature,
|
|
560
562
|
hash,
|
|
561
|
-
|
|
563
|
+
SignaturesMethods.MLDSA // Force quantum-resistant ML-DSA
|
|
562
564
|
);
|
|
563
565
|
|
|
564
566
|
const writer = new BytesWriter(1);
|
|
@@ -639,9 +641,9 @@ class OPNetContract extends OP_NET {
|
|
|
639
641
|
|
|
640
642
|
1. **Use `Address.mldsaPublicKey`** - Don't store ML-DSA keys manually; the Address class handles key loading and caching automatically (see [The Address Class](#the-address-class))
|
|
641
643
|
|
|
642
|
-
2. **Force ML-DSA for high-security operations** -
|
|
644
|
+
2. **Force ML-DSA for high-security operations** - Pass `SignaturesMethods.MLDSA` to `Blockchain.verifySignature()` for critical operations (see [Signature Verification](#signature-verification))
|
|
643
645
|
|
|
644
|
-
3. **Use consensus-aware verification for general use** -
|
|
646
|
+
3. **Use consensus-aware verification for general use** - Pass `SignaturesMethods.Schnorr` (the default) to let the network decide which algorithm to use during the transition period. When Schnorr is no longer allowed by consensus, it automatically falls back to ML-DSA
|
|
645
647
|
|
|
646
648
|
4. **Document security level** - When using quantum-resistant signatures, document the ML-DSA level in your contract comments:
|
|
647
649
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# Signature Verification
|
|
2
2
|
|
|
3
|
-
OPNet supports multiple signature schemes for authentication and authorization. This guide covers Schnorr signatures, quantum-resistant ML-DSA, and common verification patterns.
|
|
3
|
+
OPNet supports multiple signature schemes for authentication and authorization. This guide covers Schnorr signatures, ECDSA (secp256k1), quantum-resistant ML-DSA, and common verification patterns.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
|
-
import { Blockchain } from '@btc-vision/btc-runtime/runtime';
|
|
8
|
+
import { Blockchain, SignaturesMethods } from '@btc-vision/btc-runtime/runtime';
|
|
9
9
|
|
|
10
10
|
// Consensus-aware signature verification (recommended)
|
|
11
11
|
// Uses Schnorr during transition period, ML-DSA after quantum deadline
|
|
12
12
|
const isValid: bool = Blockchain.verifySignature(
|
|
13
|
-
Blockchain.tx.origin, //
|
|
13
|
+
Blockchain.tx.origin, // ExtendedAddress
|
|
14
14
|
signature, // Signature bytes
|
|
15
15
|
messageHash, // 32-byte message hash
|
|
16
|
-
|
|
16
|
+
SignaturesMethods.Schnorr // Signature type (default)
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
// Force quantum-resistant verification (always uses ML-DSA)
|
|
@@ -21,13 +21,27 @@ const isValidQuantum: bool = Blockchain.verifySignature(
|
|
|
21
21
|
Blockchain.tx.origin,
|
|
22
22
|
signature,
|
|
23
23
|
messageHash,
|
|
24
|
-
|
|
24
|
+
SignaturesMethods.MLDSA // Force ML-DSA regardless of consensus flags
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// ECDSA verification (deprecated, Ethereum ecrecover model)
|
|
28
|
+
const isValidECDSA: bool = Blockchain.verifyECDSASignature(
|
|
29
|
+
publicKey, // 33, 64, or 65-byte secp256k1 public key
|
|
30
|
+
signature, // 65-byte signature: r(32) || s(32) || v(1)
|
|
31
|
+
messageHash, // 32-byte message hash (typically keccak256)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// ECDSA verification (deprecated, Bitcoin direct verify model)
|
|
35
|
+
const isValidBTC: bool = Blockchain.verifyBitcoinECDSASignature(
|
|
36
|
+
publicKey, // 33, 64, or 65-byte secp256k1 public key
|
|
37
|
+
signature, // 64-byte compact signature: r(32) || s(32)
|
|
38
|
+
messageHash, // 32-byte message hash (typically SHA-256 double hash)
|
|
25
39
|
);
|
|
26
40
|
```
|
|
27
41
|
|
|
28
42
|
## Signature Scheme Comparison
|
|
29
43
|
|
|
30
|
-
OPNet supports
|
|
44
|
+
OPNet supports Schnorr, ECDSA (secp256k1), and quantum-resistant ML-DSA signatures:
|
|
31
45
|
|
|
32
46
|
```mermaid
|
|
33
47
|
---
|
|
@@ -36,6 +50,13 @@ config:
|
|
|
36
50
|
---
|
|
37
51
|
flowchart LR
|
|
38
52
|
subgraph OPNet["OPNet Signature Verification"]
|
|
53
|
+
subgraph ECDSA["ECDSA - Legacy (Deprecated)"]
|
|
54
|
+
E1["Public Key: 33/64/65 bytes"]
|
|
55
|
+
E2["Signature: 64 or 65 bytes"]
|
|
56
|
+
E3["Security: Classical only"]
|
|
57
|
+
E4["Status: Deprecated"]
|
|
58
|
+
end
|
|
59
|
+
|
|
39
60
|
subgraph Schnorr["Schnorr - Traditional"]
|
|
40
61
|
S1["Public Key: 32 bytes"]
|
|
41
62
|
S2["Signature: 64 bytes"]
|
|
@@ -50,7 +71,8 @@ flowchart LR
|
|
|
50
71
|
M4["Status: Recommended"]
|
|
51
72
|
end
|
|
52
73
|
|
|
53
|
-
Q["Quantum Computer Threat"] -.->|"Breaks"|
|
|
74
|
+
Q["Quantum Computer Threat"] -.->|"Breaks"| E3
|
|
75
|
+
Q -.->|"Breaks"| S3
|
|
54
76
|
Q -.->|"Cannot break"| M3
|
|
55
77
|
end
|
|
56
78
|
```
|
|
@@ -60,11 +82,13 @@ flowchart LR
|
|
|
60
82
|
The recommended approach for all signature verification:
|
|
61
83
|
|
|
62
84
|
```typescript
|
|
85
|
+
import { SignaturesMethods } from '@btc-vision/btc-runtime/runtime';
|
|
86
|
+
|
|
63
87
|
Blockchain.verifySignature(
|
|
64
88
|
address: ExtendedAddress, // Signer's address (contains both key references)
|
|
65
89
|
signature: Uint8Array, // Signature bytes
|
|
66
90
|
hash: Uint8Array, // 32-byte message hash
|
|
67
|
-
|
|
91
|
+
signatureType: SignaturesMethods = SignaturesMethods.Schnorr // Signature type
|
|
68
92
|
): boolean
|
|
69
93
|
```
|
|
70
94
|
|
|
@@ -73,13 +97,15 @@ Blockchain.verifySignature(
|
|
|
73
97
|
- `mldsaPublicKey` (1,312 bytes for Level2) - for quantum-resistant ML-DSA signatures
|
|
74
98
|
|
|
75
99
|
**Behavior:**
|
|
76
|
-
-
|
|
77
|
-
-
|
|
100
|
+
- `SignaturesMethods.Schnorr` (default): Uses Schnorr verification if `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag is set, otherwise falls back to ML-DSA
|
|
101
|
+
- `SignaturesMethods.MLDSA`: Always uses ML-DSA (quantum-resistant) verification with ML-DSA-44 (Level2)
|
|
102
|
+
- `SignaturesMethods.ECDSA`: Emits a deprecation error recommending migration to ML-DSA. Use `verifyECDSASignature()` or `verifyBitcoinECDSASignature()` directly instead
|
|
78
103
|
|
|
79
104
|
The method automatically:
|
|
80
105
|
1. Loads the appropriate public key from the address
|
|
81
|
-
2. Selects the correct verification algorithm based on consensus rules
|
|
106
|
+
2. Selects the correct verification algorithm based on the `signatureType` parameter and consensus rules
|
|
82
107
|
3. Handles all internal key formatting
|
|
108
|
+
4. Throws a `Revert` if the signature type is not allowed under current consensus rules
|
|
83
109
|
|
|
84
110
|
## Schnorr Verification
|
|
85
111
|
|
|
@@ -96,7 +122,7 @@ sequenceDiagram
|
|
|
96
122
|
participant SchnorrVerifier as Schnorr Verifier
|
|
97
123
|
participant ExtendedAddress as ExtendedAddress
|
|
98
124
|
|
|
99
|
-
Contract->>Blockchain: verifySignature(address, sig, hash,
|
|
125
|
+
Contract->>Blockchain: verifySignature(address, sig, hash, Schnorr)
|
|
100
126
|
Note over Blockchain: Check consensus flags
|
|
101
127
|
Blockchain->>Blockchain: UNSAFE_QUANTUM_SIGNATURES_ALLOWED?
|
|
102
128
|
|
|
@@ -139,8 +165,8 @@ sequenceDiagram
|
|
|
139
165
|
participant MLDSAVerifier as ML-DSA Verifier
|
|
140
166
|
participant Address as Address
|
|
141
167
|
|
|
142
|
-
Contract->>Blockchain: verifySignature(address, sig, hash,
|
|
143
|
-
Note over Blockchain:
|
|
168
|
+
Contract->>Blockchain: verifySignature(address, sig, hash, MLDSA)
|
|
169
|
+
Note over Blockchain: signatureType = MLDSA
|
|
144
170
|
|
|
145
171
|
Blockchain->>Address: Load mldsaPublicKey
|
|
146
172
|
Note over Address: SHA256 hash stored in address
|
|
@@ -181,6 +207,77 @@ const isValid = Blockchain.verifyMLDSASignature(
|
|
|
181
207
|
|
|
182
208
|
**OPNet uses ML-DSA-44 (Level2) by default.**
|
|
183
209
|
|
|
210
|
+
## ECDSA Verification (Deprecated)
|
|
211
|
+
|
|
212
|
+
OPNet now supports ECDSA (secp256k1) signatures for backward compatibility with Ethereum and Bitcoin ecosystems. These methods are **deprecated** and only available when `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag is set.
|
|
213
|
+
|
|
214
|
+
### Ethereum ECDSA (ecrecover model)
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// Verifies using Ethereum ecrecover: recovers signer from (hash, v, r, s)
|
|
218
|
+
const isValid: bool = Blockchain.verifyECDSASignature(
|
|
219
|
+
publicKey, // secp256k1 public key (33, 64, or 65 bytes)
|
|
220
|
+
signature, // 65-byte signature: r(32) || s(32) || v(1)
|
|
221
|
+
messageHash // 32-byte message hash (typically keccak256)
|
|
222
|
+
);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Bitcoin ECDSA (direct verify model)
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Verifies directly against public key, enforces BIP-0062 low-S normalization
|
|
229
|
+
const isValid: bool = Blockchain.verifyBitcoinECDSASignature(
|
|
230
|
+
publicKey, // secp256k1 public key (33, 64, or 65 bytes)
|
|
231
|
+
signature, // 64-byte compact signature: r(32) || s(32)
|
|
232
|
+
messageHash // 32-byte message hash (typically SHA-256 double hash)
|
|
233
|
+
);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### ECDSA Sub-Types
|
|
237
|
+
|
|
238
|
+
| Sub-Type | Model | Signature Size | Description |
|
|
239
|
+
|----------|-------|---------------|-------------|
|
|
240
|
+
| `ECDSASubType.Ethereum` | ecrecover | 65 bytes (r32 \|\| s32 \|\| v1) | Recovers signer public key from signature |
|
|
241
|
+
| `ECDSASubType.Bitcoin` | Direct verify | 64 bytes (r32 \|\| s32) | Verifies directly against provided public key |
|
|
242
|
+
|
|
243
|
+
### Accepted Public Key Formats
|
|
244
|
+
|
|
245
|
+
Both ECDSA methods accept secp256k1 public keys in these formats:
|
|
246
|
+
|
|
247
|
+
| Format | Size | Prefix | Description |
|
|
248
|
+
|--------|------|--------|-------------|
|
|
249
|
+
| Compressed | 33 bytes | `0x02` or `0x03` | Standard SEC1 compressed |
|
|
250
|
+
| Raw | 64 bytes | None | Raw X \|\| Y coordinates, no prefix |
|
|
251
|
+
| Uncompressed | 65 bytes | `0x04` | Standard SEC1 uncompressed |
|
|
252
|
+
| Hybrid | 65 bytes | `0x06` or `0x07` | SEC1 hybrid (rewritten to `0x04` on host) |
|
|
253
|
+
|
|
254
|
+
### ECDSA Deprecation Warning
|
|
255
|
+
|
|
256
|
+
Both ECDSA methods emit a runtime `WARNING` and are gated behind the `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag. They will throw a `Revert` if called when unsafe signatures are not allowed. Contracts should migrate to `verifySignature()` with ML-DSA for long-term quantum security.
|
|
257
|
+
|
|
258
|
+
## Keccak-256 Hashing
|
|
259
|
+
|
|
260
|
+
OPNet includes a built-in Keccak-256 implementation (Ethereum-compatible, pre-NIST). This is useful for ECDSA-related workflows, Ethereum-style function selectors, and EIP-712 typed data hashing.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { keccak256, keccak256Concat, functionSelector, ethAddressFromPubKey } from '@btc-vision/btc-runtime/runtime';
|
|
264
|
+
|
|
265
|
+
// Basic keccak256 hash
|
|
266
|
+
const hash: Uint8Array = keccak256(data); // 32-byte digest
|
|
267
|
+
|
|
268
|
+
// Hash concatenated byte arrays (common for abi.encodePacked patterns)
|
|
269
|
+
const hash2: Uint8Array = keccak256Concat(a, b);
|
|
270
|
+
|
|
271
|
+
// Compute 4-byte Ethereum function selector
|
|
272
|
+
const sel: Uint8Array = functionSelector('transfer(address,uint256)');
|
|
273
|
+
// sel == 0xa9059cbb
|
|
274
|
+
|
|
275
|
+
// Derive Ethereum address from 64-byte uncompressed public key
|
|
276
|
+
const addr: Uint8Array = ethAddressFromPubKey(publicKey64); // 20-byte address
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Important:** This is original Keccak-256 (as used by Ethereum), NOT NIST SHA-3-256. The difference is the domain separation padding byte: Keccak uses `0x01`, SHA-3 uses `0x06`.
|
|
280
|
+
|
|
184
281
|
## Message Hash Construction
|
|
185
282
|
|
|
186
283
|
When building message hashes for signature verification, use domain separation to prevent cross-contract signature reuse:
|
|
@@ -266,7 +363,7 @@ function buildPermitHash(
|
|
|
266
363
|
## Complete Contract Example
|
|
267
364
|
|
|
268
365
|
```typescript
|
|
269
|
-
import { OP_NET, Blockchain, Calldata, BytesWriter, Revert, sha256 } from '@btc-vision/btc-runtime/runtime';
|
|
366
|
+
import { OP_NET, Blockchain, Calldata, BytesWriter, Revert, sha256, SignaturesMethods } from '@btc-vision/btc-runtime/runtime';
|
|
270
367
|
|
|
271
368
|
@final
|
|
272
369
|
class SignatureContract extends OP_NET {
|
|
@@ -287,7 +384,7 @@ class SignatureContract extends OP_NET {
|
|
|
287
384
|
Blockchain.tx.origin,
|
|
288
385
|
signature,
|
|
289
386
|
messageHash,
|
|
290
|
-
|
|
387
|
+
SignaturesMethods.MLDSA // Force ML-DSA for quantum resistance
|
|
291
388
|
);
|
|
292
389
|
|
|
293
390
|
const writer = new BytesWriter(1);
|
|
@@ -313,7 +410,7 @@ class SignatureContract extends OP_NET {
|
|
|
313
410
|
Blockchain.tx.origin, // ExtendedAddress from transaction
|
|
314
411
|
signature,
|
|
315
412
|
messageHash,
|
|
316
|
-
|
|
413
|
+
SignaturesMethods.Schnorr // Use consensus-aware Schnorr verification
|
|
317
414
|
);
|
|
318
415
|
|
|
319
416
|
const writer = new BytesWriter(1);
|
|
@@ -331,34 +428,39 @@ OPNet provides significant advantages over Solidity for signature verification,
|
|
|
331
428
|
|
|
332
429
|
| Feature | Solidity/EVM | OPNet | OPNet Advantage |
|
|
333
430
|
|---------|--------------|-------|-----------------|
|
|
334
|
-
| **Primary Signature Scheme** | ECDSA (secp256k1) | Schnorr + ML-DSA |
|
|
431
|
+
| **Primary Signature Scheme** | ECDSA (secp256k1) | Schnorr + ML-DSA + ECDSA | Multiple schemes, quantum-resistant option |
|
|
335
432
|
| **Quantum Resistance** | Not supported | ML-DSA (FIPS 204) | Future-proof security |
|
|
433
|
+
| **ECDSA Support** | Only option | Supported (deprecated) | Backward compatibility with Ethereum/Bitcoin |
|
|
336
434
|
| **Signature Recovery** | `ecrecover()` returns address | Direct verification | Cleaner API |
|
|
337
435
|
| **Public Key Access** | Must be stored/derived | Automatic via `Address` | No custom storage needed |
|
|
338
436
|
| **Verification Function** | Multiple parameters (v, r, s) | Single signature bytes | Simpler interface |
|
|
339
437
|
| **EIP-712 Support** | Manual implementation | Built-in domain separation | Type-safe messages |
|
|
438
|
+
| **Keccak-256 Hashing** | Native opcode | Built-in runtime module | Ethereum-compatible hashing |
|
|
340
439
|
| **Batch Verification** | Not native | Supported | Better performance |
|
|
341
|
-
| **Key Sizes** | 33/65 bytes (secp256k1) | 32 bytes (Schnorr) / 1,312+ bytes (ML-DSA) | Flexible security |
|
|
440
|
+
| **Key Sizes** | 33/65 bytes (secp256k1) | 32 bytes (Schnorr) / 33-65 bytes (ECDSA) / 1,312+ bytes (ML-DSA) | Flexible security |
|
|
342
441
|
|
|
343
442
|
### Signature Scheme Comparison
|
|
344
443
|
|
|
345
|
-
| Aspect | Solidity (ECDSA) | OPNet (Schnorr) | OPNet (ML-DSA) |
|
|
346
|
-
|
|
347
|
-
| Algorithm | secp256k1 ECDSA | BIP340 Schnorr | FIPS 204 Lattice |
|
|
348
|
-
| Public Key Size | 33 or 65 bytes | 32 bytes | 1,312+ bytes |
|
|
349
|
-
| Signature Size | 65 bytes (v, r, s) | 64 bytes | 2,420+ bytes |
|
|
350
|
-
| Quantum Safe | No | No | **Yes** |
|
|
351
|
-
| Bitcoin Native | No | Yes | Yes |
|
|
352
|
-
| Batch Verification | No | Yes | Yes |
|
|
353
|
-
| Signature Malleability | Yes (fixable) | No | No |
|
|
444
|
+
| Aspect | Solidity (ECDSA) | OPNet (ECDSA) | OPNet (Schnorr) | OPNet (ML-DSA) |
|
|
445
|
+
|--------|------------------|---------------|-----------------|----------------|
|
|
446
|
+
| Algorithm | secp256k1 ECDSA | secp256k1 ECDSA | BIP340 Schnorr | FIPS 204 Lattice |
|
|
447
|
+
| Public Key Size | 33 or 65 bytes | 33, 64, or 65 bytes | 32 bytes | 1,312+ bytes |
|
|
448
|
+
| Signature Size | 65 bytes (v, r, s) | 64 or 65 bytes | 64 bytes | 2,420+ bytes |
|
|
449
|
+
| Quantum Safe | No | No | No | **Yes** |
|
|
450
|
+
| Bitcoin Native | No | Yes (Bitcoin sub-type) | Yes | Yes |
|
|
451
|
+
| Batch Verification | No | No | Yes | Yes |
|
|
452
|
+
| Signature Malleability | Yes (fixable) | No (BIP-0062 low-S) | No | No |
|
|
453
|
+
| Status | Only option | Deprecated | Transition | **Recommended** |
|
|
354
454
|
|
|
355
455
|
### Capability Matrix
|
|
356
456
|
|
|
357
457
|
| Capability | Solidity | OPNet |
|
|
358
458
|
|------------|:--------:|:-----:|
|
|
359
|
-
| ECDSA verification | Yes |
|
|
459
|
+
| ECDSA verification (Ethereum ecrecover) | Yes | Yes (deprecated) |
|
|
460
|
+
| ECDSA verification (Bitcoin direct) | No | Yes (deprecated) |
|
|
360
461
|
| Schnorr verification | No | Yes |
|
|
361
462
|
| ML-DSA (quantum-safe) verification | No | Yes |
|
|
463
|
+
| Keccak-256 hashing | Yes (native) | Yes (runtime module) |
|
|
362
464
|
| Automatic public key loading | No | Yes |
|
|
363
465
|
| Consensus-aware algorithm selection | No | Yes |
|
|
364
466
|
| EIP-712 domain separation | Manual | Built-in pattern |
|
|
@@ -411,10 +513,10 @@ function verifySignature(
|
|
|
411
513
|
): bool {
|
|
412
514
|
// Single function call - handles everything
|
|
413
515
|
const isValid = Blockchain.verifySignature(
|
|
414
|
-
signer,
|
|
415
|
-
signature,
|
|
416
|
-
hash,
|
|
417
|
-
|
|
516
|
+
signer, // Address contains public key reference
|
|
517
|
+
signature, // Full signature bytes
|
|
518
|
+
hash, // Message hash
|
|
519
|
+
SignaturesMethods.MLDSA // Force quantum-resistant ML-DSA
|
|
418
520
|
);
|
|
419
521
|
|
|
420
522
|
// Returns false on invalid (never throws for invalid sig)
|
|
@@ -480,7 +582,7 @@ public permit(calldata: Calldata): BytesWriter {
|
|
|
480
582
|
|
|
481
583
|
const digest = this.buildPermitHash(owner, spender, value, nonce, deadline);
|
|
482
584
|
|
|
483
|
-
if (!Blockchain.verifySignature(owner, signature, digest,
|
|
585
|
+
if (!Blockchain.verifySignature(owner, signature, digest, SignaturesMethods.Schnorr)) {
|
|
484
586
|
throw new Revert('Invalid signature');
|
|
485
587
|
}
|
|
486
588
|
|
|
@@ -528,7 +630,7 @@ function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public view returns
|
|
|
528
630
|
function verify(hash: Uint8Array, signature: Uint8Array, signer: Address): bool {
|
|
529
631
|
// Returns false on invalid signature - no silent failures
|
|
530
632
|
// Returns false on malformed input - no exceptions
|
|
531
|
-
return Blockchain.verifySignature(signer, signature, hash,
|
|
633
|
+
return Blockchain.verifySignature(signer, signature, hash, SignaturesMethods.MLDSA);
|
|
532
634
|
}
|
|
533
635
|
```
|
|
534
636
|
|
|
@@ -536,12 +638,13 @@ function verify(hash: Uint8Array, signature: Uint8Array, signer: Address): bool
|
|
|
536
638
|
|
|
537
639
|
| Solidity Limitation | OPNet Solution |
|
|
538
640
|
|---------------------|----------------|
|
|
539
|
-
| ECDSA only | Schnorr + ML-DSA support |
|
|
641
|
+
| ECDSA only | ECDSA + Schnorr + ML-DSA support |
|
|
540
642
|
| No quantum resistance | Built-in ML-DSA (FIPS 204) |
|
|
541
643
|
| Complex v, r, s handling | Single signature bytes parameter |
|
|
542
644
|
| Must store/derive public keys | Automatic key loading from Address |
|
|
543
645
|
| Silent ecrecover failures | Clean boolean returns |
|
|
544
|
-
| Signature malleability | BIP340 Schnorr (no malleability) |
|
|
646
|
+
| Signature malleability | BIP340 Schnorr / BIP-0062 low-S ECDSA (no malleability) |
|
|
647
|
+
| No keccak256 in runtime | Built-in Ethereum-compatible keccak256 |
|
|
545
648
|
| Manual EIP-712 implementation | Built-in domain separation patterns |
|
|
546
649
|
| No consensus-aware selection | Automatic algorithm selection |
|
|
547
650
|
|
|
@@ -572,7 +675,7 @@ public executeWithSignature(calldata: Calldata): BytesWriter {
|
|
|
572
675
|
const messageHash = sha256(message.getBuffer());
|
|
573
676
|
|
|
574
677
|
// Verify signature from sender
|
|
575
|
-
if (!Blockchain.verifySignature(Blockchain.tx.origin, signature, messageHash,
|
|
678
|
+
if (!Blockchain.verifySignature(Blockchain.tx.origin, signature, messageHash, SignaturesMethods.MLDSA)) {
|
|
576
679
|
throw new Revert('Invalid signature');
|
|
577
680
|
}
|
|
578
681
|
|
|
@@ -617,7 +720,7 @@ public permit(calldata: Calldata): BytesWriter {
|
|
|
617
720
|
const messageHash = this.buildPermitHash(owner, spender, value, nonce, deadline);
|
|
618
721
|
|
|
619
722
|
// Verify signature
|
|
620
|
-
if (!Blockchain.verifySignature(owner, signature, messageHash,
|
|
723
|
+
if (!Blockchain.verifySignature(owner, signature, messageHash, SignaturesMethods.Schnorr)) {
|
|
621
724
|
throw new Revert('Invalid signature');
|
|
622
725
|
}
|
|
623
726
|
|
|
@@ -651,7 +754,7 @@ public executeMultiSig(calldata: Calldata): BytesWriter {
|
|
|
651
754
|
const signature = signatures[i];
|
|
652
755
|
|
|
653
756
|
if (this.isAuthorizedSigner(signer)) {
|
|
654
|
-
if (Blockchain.verifySignature(signer, signature, actionHash,
|
|
757
|
+
if (Blockchain.verifySignature(signer, signature, actionHash, SignaturesMethods.MLDSA)) {
|
|
655
758
|
validCount++;
|
|
656
759
|
}
|
|
657
760
|
}
|
|
@@ -705,7 +808,7 @@ const DOMAIN_SEPARATOR = buildDomainSeparator(
|
|
|
705
808
|
|
|
706
809
|
```typescript
|
|
707
810
|
// For high-security operations, force ML-DSA
|
|
708
|
-
Blockchain.verifySignature(signer, signature, hash,
|
|
811
|
+
Blockchain.verifySignature(signer, signature, hash, SignaturesMethods.MLDSA);
|
|
709
812
|
```
|
|
710
813
|
|
|
711
814
|
---
|
|
@@ -493,11 +493,13 @@ const txHash = Blockchain.hash256(txData); // 32 bytes
|
|
|
493
493
|
Verifies signature based on current consensus rules.
|
|
494
494
|
|
|
495
495
|
```typescript
|
|
496
|
+
import { SignaturesMethods } from '@btc-vision/btc-runtime/runtime';
|
|
497
|
+
|
|
496
498
|
verifySignature(
|
|
497
499
|
address: ExtendedAddress,
|
|
498
500
|
signature: Uint8Array,
|
|
499
501
|
hash: Uint8Array,
|
|
500
|
-
|
|
502
|
+
signatureType: SignaturesMethods = SignaturesMethods.Schnorr
|
|
501
503
|
): boolean
|
|
502
504
|
```
|
|
503
505
|
|
|
@@ -506,24 +508,25 @@ verifySignature(
|
|
|
506
508
|
| `address` | `ExtendedAddress` | Signer's address |
|
|
507
509
|
| `signature` | `Uint8Array` | Signature bytes (64 for Schnorr, 2420+ for ML-DSA) |
|
|
508
510
|
| `hash` | `Uint8Array` | 32-byte message hash |
|
|
509
|
-
| `
|
|
511
|
+
| `signatureType` | `SignaturesMethods` | Signature type: `Schnorr` (default), `MLDSA`, or `ECDSA` |
|
|
510
512
|
| **Returns** | `boolean` | True if valid |
|
|
511
513
|
|
|
512
|
-
The signature verification selects the appropriate algorithm based on consensus rules:
|
|
514
|
+
The signature verification selects the appropriate algorithm based on the `signatureType` parameter and consensus rules:
|
|
513
515
|
|
|
514
516
|
```mermaid
|
|
515
517
|
flowchart LR
|
|
516
518
|
subgraph "verifySignature Flow"
|
|
517
519
|
Start([Verify Signature]) --> GetInput[Receive address,<br/>signature, hash]
|
|
518
|
-
GetInput -->
|
|
520
|
+
GetInput --> CheckType{signatureType?}
|
|
521
|
+
CheckType -->|Schnorr| CheckConsensus{unsafeSignatures<br/>Allowed?}
|
|
519
522
|
CheckConsensus -->|Yes| Schnorr[Schnorr Path]
|
|
520
|
-
CheckConsensus -->|No| MLDSA
|
|
523
|
+
CheckConsensus -->|No| MLDSA
|
|
524
|
+
CheckType -->|MLDSA| MLDSA[ML-DSA Path]
|
|
525
|
+
CheckType -->|ECDSA| ECDSAErr[Error: Use dedicated<br/>ECDSA methods]
|
|
521
526
|
end
|
|
522
527
|
|
|
523
528
|
subgraph "Schnorr Verification"
|
|
524
|
-
Schnorr -->
|
|
525
|
-
ValidateSig -->|Yes| SchnorrVerify[Verify Schnorr]
|
|
526
|
-
ValidateSig -->|No| SchnorrFail([Revert])
|
|
529
|
+
Schnorr --> SchnorrVerify[Verify Schnorr]
|
|
527
530
|
SchnorrVerify --> SchnorrResult{Valid?}
|
|
528
531
|
end
|
|
529
532
|
|
|
@@ -564,16 +567,15 @@ sequenceDiagram
|
|
|
564
567
|
C->>C: Combine: 0x1901 + domain + structHash
|
|
565
568
|
C->>C: messageHash = SHA-256(combined)
|
|
566
569
|
|
|
567
|
-
C->>BC: verifySignature(address, signature, messageHash,
|
|
570
|
+
C->>BC: verifySignature(address, signature, messageHash, signatureType)
|
|
568
571
|
|
|
569
572
|
BC->>Cons: Check consensus flags
|
|
570
573
|
Cons->>BC: unsafeSignaturesAllowed()
|
|
571
574
|
|
|
572
|
-
alt unsafeSignaturesAllowed()
|
|
573
|
-
BC->>BC: Validate signature.length == 64
|
|
575
|
+
alt signatureType == Schnorr AND unsafeSignaturesAllowed()
|
|
574
576
|
BC->>Crypto: verifySchnorr(tweakedPublicKey, sig, hash)
|
|
575
577
|
Crypto->>BC: Valid/Invalid
|
|
576
|
-
else
|
|
578
|
+
else signatureType == MLDSA OR !unsafeSignaturesAllowed()
|
|
577
579
|
BC->>BC: Use ML-DSA Level2 (ML-DSA-44)
|
|
578
580
|
BC->>Crypto: verifyMLDSA(Level2, mldsaPublicKey, sig, hash)
|
|
579
581
|
Crypto->>BC: Valid/Invalid
|
|
@@ -582,6 +584,48 @@ sequenceDiagram
|
|
|
582
584
|
BC->>C: Return boolean
|
|
583
585
|
```
|
|
584
586
|
|
|
587
|
+
### verifyECDSASignature (Deprecated)
|
|
588
|
+
|
|
589
|
+
Verifies an ECDSA signature using the Ethereum ecrecover model (secp256k1).
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
verifyECDSASignature(
|
|
593
|
+
publicKey: Uint8Array,
|
|
594
|
+
signature: Uint8Array,
|
|
595
|
+
hash: Uint8Array
|
|
596
|
+
): boolean
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
| Parameter | Type | Description |
|
|
600
|
+
|-----------|------|-------------|
|
|
601
|
+
| `publicKey` | `Uint8Array` | secp256k1 public key (33, 64, or 65 bytes) |
|
|
602
|
+
| `signature` | `Uint8Array` | 65-byte signature: r(32) \|\| s(32) \|\| v(1) |
|
|
603
|
+
| `hash` | `Uint8Array` | 32-byte message hash (typically keccak256) |
|
|
604
|
+
| **Returns** | `boolean` | True if ecrecover produces matching key |
|
|
605
|
+
|
|
606
|
+
> **Warning:** Only available when `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag is set. Throws `Revert` otherwise.
|
|
607
|
+
|
|
608
|
+
### verifyBitcoinECDSASignature (Deprecated)
|
|
609
|
+
|
|
610
|
+
Verifies an ECDSA signature using the Bitcoin direct verification model (secp256k1).
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
verifyBitcoinECDSASignature(
|
|
614
|
+
publicKey: Uint8Array,
|
|
615
|
+
signature: Uint8Array,
|
|
616
|
+
hash: Uint8Array
|
|
617
|
+
): boolean
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
| Parameter | Type | Description |
|
|
621
|
+
|-----------|------|-------------|
|
|
622
|
+
| `publicKey` | `Uint8Array` | secp256k1 public key (33, 64, or 65 bytes) |
|
|
623
|
+
| `signature` | `Uint8Array` | 64-byte compact signature: r(32) \|\| s(32) |
|
|
624
|
+
| `hash` | `Uint8Array` | 32-byte message hash (typically SHA-256 double hash) |
|
|
625
|
+
| **Returns** | `boolean` | True if signature is valid |
|
|
626
|
+
|
|
627
|
+
> **Warning:** Only available when `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag is set. Throws `Revert` otherwise. Enforces BIP-0062 low-S normalization.
|
|
628
|
+
|
|
585
629
|
### verifyMLDSASignature
|
|
586
630
|
|
|
587
631
|
Verifies ML-DSA (quantum-resistant) signature.
|
|
@@ -626,6 +670,45 @@ verifySchnorrSignature(
|
|
|
626
670
|
|
|
627
671
|
> **Note:** Use `verifySignature()` instead for automatic consensus migration.
|
|
628
672
|
|
|
673
|
+
## Keccak-256 Hashing
|
|
674
|
+
|
|
675
|
+
Ethereum-compatible Keccak-256 hash functions (original Keccak, not NIST SHA-3-256).
|
|
676
|
+
|
|
677
|
+
### keccak256
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
import { keccak256 } from '@btc-vision/btc-runtime/runtime';
|
|
681
|
+
keccak256(data: Uint8Array): Uint8Array // 32-byte digest
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### keccak256Concat
|
|
685
|
+
|
|
686
|
+
Hash two concatenated byte arrays. Common for `abi.encodePacked` patterns.
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
import { keccak256Concat } from '@btc-vision/btc-runtime/runtime';
|
|
690
|
+
keccak256Concat(a: Uint8Array, b: Uint8Array): Uint8Array // 32-byte digest
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### functionSelector
|
|
694
|
+
|
|
695
|
+
Compute Ethereum-style 4-byte function selector.
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
import { functionSelector } from '@btc-vision/btc-runtime/runtime';
|
|
699
|
+
functionSelector(signature: string): Uint8Array // 4 bytes
|
|
700
|
+
// e.g. functionSelector('transfer(address,uint256)') => 0xa9059cbb
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### ethAddressFromPubKey
|
|
704
|
+
|
|
705
|
+
Derive Ethereum address from 64-byte uncompressed public key.
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
import { ethAddressFromPubKey } from '@btc-vision/btc-runtime/runtime';
|
|
709
|
+
ethAddressFromPubKey(publicKey: Uint8Array): Uint8Array // 20-byte Ethereum address
|
|
710
|
+
```
|
|
711
|
+
|
|
629
712
|
## Utility Methods
|
|
630
713
|
|
|
631
714
|
### validateBitcoinAddress
|
|
@@ -42,7 +42,7 @@ export class MyToken extends OP20S {
|
|
|
42
42
|
| Feature | ERC20Permit (EIP-2612) | OP20S (OPNet) |
|
|
43
43
|
|---------|------------------------|---------------|
|
|
44
44
|
| Language | Solidity | AssemblyScript |
|
|
45
|
-
| Signature Type | ECDSA (v, r, s) | Schnorr or ML-DSA |
|
|
45
|
+
| Signature Type | ECDSA (v, r, s) | Schnorr, ECDSA (deprecated), or ML-DSA |
|
|
46
46
|
| Domain Separator | EIP-712 | EIP-712 style |
|
|
47
47
|
| Quantum Resistance | No | Yes (ML-DSA option) |
|
|
48
48
|
| Signature Parameter | Three params (v, r, s) | Single bytes param |
|