UNPKG

@btc-vision/btc-runtime

Version:

Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.

940 lines (741 loc) 27.3 kB
# Bitcoin Scripts OP_NET provides utilities for working with Bitcoin scripts, addresses, and timelocks. This enables contracts to interact with Bitcoin's native scripting capabilities. ## Overview ```typescript import { Blockchain, BitcoinOpcodes, BitcoinScript, BitcoinAddresses, ScriptNumber, ScriptIO, Segwit, Networks, Network, } from '@btc-vision/btc-runtime/runtime'; ``` ## Networks OP_NET supports three Bitcoin networks: ```typescript import { Networks, Network } from '@btc-vision/btc-runtime/runtime'; // Network enum values Networks.Unknown // -1 (uninitialized) Networks.Mainnet // 0 Networks.Testnet // 1 Networks.Regtest // 2 // Get human-readable prefix for addresses const hrp = Network.hrp(Networks.Mainnet); // "bc" const hrpTestnet = Network.hrp(Networks.Testnet); // "tb" const hrpRegtest = Network.hrp(Networks.Regtest); // "bcrt" // Get chain ID (32-byte identifier) const chainId = Network.getChainId(Networks.Mainnet); // Convert chain ID back to network enum const network = Network.fromChainId(chainId); // Networks.Mainnet ``` ## Address Types ### P2PKH (Pay to Public Key Hash) ```mermaid --- config: theme: dark --- flowchart LR PK["Public Key<br/>33 bytes compressed"] --> H1["SHA256"] H1 --> H2["RIPEMD160"] H2 --> PKH["20-byte hash"] PKH --> B58["Base58Check Encoding"] B58 --> ADDR["Address: 1..."] ``` ### P2SH (Pay to Script Hash) ```mermaid --- config: theme: dark --- flowchart LR SC["Redeem Script"] --> SH1["SHA256"] SH1 --> SH2["RIPEMD160"] SH2 --> SHH["20-byte hash"] SHH --> B58["Base58Check Encoding"] B58 --> ADDR["Address: 3..."] ``` ### P2WPKH (Pay to Witness Public Key Hash) ```mermaid --- config: theme: dark --- flowchart LR PK["Public Key<br/>33 bytes"] --> H1["SHA256"] H1 --> H2["RIPEMD160"] H2 --> WPH["20-byte hash"] WPH --> SEG["Bech32 Encoding"] SEG --> ADDR["Address: bc1q..."] ``` ```typescript import { BitcoinAddresses, Segwit } from '@btc-vision/btc-runtime/runtime'; // P2WPKH from compressed public key public createP2WPKHAddress(pubkey: Uint8Array, hrp: string): string { return BitcoinAddresses.p2wpkh(pubkey, hrp); } ``` ### P2TR (Pay to Taproot) ```mermaid --- config: theme: dark --- flowchart LR PK["x-only Public Key<br/>32 bytes"] --> TR["Tweak key"] TR --> TRK["Tweaked key<br/>32 bytes"] TRK --> SEG["Bech32m Encoding"] SEG --> ADDR["Address: bc1p..."] ``` ```typescript import { BitcoinAddresses, Networks, Network } from '@btc-vision/btc-runtime/runtime'; // P2TR address from 32-byte x-only public key public createP2TRAddress(outputKeyX32: Uint8Array): string { const hrp = Network.hrp(Networks.Mainnet); return BitcoinAddresses.p2trKeyPathAddress(outputKeyX32, hrp); } // Verify a P2TR address public verifyP2TRAddress(outputKeyX32: Uint8Array, address: string): bool { const hrp = Network.hrp(Networks.Mainnet); return BitcoinAddresses.verifyP2trAddress(outputKeyX32, address, hrp); } ``` ### P2WSH (Pay to Witness Script Hash) For complex scripts requiring witness script hash: ```typescript import { BitcoinAddresses, Segwit, sha256 } from '@btc-vision/btc-runtime/runtime'; // Create P2WSH from script public createP2WSHAddress(script: Uint8Array, hrp: string): string { return Segwit.p2wsh(hrp, script); } // Create multisig P2WSH address public createMultisigP2WSH( m: i32, pubkeys: Array<Uint8Array>, hrp: string ): MultisigP2wshResult { return BitcoinAddresses.multisigP2wshAddress(m, pubkeys, hrp); } ``` ## Bitcoin Opcodes The `BitcoinOpcodes` class provides all standard Bitcoin opcodes: ```typescript import { BitcoinOpcodes } from '@btc-vision/btc-runtime/runtime'; // Stack operations BitcoinOpcodes.OP_DUP // 118 - Duplicate top stack item BitcoinOpcodes.OP_DROP // 117 - Remove top stack item BitcoinOpcodes.OP_SWAP // 124 - Swap top two items // Crypto operations BitcoinOpcodes.OP_HASH160 // 169 - RIPEMD160(SHA256(x)) BitcoinOpcodes.OP_SHA256 // 168 - SHA256(x) BitcoinOpcodes.OP_CHECKSIG // 172 - Verify signature BitcoinOpcodes.OP_CHECKMULTISIG // 174 - Verify multiple signatures // Control flow BitcoinOpcodes.OP_IF // 99 - Conditional execution BitcoinOpcodes.OP_ELSE // 103 BitcoinOpcodes.OP_ENDIF // 104 BitcoinOpcodes.OP_VERIFY // 105 - Verify top is truthy BitcoinOpcodes.OP_RETURN // 106 - Mark output as unspendable // Timelocks BitcoinOpcodes.OP_CHECKLOCKTIMEVERIFY // 177 - nLockTime check BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY // 178 - Relative timelock // Number opcodes (OP_0 through OP_16) BitcoinOpcodes.OP_0 // 0 - Push empty/zero BitcoinOpcodes.OP_1 // 81 - Push 1 BitcoinOpcodes.OP_2 // 82 - Push 2 BitcoinOpcodes.OP_3 // 83 - Push 3 // ... up to OP_16 // Get OP_N dynamically BitcoinOpcodes.opN(5); // Returns OP_5 (85) ``` ## Script Building ### Building Scripts with BytesWriter ```typescript import { BytesWriter, BitcoinOpcodes, ScriptIO, ScriptNumber } from '@btc-vision/btc-runtime/runtime'; // Build a simple P2PKH-style script public buildP2PKHScript(pubkeyHash: Uint8Array): Uint8Array { const script = new BytesWriter(25); script.writeU8(BitcoinOpcodes.OP_DUP); script.writeU8(BitcoinOpcodes.OP_HASH160); script.writeU8(20); // Push 20 bytes script.writeBytes(pubkeyHash); script.writeU8(BitcoinOpcodes.OP_EQUALVERIFY); script.writeU8(BitcoinOpcodes.OP_CHECKSIG); return script.getBuffer(); } ``` ### Building Multisig Scripts Use the built-in `BitcoinScript.multisig()` method: ```typescript import { BitcoinScript } from '@btc-vision/btc-runtime/runtime'; // Build a 2-of-3 multisig script public buildMultisigScript( pubkey1: Uint8Array, pubkey2: Uint8Array, pubkey3: Uint8Array ): Uint8Array { const pubkeys = new Array<Uint8Array>(3); pubkeys[0] = pubkey1; pubkeys[1] = pubkey2; pubkeys[2] = pubkey3; // m = 2 (required signatures), n = 3 (total keys) return BitcoinScript.multisig(2, pubkeys); } ``` Manual multisig script building: ```typescript public buildMultisigManual( pubkey1: Uint8Array, pubkey2: Uint8Array, pubkey3: Uint8Array ): Uint8Array { const script = new BytesWriter(105); // OP_2 - require 2 signatures script.writeU8(BitcoinOpcodes.OP_2); // Push pubkey1 (33 bytes compressed) script.writeU8(33); script.writeBytes(pubkey1); // Push pubkey2 script.writeU8(33); script.writeBytes(pubkey2); // Push pubkey3 script.writeU8(33); script.writeBytes(pubkey3); // OP_3 - 3 total pubkeys script.writeU8(BitcoinOpcodes.OP_3); // OP_CHECKMULTISIG script.writeU8(BitcoinOpcodes.OP_CHECKMULTISIG); return script.getBuffer(); } ``` ## Timelock Scripts ### CSV (CheckSequenceVerify) - Relative Timelock CSV enables relative timelocks based on block count or time since the UTXO was confirmed: ```mermaid --- config: theme: dark --- flowchart LR CSV1["Transaction Input<br/>nSequence field"] --> CSV2{"Timelock Type?"} CSV2 -->|"Block-based"| CSV3["Blocks since confirmation"] CSV2 -->|"Time-based"| CSV4["512-second units + FLAG"] CSV3 --> CSV5["OP_CSV verifies:<br/>nSequence >= CSV_VALUE"] CSV4 --> CSV5 CSV5 --> CSV6{"Valid?"} CSV6 -->|"Yes"| CSV7["Continue execution"] CSV6 -->|"No"| CSV8["Transaction invalid"] ``` OP_NET provides a built-in method for CSV scripts: ```typescript import { BitcoinScript, BitcoinAddresses, Network, Networks } from '@btc-vision/btc-runtime/runtime'; // Create a CSV timelock script (144 blocks = ~1 day) public createCSVScript(pubkey: Uint8Array, lockBlocks: i32): Uint8Array { // lockBlocks must be 0-65535 return BitcoinScript.csvTimelock(pubkey, lockBlocks); } // Create a P2WSH address with CSV timelock public createCSVAddress(pubkey: Uint8Array, lockBlocks: i32): CsvP2wshResult { const hrp = Network.hrp(Networks.Mainnet); return BitcoinAddresses.csvP2wshAddress(pubkey, lockBlocks, hrp); } // Verify a CSV P2WSH address public verifyCSVAddress( pubkey: Uint8Array, lockBlocks: i32, address: string ): bool { const hrp = Network.hrp(Networks.Mainnet); return BitcoinAddresses.verifyCsvP2wshAddress(pubkey, lockBlocks, address, hrp); } ``` ### Manual CSV Script Building ```typescript import { BytesWriter, BitcoinOpcodes, ScriptNumber, ScriptIO } from '@btc-vision/btc-runtime/runtime'; public buildCSVScriptManual(pubkey: Uint8Array, lockBlocks: i32): Uint8Array { const script = new BytesWriter(50); // Push lock value using proper encoding if (lockBlocks == 0) { script.writeU8(BitcoinOpcodes.OP_0); } else if (lockBlocks <= 16) { // Use OP_1 through OP_16 script.writeU8(BitcoinOpcodes.opN(lockBlocks)); } else { // Encode as script number and push const encoded = ScriptNumber.encode(lockBlocks); ScriptIO.writePush(script, encoded); } // CSV check script.writeU8(BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY); script.writeU8(BitcoinOpcodes.OP_DROP); // Then normal sig check ScriptIO.writePush(script, pubkey); script.writeU8(BitcoinOpcodes.OP_CHECKSIG); return script.getBuffer(); } ``` ### Time-Based Timelock ```typescript // Lock for ~1 week (in 512-second units) // Bit 22 set indicates time-based const TIME_FLAG: u32 = 0x400000; const SECONDS_PER_UNIT: u32 = 512; public buildTimeCSV(pubkey: Uint8Array, seconds: u32): Uint8Array { const units = seconds / SECONDS_PER_UNIT; const csvValue = TIME_FLAG | units; const script = new BytesWriter(50); // Encode the time-based CSV value const encoded = ScriptNumber.encode(csvValue); ScriptIO.writePush(script, encoded); script.writeU8(BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY); script.writeU8(BitcoinOpcodes.OP_DROP); ScriptIO.writePush(script, pubkey); script.writeU8(BitcoinOpcodes.OP_CHECKSIG); return script.getBuffer(); } ``` ### CLTV (CheckLockTimeVerify) - Absolute Timelock CLTV enables absolute timelocks based on block height or Unix timestamp: ```mermaid --- config: theme: dark --- flowchart LR CLTV1["Transaction<br/>nLockTime field"] --> CLTV2{"Timelock Type?"} CLTV2 -->|"Block-based"| CLTV3["Block height<br/>Value < 500000000"] CLTV2 -->|"Time-based"| CLTV4["Unix timestamp<br/>Value >= 500000000"] CLTV3 --> CLTV5["OP_CLTV verifies:<br/>nLockTime >= CLTV_VALUE"] CLTV4 --> CLTV5 CLTV5 --> CLTV6{"Valid?"} CLTV6 -->|"Yes"| CLTV7["Continue execution"] CLTV6 -->|"No"| CLTV8["Transaction invalid"] ``` ## Script Number Encoding Bitcoin Script uses a unique number encoding format: ```typescript import { ScriptNumber } from '@btc-vision/btc-runtime/runtime'; // Encode a number for script const encoded: Uint8Array = ScriptNumber.encode(144); // Get encoded length without encoding const len: i32 = ScriptNumber.encodedLen(144); // Decode a script number const decoded: i64 = ScriptNumber.decode(encoded); // Safe decoding with result type const result = ScriptNumber.decodeResult(encoded, true); if (result.success) { const value = result.value; } else { const error = result.error; } ``` ## Script Recognition OP_NET can parse and recognize common script patterns: ### Recognize CSV Timelock ```typescript import { BitcoinScript } from '@btc-vision/btc-runtime/runtime'; public parseCSVScript(script: Uint8Array): void { const result = BitcoinScript.recognizeCsvTimelock(script); if (result.ok) { const csvBlocks: i64 = result.csvBlocks; const pubkey: Uint8Array | null = result.pubkey; // Process recognized script } } ``` ### Recognize Multisig ```typescript public parseMultisigScript(script: Uint8Array): void { const result = BitcoinScript.recognizeMultisig(script); if (result.ok) { const m: i32 = result.m; // Required signatures const n: i32 = result.n; // Total keys const keys: Array<Uint8Array> | null = result.pubkeys; } } ``` ## Transaction Parsing ### Reading Transaction Outputs ```typescript // Access current transaction outputs public analyzeOutputs(): void { const outputs = Blockchain.tx.outputs; for (let i = 0; i < outputs.length; i++) { const output = outputs[i]; // Output value in satoshis const value: u64 = output.value; // Output script (may be null) const script: Uint8Array | null = output.scriptPublicKey; // Check if output has script before parsing if (script !== null) { // Parse script type if (this.isP2TR(script)) { // Taproot output } else if (this.isP2WSH(script)) { // Witness script hash } } } } private isP2TR(script: Uint8Array): bool { // P2TR: OP_1 <32-byte key> return script.length == 34 && script[0] == 0x51 && script[1] == 0x20; } private isP2WSH(script: Uint8Array): bool { // P2WSH: OP_0 <32-byte hash> return script.length == 34 && script[0] == 0x00 && script[1] == 0x20; } private isP2WPKH(script: Uint8Array): bool { // P2WPKH: OP_0 <20-byte hash> return script.length == 22 && script[0] == 0x00 && script[1] == 0x14; } ``` ### Parsing Inputs ```typescript // Access transaction inputs public analyzeInputs(): void { const inputs = Blockchain.tx.inputs; for (let i = 0; i < inputs.length; i++) { const input = inputs[i]; // Previous transaction hash const txId: Uint8Array = input.txId; // Output index being spent (u16) const outputIndex: u16 = input.outputIndex; // Script signature const scriptSig: Uint8Array = input.scriptSig; // Witness data (may be null) const witnesses: Uint8Array[] | null = input.witnesses; // Check if input has witnesses if (input.hasWitnesses && witnesses !== null) { // Process witness data } // Check if this is a coinbase input if (input.isCoinbase) { // This is a coinbase transaction } } } ``` ## Common Patterns ### Escrow with CSV Timeout ```typescript // Build escrow script: Either both parties agree, or timeout to sender public buildEscrowScript( senderPubkey: Uint8Array, recipientPubkey: Uint8Array, timeoutBlocks: i32 ): Uint8Array { const script = new BytesWriter(150); // Path 1: Both signatures script.writeU8(BitcoinOpcodes.OP_IF); script.writeU8(BitcoinOpcodes.OP_2); script.writeU8(33); script.writeBytes(senderPubkey); script.writeU8(33); script.writeBytes(recipientPubkey); script.writeU8(BitcoinOpcodes.OP_2); script.writeU8(BitcoinOpcodes.OP_CHECKMULTISIG); // Path 2: Timeout to sender script.writeU8(BitcoinOpcodes.OP_ELSE); // Push CSV blocks if (timeoutBlocks <= 16) { script.writeU8(BitcoinOpcodes.opN(timeoutBlocks)); } else { const encoded = ScriptNumber.encode(timeoutBlocks); ScriptIO.writePush(script, encoded); } script.writeU8(BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY); script.writeU8(BitcoinOpcodes.OP_DROP); script.writeU8(33); script.writeBytes(senderPubkey); script.writeU8(BitcoinOpcodes.OP_CHECKSIG); script.writeU8(BitcoinOpcodes.OP_ENDIF); return script.getBuffer(); } ``` ### Verifying Output to Contract ```typescript // Verify transaction sends to this contract public verifyPaymentToContract(requiredAmount: u64): bool { const contractAddress = Blockchain.contract.address; const outputs = Blockchain.tx.outputs; for (let i = 0; i < outputs.length; i++) { const output = outputs[i]; if (output.value >= requiredAmount) { // Check if output is to contract address if (this.outputMatchesAddress(output.script, contractAddress)) { return true; } } } return false; } private outputMatchesAddress(script: Uint8Array, address: Address): bool { // Implementation depends on script type // For P2TR, compare the 32-byte key in script[2..34] with address bytes if (script.length == 34 && script[0] == 0x51 && script[1] == 0x20) { for (let i: i32 = 0; i < 32; i++) { if (script[i + 2] != address[i]) return false; } return true; } return false; } ``` ### Create OP_RETURN Data ```typescript // Embed data in an OP_RETURN output public buildOpReturnScript(data: Uint8Array): Uint8Array { if (data.length > 80) { throw new Revert('OP_RETURN data too large'); } const script = new BytesWriter(data.length + 2); script.writeU8(BitcoinOpcodes.OP_RETURN); script.writeU8(u8(data.length)); script.writeBytes(data); return script.getBuffer(); } ``` ## Solidity vs OP_NET: Bitcoin Scripts Comparison Bitcoin scripting is fundamentally different from Solidity, as it operates on UTXOs rather than account balances. OP_NET uniquely bridges smart contract functionality with native Bitcoin scripting capabilities. ### Feature Comparison Table | Feature | Solidity/EVM | OP_NET | OP_NET Advantage | |---------|--------------|-------|-----------------| | **Bitcoin Script Support** | Not supported | Full support via `BitcoinOpcodes` | Native Bitcoin integration | | **Address Types** | Single type (20 bytes) | P2PKH, P2SH, P2WPKH, P2WSH, P2TR | Full Bitcoin address compatibility | | **Native Timelocks** | Custom implementation required | OP_CLTV, OP_CSV built-in | Consensus-enforced timelocks | | **Multi-signature** | Custom contract logic | Native OP_CHECKMULTISIG | Bitcoin-native security | | **Script Execution Model** | Turing-complete EVM | Stack-based Bitcoin Script | Predictable, secure execution | | **Data Embedding** | Events, storage (expensive) | OP_RETURN (80 bytes) | Immutable on-chain data | | **Taproot Support** | Not applicable | Full P2TR support | Schnorr-based privacy | | **Witness Scripts** | Not applicable | P2WSH, SegWit support | Lower transaction fees | | **Network Awareness** | Chain ID only | Mainnet/Testnet/Regtest support | Full Bitcoin network support | | **UTXO Introspection** | Not possible | Full transaction input/output access | Bitcoin transaction analysis | ### Capability Matrix | Capability | Solidity | OP_NET | |------------|:--------:|:-----:| | Create P2PKH addresses | No | Yes | | Create P2SH addresses | No | Yes | | Create P2WPKH (SegWit) addresses | No | Yes | | Create P2WSH (Witness Script) addresses | No | Yes | | Create P2TR (Taproot) addresses | No | Yes | | Build multisig scripts | No | Yes | | Build timelock scripts (CSV) | No | Yes | | Build absolute timelock scripts (CLTV) | No | Yes | | Parse/recognize Bitcoin scripts | No | Yes | | Access transaction inputs | No | Yes | | Access transaction outputs | No | Yes | | Verify Bitcoin script patterns | No | Yes | | Create OP_RETURN data | No | Yes | ### Timelock Comparison #### Solidity Approach (Custom Implementation) ```solidity // Solidity - time-based lock (requires custom implementation) contract TimeLock { uint256 public unlockTime; mapping(address => uint256) public deposits; constructor(uint256 _lockDuration) { unlockTime = block.timestamp + _lockDuration; } function deposit() external payable { deposits[msg.sender] += msg.value; } function withdraw() external { require(block.timestamp >= unlockTime, "Still locked"); uint256 amount = deposits[msg.sender]; deposits[msg.sender] = 0; payable(msg.sender).transfer(amount); } // Limitations: // - Relies on block.timestamp (can be manipulated by miners) // - No relative timelocks // - Must implement custom logic // - No Bitcoin script compatibility } ``` #### OP_NET Approach (Consensus-Enforced) ```typescript // OP_NET - CSV relative timelock (consensus-enforced) import { BitcoinScript, BitcoinAddresses, Network, Networks } from '@btc-vision/btc-runtime/runtime'; // Create a timelock script - 144 blocks = ~1 day const csvScript = BitcoinScript.csvTimelock(pubkey, 144); // Create P2WSH address with CSV timelock const result = BitcoinAddresses.csvP2wshAddress(pubkey, 144, Network.hrp(Networks.Mainnet)); const address = result.address; const witnessScript = result.witnessScript; // Advantages: // - Enforced by Bitcoin consensus (not contract logic) // - Cannot be manipulated by miners // - Supports both relative (CSV) and absolute (CLTV) timelocks // - Native to Bitcoin - no custom implementation needed // - Works with any Bitcoin wallet ``` ### Multisig Comparison #### Solidity Approach (Custom Contract) ```solidity // Solidity - custom multisig (complex, resource-intensive) contract MultiSig { mapping(address => bool) public owners; uint256 public required; uint256 public transactionCount; struct Transaction { address to; uint256 value; bytes data; bool executed; uint256 confirmations; } mapping(uint256 => Transaction) public transactions; mapping(uint256 => mapping(address => bool)) public confirmations; function submitTransaction(address to, uint256 value, bytes memory data) public returns (uint256) { // ... complex submission logic } function confirmTransaction(uint256 transactionId) public { require(owners[msg.sender], "Not owner"); confirmations[transactionId][msg.sender] = true; // ... confirmation logic } function executeTransaction(uint256 transactionId) public { Transaction storage txn = transactions[transactionId]; require(txn.confirmations >= required, "Not enough confirmations"); // ... execution logic } // Limitations: // - High storage costs // - Complex signature verification // - Must manually count confirmations // - No native Bitcoin integration } ``` #### OP_NET Approach (Native Bitcoin Multisig) ```typescript // OP_NET - native Bitcoin multisig (simple, consensus-enforced) import { BitcoinScript, BitcoinAddresses, Network, Networks } from '@btc-vision/btc-runtime/runtime'; // Build a 2-of-3 multisig script - one line of code! const multisigScript = BitcoinScript.multisig(2, [pubkey1, pubkey2, pubkey3]); // Create P2WSH address for the multisig const result = BitcoinAddresses.multisigP2wshAddress(2, [pubkey1, pubkey2, pubkey3], Network.hrp(Networks.Mainnet)); const address = result.address; // Verify and parse existing multisig scripts const recognized = BitcoinScript.recognizeMultisig(someScript); if (recognized.ok) { const requiredSigs = recognized.m; // e.g., 2 const totalKeys = recognized.n; // e.g., 3 const publicKeys = recognized.pubkeys; } // Advantages: // - No custom signature verification needed // - Bitcoin consensus handles signature counting // - Works with any Bitcoin wallet supporting multisig // - No storage costs for signature storage // - Native OP_CHECKMULTISIG opcode ``` ### Script Building Comparison | Task | Solidity | OP_NET | |------|----------|-------| | Build P2PKH script | Not possible | `buildP2PKHScript(pubkeyHash)` | | Build multisig script | Custom contract | `BitcoinScript.multisig(m, pubkeys)` | | Build CSV timelock | Custom logic | `BitcoinScript.csvTimelock(pubkey, blocks)` | | Create witness address | Not possible | `Segwit.p2wsh(hrp, script)` | | Parse script patterns | Not possible | `BitcoinScript.recognizeCsvTimelock(script)` | | Encode script numbers | Not needed | `ScriptNumber.encode(value)` | | Access Bitcoin opcodes | Not available | All opcodes via `BitcoinOpcodes` | ### Transaction Introspection | Feature | Solidity | OP_NET | |---------|----------|-------| | Access tx outputs | `msg.value` only | `Blockchain.tx.outputs` (full array) | | Access tx inputs | Not possible | `Blockchain.tx.inputs` (full array) | | Get output value | Limited | `output.value` (satoshis) | | Get output script | Not possible | `output.scriptPublicKey` (full script bytes, nullable) | | Get input txid | Not possible | `input.txId` | | Get input output index | Not possible | `input.outputIndex` (u16) | | Get witness data | Not possible | `input.witnesses` (array, nullable) | | Get script signature | Not possible | `input.scriptSig` | | Check coinbase input | Not possible | `input.isCoinbase` | | Parse script type | Not possible | Pattern matching on script bytes | ### Data Embedding Comparison ```solidity // Solidity - Events (expensive, not part of state) contract DataEmbed { event DataStored(bytes32 indexed hash, bytes data); function storeData(bytes memory data) external { emit DataStored(keccak256(data), data); // Cost scales with data size // Data is not part of UTXO set } } ``` ```typescript // OP_NET - OP_RETURN (native Bitcoin, permanent) import { BytesWriter, BitcoinOpcodes } from '@btc-vision/btc-runtime/runtime'; function buildOpReturnScript(data: Uint8Array): Uint8Array { if (data.length > 80) { throw new Revert('OP_RETURN data too large'); } const script = new BytesWriter(data.length + 2); script.writeU8(BitcoinOpcodes.OP_RETURN); script.writeU8(u8(data.length)); script.writeBytes(data); return script.getBuffer(); } // Advantages: // - Standard Bitcoin OP_RETURN // - Permanent, immutable storage // - Recognized by all Bitcoin explorers // - No complex event parsing needed ``` ### Why OP_NET for Bitcoin Integration? | Solidity Limitation | OP_NET Solution | |---------------------|----------------| | Cannot interact with Bitcoin | Full Bitcoin script support | | No UTXO awareness | Complete transaction introspection | | Single address format | All Bitcoin address types | | No native timelocks | CSV and CLTV support | | Custom multisig required | Native OP_CHECKMULTISIG | | No Taproot support | Full P2TR integration | | EVM-only execution | Bitcoin consensus enforcement | ## Best Practices ### 1. Validate All Script Inputs ```typescript public processScript(script: Uint8Array): void { // Check minimum length if (script.length < 1) { throw new Revert('Empty script'); } // Check maximum length if (script.length > 10000) { throw new Revert('Script too large'); } // Validate script structure // ... } ``` ### 2. Use Named Opcode Constants ```typescript // Good: Named constants from BitcoinOpcodes if (script[0] == BitcoinOpcodes.OP_RETURN) { } // Bad: Magic numbers if (script[0] == 0x6a) { } ``` ### 3. Use Networks Enum for Network Selection ```typescript // Good: Use Networks enum const hrp = Network.hrp(Networks.Mainnet); // Bad: Hardcoded strings const hrp = 'bc'; ``` ### 4. Use Built-in Script Builders ```typescript // Good: Use BitcoinScript methods const csvScript = BitcoinScript.csvTimelock(pubkey, 144); const multisigScript = BitcoinScript.multisig(2, pubkeys); // Only build manually when needed for custom scripts ``` --- **Navigation:** - Previous: [Quantum Resistance](./quantum-resistance.md) - Next: [Plugins](./plugins.md)