@btc-vision/btc-runtime
Version:
Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.
663 lines (525 loc) • 22.8 kB
Markdown
# Quantum Resistance
OP_NET includes built-in quantum-resistant cryptography through ML-DSA (Module-Lattice Digital Signature Algorithm). The `Address` class provides automatic access to ML-DSA public keys without requiring any custom storage.
## Overview
Quantum computers pose a threat to traditional cryptographic schemes:
| Algorithm | Quantum Threat | OP_NET Status |
|-----------|---------------|--------------|
| ECDSA | Vulnerable (Shor's algorithm) | Supported (deprecated), transition to ML-DSA |
| Schnorr | Vulnerable (Shor's algorithm) | Supported, with transition plan |
| ML-DSA | Quantum-resistant | **Fully supported** |
## Quick Start
```typescript
import { Blockchain, sha256 } from '@btc-vision/btc-runtime/runtime';
// Verify a signature with quantum resistance
const message = new BytesWriter(32);
message.writeString('Sign this message');
const messageHash = sha256(message.getBuffer());
// SignaturesMethods.MLDSA ensures quantum-resistant verification
const isValid = Blockchain.verifySignature(
Blockchain.tx.origin, // Signer's address
signature, // Signature bytes
messageHash, // Message hash
SignaturesMethods.MLDSA // Force ML-DSA (quantum-resistant)
);
```
## Why Quantum Resistance?
### The Threat
- **Public keys are exposed** when addresses send transactions
- Quantum computers with ~4000 qubits could derive private keys
- Existing signatures remain safe (retroactive attack impossible)
- Future transactions from exposed addresses are at risk
### OP_NET's Approach
1. **Dual signature support** - Both Schnorr and ML-DSA signatures
2. **Extended addresses** - Store both Schnorr (taproot) and ML-DSA key references
3. **Built-in key access** - `Address.mldsaPublicKey` property loads keys automatically
4. **Consensus-managed transition** - Automatic migration from Schnorr to ML-DSA
## ML-DSA Security Levels
OP_NET supports three ML-DSA security levels, from classical Schnorr to quantum-resistant ML-DSA:
```mermaid
config:
theme: dark
flowchart LR
subgraph OP_NET["OP_NET Security Architecture"]
subgraph Classical["Classical Security"]
C1["Schnorr<br/>256-bit<br/>Classical: Strong<br/>Quantum: Broken"]
end
subgraph PostQuantum["Post-Quantum Security Levels"]
Q1["ML-DSA-44<br/>Level2<br/>~AES-128<br/>Quantum: Strong"]
Q2["ML-DSA-65<br/>Level3<br/>~AES-192<br/>Quantum: Stronger"]
Q3["ML-DSA-87<br/>Level5<br/>~AES-256<br/>Quantum: Strongest"]
end
QC["Quantum Computer<br/>4000+ qubits"]
QC -.->|"Breaks in hours"| C1
QC -.->|"Cannot break"| Q1
QC -.->|"Cannot break"| Q2
QC -.->|"Cannot break"| Q3
end
```
| Level | Name | Public Key | Signature | Private Key | NIST Category |
|-------|------|------------|-----------|-------------|---------------|
| Level2 | ML-DSA-44 | 1,312 bytes | 2,420 bytes | 2,560 bytes | Category 2 (~AES-128) |
| Level3 | ML-DSA-65 | 1,952 bytes | 3,309 bytes | 4,032 bytes | Category 3 (~AES-192) |
| Level5 | ML-DSA-87 | 2,592 bytes | 4,627 bytes | 4,896 bytes | Category 5 (~AES-256) |
**OP_NET uses ML-DSA-44 (Level2) by default**, balancing security and performance.
### Level Constants
```typescript
import {
MLDSASecurityLevel,
MLDSA44_PUBLIC_KEY_LEN, // 1312
MLDSA44_SIGNATURE_LEN, // 2420
MLDSA65_PUBLIC_KEY_LEN, // 1952
MLDSA65_SIGNATURE_LEN, // 3309
MLDSA87_PUBLIC_KEY_LEN, // 2592
MLDSA87_SIGNATURE_LEN // 4627
} from '@btc-vision/btc-runtime/runtime';
```
## ML-DSA Signature Structure
The ML-DSA signature format follows FIPS 204:
```mermaid
config:
theme: dark
flowchart LR
subgraph OP_NET["OP_NET Quantum-Resistant Signatures"]
subgraph MLDSA["ML-DSA-44 - Level2 - Default"]
PK["Public Key<br/>1,312 bytes"]
SIG["Signature<br/>2,420 bytes"]
SK["Private Key<br/>2,560 bytes"]
end
subgraph PKComp["Public Key Components"]
PK1["rho: seed"]
PK2["t1: compressed hint"]
end
subgraph SigComp["Signature Components"]
SIG1["c_tilde: commitment hash"]
SIG2["z: response vector"]
SIG3["h: hint"]
end
end
```
## The Address Class
### Automatic ML-DSA Key Access
Every `Address` in OP_NET stores the SHA256 hash of an ML-DSA public key. The full public key is automatically loaded on demand:
```typescript
import { Address, Blockchain } from '@btc-vision/btc-runtime/runtime';
// Any address can access its ML-DSA public key
const sender: Address = Blockchain.tx.sender;
// Get the ML-DSA public key (loaded automatically from the blockchain)
const mldsaKey: Uint8Array = sender.mldsaPublicKey;
// The key is cached after first access for efficiency
const sameKey: Uint8Array = sender.mldsaPublicKey; // Returns cached key
```
**Key points:**
- No custom storage needed - the runtime handles key storage
- Keys are lazily loaded on first access and cached
- The address itself is the SHA256 hash of the ML-DSA public key
### How It Works Internally
```typescript
// Address stores 32-byte SHA256 hash of ML-DSA public key
class Address extends Uint8Array {
protected _mldsaPublicKey: Uint8Array | null = null;
// Getter loads key from blockchain on demand
public get mldsaPublicKey(): Uint8Array {
if (!this._mldsaPublicKey) {
this._mldsaPublicKey = loadMLDSAPublicKey(this, MLDSASecurityLevel.Level2);
}
return this._mldsaPublicKey;
}
}
```
## Extended Address
`ExtendedAddress` supports dual-key addresses for the quantum transition:
```typescript
import { ExtendedAddress } from '@btc-vision/btc-runtime/runtime';
// Create from both key components
const extAddr = ExtendedAddress.fromStringPair(
'0x' + 'aa'.repeat(32), // Tweaked Schnorr key (taproot)
'0x' + 'bb'.repeat(32) // ML-DSA key hash
);
// Access the tweaked Schnorr key (for taproot/P2TR)
const schnorrKey: Uint8Array = extAddr.tweakedPublicKey; // 32 bytes
// Access the ML-DSA public key (inherited from Address)
const mldsaKey: Uint8Array = extAddr.mldsaPublicKey; // 1312 bytes (ML-DSA-44)
// Generate Bitcoin addresses
const p2trAddress: string = extAddr.p2tr(); // "bc1p..." or "tb1p..."
```
### Dual Key Structure
```
ExtendedAddress (64 bytes reference)
+-- tweakedPublicKey: Uint8Array[32] --> Schnorr (taproot)
+-- Address bytes: Uint8Array[32] --> SHA256(ML-DSA public key)
+-> .mldsaPublicKey loads full key
```
## ML-DSA Key Generation and Verification Flow
The following diagram shows the complete lifecycle from key generation to signature verification:
```mermaid
config:
theme: dark
sequenceDiagram
participant User as 👤 User
participant Wallet as Wallet
participant Blockchain as OP_NET Runtime
participant Contract as Contract
Note over User,Blockchain: Key Generation (Off-chain)
User->>Wallet: Generate ML-DSA keypair
Wallet->>Wallet: Generate random seed
Wallet->>Wallet: Expand seed to matrix A
Wallet->>Wallet: Sample secret vectors s1, s2
Wallet->>Wallet: Compute t = As1 + s2
Wallet->>Wallet: Public key = (rho, t1)
Wallet->>Wallet: Private key = (rho, K, tr, s1, s2, t0)
Note over User,Blockchain: Address Creation
Wallet->>Wallet: Hash ML-DSA public key (SHA256)
Wallet->>Wallet: Store 32-byte hash as Address
Wallet->>Blockchain: Register address with full public key
Note over User,Contract: Signature Verification
User->>Contract: call method(signature, message)
Contract->>Contract: Get sender address (32-byte hash)
Contract->>Blockchain: verifySignature(address, sig, hash, true)
Blockchain->>Blockchain: Load cached ML-DSA public key
Note over Blockchain: If not cached, load from storage
Blockchain->>Blockchain: Verify ML-DSA signature
Blockchain->>Blockchain: Decode signature (c_tilde, z, h)
Blockchain->>Blockchain: Reconstruct commitment w'
Blockchain->>Blockchain: Verify ||z|| < gamma1 - beta
Blockchain->>Blockchain: Verify high-order bits match
Blockchain-->>Contract: valid: bool
```
## Signature Verification
### The Simple Way (Recommended)
Just use `Blockchain.verifySignature()` - it handles everything:
```typescript
import { Blockchain, sha256 } from '@btc-vision/btc-runtime/runtime';
@method(ABIDataTypes.BYTES)
@returns({ name: 'valid', type: ABIDataTypes.BOOL })
public verifySignature(calldata: Calldata): BytesWriter {
const signature = calldata.readBytesWithLength();
// Create message hash
const message = new BytesWriter(55);
message.writeString('Hello, world! This is a test message for MLDSA signing.');
const messageHash = sha256(message.getBuffer());
// Verify with quantum resistance (MLDSA)
const isValid = Blockchain.verifySignature(
Blockchain.tx.origin,
signature,
messageHash,
SignaturesMethods.MLDSA // Force ML-DSA
);
const writer = new BytesWriter(1);
writer.writeBoolean(isValid);
return writer;
}
```
### Consensus-Aware Verification
Let the consensus decide which algorithm to use:
```typescript
// During transition: uses Schnorr if allowed, ML-DSA otherwise
// After transition: always uses ML-DSA
const isValid = Blockchain.verifySignature(
signer,
signature,
messageHash,
SignaturesMethods.Schnorr // Let consensus decide (falls back to ML-DSA if Schnorr not allowed)
);
```
### Direct ML-DSA Verification
For cases where you need explicit control:
```typescript
import { MLDSASecurityLevel } from '@btc-vision/btc-runtime/runtime';
const isValid = Blockchain.verifyMLDSASignature(
MLDSASecurityLevel.Level2, // Security level
signer.mldsaPublicKey, // Auto-loaded from address
signature, // 2420-byte signature
messageHash // 32-byte message hash
);
```
## Migration Path: Schnorr to ML-DSA
OP_NET manages a phased transition from classical to quantum-resistant signatures:
```mermaid
config:
theme: dark
sequenceDiagram
participant User as User/Wallet
participant Network as OP_NET Network
participant Consensus as Consensus Rules
Note over User,Consensus: Phase 1: Transition Period (Current)
User->>Network: Submit Schnorr signature
Network->>Consensus: Check UNSAFE_QUANTUM_SIGNATURES_ALLOWED
Consensus-->>Network: Flag = true
Network-->>User: Signature accepted
User->>Network: Submit ML-DSA signature
Network-->>User: Signature accepted
Note over Network: Both signature types valid
Note over User,Consensus: Phase 2: Warning Period
User->>Network: Submit Schnorr signature
Network->>Consensus: Check signature type
Consensus-->>Network: Log deprecation warning
Network-->>User: Signature accepted (with warning)
Note over User: Encouraged to migrate to ML-DSA
Note over User,Consensus: Phase 3: Quantum-Safe Only
User->>Network: Submit Schnorr signature
Network->>Consensus: Check UNSAFE_QUANTUM_SIGNATURES_ALLOWED
Consensus-->>Network: Flag = false
Network-->>User: Signature REJECTED
User->>Network: Submit ML-DSA signature
Network-->>User: Signature accepted
Note over Network: Only ML-DSA signatures valid
```
The `UNSAFE_QUANTUM_SIGNATURES_ALLOWED` consensus flag controls whether Schnorr signatures are still permitted. When disabled, only ML-DSA signatures will be valid.
## Complete Contract Example
```typescript
import {
OP_NET,
Blockchain,
Calldata,
BytesWriter,
Revert,
sha256,
ABIDataTypes
} from '@btc-vision/btc-runtime/runtime';
@final
class QuantumSecureContract extends OP_NET {
@method(
{ name: 'message', type: ABIDataTypes.BYTES },
{ name: 'signature', type: ABIDataTypes.BYTES },
)
@returns({ name: 'valid', type: ABIDataTypes.BOOL })
public verifyQuantum(calldata: Calldata): BytesWriter {
const message = calldata.readBytesWithLength();
const signature = calldata.readBytesWithLength();
const messageHash = sha256(message);
// Always use quantum-resistant verification
const isValid = Blockchain.verifySignature(
Blockchain.tx.origin,
signature,
messageHash,
SignaturesMethods.MLDSA // Force ML-DSA
);
const writer = new BytesWriter(1);
writer.writeBoolean(isValid);
return writer;
}
@method(
{ name: 'signer', type: ABIDataTypes.ADDRESS },
{ name: 'message', type: ABIDataTypes.BYTES },
{ name: 'signature', type: ABIDataTypes.BYTES },
)
@returns({ name: 'valid', type: ABIDataTypes.BOOL })
public verifyForAddress(calldata: Calldata): BytesWriter {
const signer = calldata.readAddress();
const message = calldata.readBytesWithLength();
const signature = calldata.readBytesWithLength();
const messageHash = sha256(message);
// Verify for a specific address
const isValid = Blockchain.verifySignature(
signer,
signature,
messageHash,
SignaturesMethods.MLDSA
);
const writer = new BytesWriter(1);
writer.writeBoolean(isValid);
return writer;
}
@method({ name: 'owner', type: ABIDataTypes.ADDRESS })
@returns({ name: 'keyLength', type: ABIDataTypes.UINT32 })
public getMLDSAKeyLength(calldata: Calldata): BytesWriter {
const owner = calldata.readAddress();
// Access ML-DSA public key - automatically loaded
const mldsaKey = owner.mldsaPublicKey;
const writer = new BytesWriter(4);
writer.writeU32(mldsaKey.length); // 1312 for Level2
return writer;
}
}
```
## Solidity vs OP_NET: Quantum Resistance Comparison
OP_NET is the first smart contract platform with built-in quantum-resistant cryptography. Solidity and the EVM have no quantum resistance capabilities.
### Feature Comparison Table
| Feature | Solidity/EVM | OP_NET | OP_NET Advantage |
|---------|--------------|-------|-----------------|
| **Quantum-Safe Signatures** | Not supported | ML-DSA (FIPS 204) | Future-proof security |
| **Post-Quantum Algorithm** | None | ML-DSA-44/65/87 | NIST standardized |
| **Dual-Key Architecture** | Not available | Schnorr + ML-DSA | Smooth transition |
| **Key Loading** | Manual storage/derivation | Automatic via `Address` | Zero setup required |
| **Consensus Migration** | Not applicable | Built-in transition plan | Network-coordinated |
| **Security Levels** | ECDSA only | 3 ML-DSA levels | Flexible security |
| **Algorithm Selection** | Fixed (ECDSA) | Consensus-aware | Automatic switching |
### Quantum Threat Analysis
| Algorithm | Shor's Algorithm Impact | Grover's Algorithm Impact | Status in OP_NET |
|-----------|------------------------|---------------------------|-----------------|
| ECDSA (Solidity) | **Broken** (polynomial time) | Weakened | N/A |
| ECDSA (OP_NET) | **Broken** (polynomial time) | Weakened | Deprecated |
| Schnorr (OP_NET) | **Broken** (polynomial time) | Weakened | Transition only |
| ML-DSA (OP_NET) | **Secure** | Minimal impact | **Recommended** |
### Security Level Comparison
| Security Level | Solidity | OP_NET ML-DSA | NIST Category | Quantum Security |
|----------------|----------|--------------|---------------|------------------|
| ~AES-128 equivalent | ECDSA (broken by quantum) | ML-DSA-44 (Level2) | Category 2 | **Secure** |
| ~AES-192 equivalent | Not available | ML-DSA-65 (Level3) | Category 3 | **Secure** |
| ~AES-256 equivalent | Not available | ML-DSA-87 (Level5) | Category 5 | **Secure** |
### Key and Signature Size Comparison
| Metric | Solidity (ECDSA) | OP_NET (Schnorr) | OP_NET (ML-DSA-44) | OP_NET (ML-DSA-87) |
|--------|------------------|-----------------|-------------------|-------------------|
| Public Key Size | 33/65 bytes | 32 bytes | 1,312 bytes | 2,592 bytes |
| Signature Size | 65 bytes | 64 bytes | 2,420 bytes | 4,627 bytes |
| Private Key Size | 32 bytes | 32 bytes | 2,560 bytes | 4,896 bytes |
| Quantum Safe | **No** | **No** | **Yes** | **Yes** |
### Capability Matrix
| Capability | Solidity | OP_NET |
|------------|:--------:|:-----:|
| ECDSA verification (Ethereum) | Yes | Yes (deprecated) |
| ECDSA verification (Bitcoin) | No | Yes (deprecated) |
| Schnorr verification | No | Yes |
| ML-DSA-44 verification | No | Yes |
| ML-DSA-65 verification | No | Yes |
| ML-DSA-87 verification | No | Yes |
| Automatic public key loading | No | Yes |
| Quantum-resistant addresses | No | Yes |
| Dual-key addresses | No | Yes |
| Consensus-aware algorithm | No | Yes |
| Phased migration support | No | Yes |
### Timeline: Quantum Threat vs Platform Readiness
| Timeframe | Quantum Computer Status | Solidity Status | OP_NET Status |
|-----------|------------------------|-----------------|--------------|
| **2024-2025** | Early NISQ era (~1000 qubits) | Vulnerable (no plan) | ML-DSA ready |
| **2026-2030** | Scaling (~4000+ qubits possible) | **Critical risk** | Dual-key transition |
| **2030+** | Cryptographically relevant | **Funds at risk** | ML-DSA only (secure) |
### Migration Path Comparison
```mermaid
config:
theme: dark
flowchart LR
subgraph Solidity["Solidity/EVM - No Migration Path"]
S1["ECDSA Only"] --> S2["No quantum option"]
S2 --> S3["Fork required?"]
S3 --> S4["Funds at risk"]
end
subgraph OP_NET["OP_NET - Built-in Migration"]
O1["Schnorr (current)"] --> O2["Dual-key period"]
O2 --> O3["ML-DSA only"]
O3 --> O4["Quantum secure"]
end
```
### Signature Verification Comparison
#### Solidity: No Quantum Protection
```solidity
// Solidity - Vulnerable to quantum attacks
// There is NO way to add quantum resistance to Solidity/EVM
function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) external view returns (bool) {
address recovered = ecrecover(hash, v, r, s);
return recovered == expectedSigner;
// CRITICAL VULNERABILITIES:
// - ECDSA is broken by Shor's algorithm
// - No update path to quantum-safe algorithms
// - All funds signed with exposed public keys at risk
// - No way to add ML-DSA or other PQC algorithms
}
// Even with precompiles, no quantum-safe option exists
// EIP proposals for PQC have not been implemented
```
#### OP_NET: Built-in Quantum Resistance
```typescript
// OP_NET - Quantum-resistant
@method(
{ name: 'hash', type: ABIDataTypes.BYTES32 },
{ name: 'signature', type: ABIDataTypes.BYTES },
)
@returns({ name: 'valid', type: ABIDataTypes.BOOL })
public verify(calldata: Calldata): BytesWriter {
const hash = calldata.readBytes(32);
const signature = calldata.readBytesWithLength();
// ML-DSA provides quantum resistance
// FIPS 204 standardized algorithm
// Public key loaded automatically from address
const valid = Blockchain.verifySignature(
this.expectedSigner.value,
signature,
hash,
SignaturesMethods.MLDSA // Force quantum-resistant ML-DSA
);
const writer = new BytesWriter(1);
writer.writeBoolean(valid);
return writer;
}
// Advantages:
// - ML-DSA is quantum-resistant (lattice-based)
// - NIST standardized (FIPS 204)
// - Automatic key loading from address
// - Consensus-managed transition
// - No code changes needed for migration
```
### Public Key Access Comparison
```solidity
// Solidity - Must store or derive public key
contract SolidityContract {
// Must manually store public keys
mapping(address => bytes) public publicKeys;
function registerKey(bytes calldata pubkey) external {
// Verify key matches address
require(address(uint160(uint256(keccak256(pubkey)))) == msg.sender);
publicKeys[msg.sender] = pubkey;
}
// No way to store/use ML-DSA keys (1,312+ bytes each!)
// Storage costs would be prohibitive
}
```
```typescript
// OP_NET - Automatic key access
@final
class OPNetContract extends OP_NET {
public getPublicKey(calldata: Calldata): BytesWriter {
const user = calldata.readAddress();
// ML-DSA public key loaded automatically!
// No storage needed - runtime handles it
const mldsaKey = user.mldsaPublicKey; // 1,312 bytes
// Key is cached after first access
const writer = new BytesWriter(4);
writer.writeU32(mldsaKey.length);
return writer;
}
}
```
### Why OP_NET for Quantum Security?
| Solidity Limitation | OP_NET Solution |
|---------------------|----------------|
| ECDSA only (quantum vulnerable) | ML-DSA (quantum resistant) |
| No update path | Built-in consensus migration |
| Must store large keys manually | Automatic key loading |
| No NIST PQC algorithms | FIPS 204 ML-DSA |
| Single key per address | Dual-key architecture |
| Fixed algorithm forever | Consensus-aware selection |
| Fork required for PQC | Smooth transition built-in |
| All funds at risk | Protected from day one |
### Cost Comparison
| Operation | Solidity | OP_NET Cost | Notes |
|-----------|----------|------------|-------|
| Store ML-DSA public key | Not practical (1,312 bytes) | 0 | OP_NET loads automatically |
| Store ML-DSA signature | Not practical (2,420 bytes) | N/A | Not stored, verified |
| Quantum-safe verification | Not possible | Standard | No additional cost |
| Key migration | Contract redeploy | Consensus-managed | No user action needed |
## Best Practices
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))
2. **Force ML-DSA for high-security operations** - Pass `SignaturesMethods.MLDSA` to `Blockchain.verifySignature()` for critical operations (see [Signature Verification](#signature-verification))
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
4. **Document security level** - When using quantum-resistant signatures, document the ML-DSA level in your contract comments:
```typescript
/**
* Security Note: This contract uses ML-DSA-44 for quantum resistance.
* ML-DSA-44 provides NIST Category 2 security (~AES-128 equivalent).
* For higher security requirements, ML-DSA-65 or ML-DSA-87 may be used.
*/
```
**Navigation:**
- Previous: [Signature Verification](./signature-verification.md)
- Next: [Bitcoin Scripts](./bitcoin-scripts.md)