@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.
@@ -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) | Not used |
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
- // forceMLDSA = true ensures quantum-resistant verification
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
- true // Force ML-DSA (quantum-resistant)
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 (forceMLDSA = true)
267
+ // Verify with quantum resistance (MLDSA)
268
268
  const isValid = Blockchain.verifySignature(
269
269
  Blockchain.tx.origin,
270
270
  signature,
271
271
  messageHash,
272
- true // Force ML-DSA
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
- false // Let consensus decide
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
- true // Force ML-DSA
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
- true
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 | Not used |
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 | No (deprecated) |
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
- true // Force quantum-resistant ML-DSA
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** - Set `forceMLDSA = true` in `Blockchain.verifySignature()` for critical operations (see [Signature Verification](#signature-verification))
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** - Set `forceMLDSA = false` to let the network decide which algorithm to use during the transition period
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, // Address or ExtendedAddress
13
+ Blockchain.tx.origin, // ExtendedAddress
14
14
  signature, // Signature bytes
15
15
  messageHash, // 32-byte message hash
16
- false // forceMLDSA: false = auto, true = require ML-DSA
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
- true // Force ML-DSA regardless of consensus flags
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 both traditional Schnorr signatures and quantum-resistant ML-DSA:
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"| S3
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
- forceMLDSA: boolean = false // Force quantum-resistant verification
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
- - When `forceMLDSA = false`: Uses Schnorr if `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag is set, otherwise ML-DSA
77
- - When `forceMLDSA = true`: Always uses ML-DSA (quantum-resistant)
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, false)
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, true)
143
- Note over Blockchain: forceMLDSA = true
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
- true // Force ML-DSA for quantum resistance
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
- false // Use consensus-aware verification
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 | Quantum-resistant option |
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 | No (not needed) |
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, // Address contains public key reference
415
- signature, // Full signature bytes
416
- hash, // Message hash
417
- true // Force quantum-resistant ML-DSA
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, false)) {
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, true);
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, true)) {
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, false)) {
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, true)) {
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, true);
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
- forceMLDSA: boolean = false
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
- | `forceMLDSA` | `boolean` | Force ML-DSA verification |
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 --> CheckConsensus{unsafeSignaturesAllowed()<br/>AND !forceMLDSA?}
520
+ GetInput --> CheckType{signatureType?}
521
+ CheckType -->|Schnorr| CheckConsensus{unsafeSignatures<br/>Allowed?}
519
522
  CheckConsensus -->|Yes| Schnorr[Schnorr Path]
520
- CheckConsensus -->|No| MLDSA[ML-DSA Path]
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 --> ValidateSig{signature.length<br/>== 64?}
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, forceMLDSA?)
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() AND !forceMLDSA
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 ML-DSA required (consensus or forced)
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 |