@btc-vision/btc-runtime 1.10.8 → 1.10.11

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.
Files changed (44) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +258 -137
  3. package/SECURITY.md +226 -0
  4. package/docs/README.md +614 -0
  5. package/docs/advanced/bitcoin-scripts.md +939 -0
  6. package/docs/advanced/cross-contract-calls.md +579 -0
  7. package/docs/advanced/plugins.md +1006 -0
  8. package/docs/advanced/quantum-resistance.md +660 -0
  9. package/docs/advanced/signature-verification.md +715 -0
  10. package/docs/api-reference/blockchain.md +729 -0
  11. package/docs/api-reference/events.md +642 -0
  12. package/docs/api-reference/op20.md +902 -0
  13. package/docs/api-reference/op721.md +819 -0
  14. package/docs/api-reference/safe-math.md +510 -0
  15. package/docs/api-reference/storage.md +840 -0
  16. package/docs/contracts/op-net-base.md +786 -0
  17. package/docs/contracts/op20-token.md +687 -0
  18. package/docs/contracts/op20s-signatures.md +614 -0
  19. package/docs/contracts/op721-nft.md +785 -0
  20. package/docs/contracts/reentrancy-guard.md +787 -0
  21. package/docs/core-concepts/blockchain-environment.md +724 -0
  22. package/docs/core-concepts/decorators.md +466 -0
  23. package/docs/core-concepts/events.md +652 -0
  24. package/docs/core-concepts/pointers.md +391 -0
  25. package/docs/core-concepts/security.md +473 -0
  26. package/docs/core-concepts/storage-system.md +969 -0
  27. package/docs/examples/basic-token.md +745 -0
  28. package/docs/examples/nft-with-reservations.md +1440 -0
  29. package/docs/examples/oracle-integration.md +1212 -0
  30. package/docs/examples/stablecoin.md +1180 -0
  31. package/docs/getting-started/first-contract.md +575 -0
  32. package/docs/getting-started/installation.md +384 -0
  33. package/docs/getting-started/project-structure.md +630 -0
  34. package/docs/storage/memory-maps.md +764 -0
  35. package/docs/storage/stored-arrays.md +778 -0
  36. package/docs/storage/stored-maps.md +758 -0
  37. package/docs/storage/stored-primitives.md +655 -0
  38. package/docs/types/address.md +773 -0
  39. package/docs/types/bytes-writer-reader.md +938 -0
  40. package/docs/types/calldata.md +744 -0
  41. package/docs/types/safe-math.md +446 -0
  42. package/package.json +52 -27
  43. package/runtime/memory/MapOfMap.ts +1 -0
  44. package/LICENSE.md +0 -21
@@ -0,0 +1,939 @@
1
+ # Bitcoin Scripts
2
+
3
+ OPNet provides utilities for working with Bitcoin scripts, addresses, and timelocks. This enables contracts to interact with Bitcoin's native scripting capabilities.
4
+
5
+ ## Overview
6
+
7
+ ```typescript
8
+ import {
9
+ Blockchain,
10
+ BitcoinOpcodes,
11
+ BitcoinScript,
12
+ BitcoinAddresses,
13
+ ScriptNumber,
14
+ ScriptIO,
15
+ Segwit,
16
+ Networks,
17
+ Network,
18
+ } from '@btc-vision/btc-runtime/runtime';
19
+ ```
20
+
21
+ ## Networks
22
+
23
+ OPNet supports three Bitcoin networks:
24
+
25
+ ```typescript
26
+ import { Networks, Network } from '@btc-vision/btc-runtime/runtime';
27
+
28
+ // Network enum values
29
+ Networks.Unknown // -1 (uninitialized)
30
+ Networks.Mainnet // 0
31
+ Networks.Testnet // 1
32
+ Networks.Regtest // 2
33
+
34
+ // Get human-readable prefix for addresses
35
+ const hrp = Network.hrp(Networks.Mainnet); // "bc"
36
+ const hrpTestnet = Network.hrp(Networks.Testnet); // "tb"
37
+ const hrpRegtest = Network.hrp(Networks.Regtest); // "bcrt"
38
+
39
+ // Get chain ID (32-byte identifier)
40
+ const chainId = Network.getChainId(Networks.Mainnet);
41
+
42
+ // Convert chain ID back to network enum
43
+ const network = Network.fromChainId(chainId); // Networks.Mainnet
44
+ ```
45
+
46
+ ## Address Types
47
+
48
+ ### P2PKH (Pay to Public Key Hash)
49
+
50
+ ```mermaid
51
+ ---
52
+ config:
53
+ theme: dark
54
+ ---
55
+ flowchart LR
56
+ PK["Public Key<br/>33 bytes compressed"] --> H1["SHA256"]
57
+ H1 --> H2["RIPEMD160"]
58
+ H2 --> PKH["20-byte hash"]
59
+ PKH --> B58["Base58Check Encoding"]
60
+ B58 --> ADDR["Address: 1..."]
61
+ ```
62
+
63
+ ### P2SH (Pay to Script Hash)
64
+
65
+ ```mermaid
66
+ ---
67
+ config:
68
+ theme: dark
69
+ ---
70
+ flowchart LR
71
+ SC["Redeem Script"] --> SH1["SHA256"]
72
+ SH1 --> SH2["RIPEMD160"]
73
+ SH2 --> SHH["20-byte hash"]
74
+ SHH --> B58["Base58Check Encoding"]
75
+ B58 --> ADDR["Address: 3..."]
76
+ ```
77
+
78
+ ### P2WPKH (Pay to Witness Public Key Hash)
79
+
80
+ ```mermaid
81
+ ---
82
+ config:
83
+ theme: dark
84
+ ---
85
+ flowchart LR
86
+ PK["Public Key<br/>33 bytes"] --> H1["SHA256"]
87
+ H1 --> H2["RIPEMD160"]
88
+ H2 --> WPH["20-byte hash"]
89
+ WPH --> SEG["Bech32 Encoding"]
90
+ SEG --> ADDR["Address: bc1q..."]
91
+ ```
92
+
93
+ ```typescript
94
+ import { BitcoinAddresses, Segwit } from '@btc-vision/btc-runtime/runtime';
95
+
96
+ // P2WPKH from compressed public key
97
+ public createP2WPKHAddress(pubkey: Uint8Array, hrp: string): string {
98
+ return BitcoinAddresses.p2wpkh(pubkey, hrp);
99
+ }
100
+ ```
101
+
102
+ ### P2TR (Pay to Taproot)
103
+
104
+ ```mermaid
105
+ ---
106
+ config:
107
+ theme: dark
108
+ ---
109
+ flowchart LR
110
+ PK["x-only Public Key<br/>32 bytes"] --> TR["Tweak key"]
111
+ TR --> TRK["Tweaked key<br/>32 bytes"]
112
+ TRK --> SEG["Bech32m Encoding"]
113
+ SEG --> ADDR["Address: bc1p..."]
114
+ ```
115
+
116
+ ```typescript
117
+ import { BitcoinAddresses, Networks, Network } from '@btc-vision/btc-runtime/runtime';
118
+
119
+ // P2TR address from 32-byte x-only public key
120
+ public createP2TRAddress(outputKeyX32: Uint8Array): string {
121
+ const hrp = Network.hrp(Networks.Mainnet);
122
+ return BitcoinAddresses.p2trKeyPathAddress(outputKeyX32, hrp);
123
+ }
124
+
125
+ // Verify a P2TR address
126
+ public verifyP2TRAddress(outputKeyX32: Uint8Array, address: string): bool {
127
+ const hrp = Network.hrp(Networks.Mainnet);
128
+ return BitcoinAddresses.verifyP2trAddress(outputKeyX32, address, hrp);
129
+ }
130
+ ```
131
+
132
+ ### P2WSH (Pay to Witness Script Hash)
133
+
134
+ For complex scripts requiring witness script hash:
135
+
136
+ ```typescript
137
+ import { BitcoinAddresses, Segwit, sha256 } from '@btc-vision/btc-runtime/runtime';
138
+
139
+ // Create P2WSH from script
140
+ public createP2WSHAddress(script: Uint8Array, hrp: string): string {
141
+ return Segwit.p2wsh(hrp, script);
142
+ }
143
+
144
+ // Create multisig P2WSH address
145
+ public createMultisigP2WSH(
146
+ m: i32,
147
+ pubkeys: Array<Uint8Array>,
148
+ hrp: string
149
+ ): MultisigP2wshResult {
150
+ return BitcoinAddresses.multisigP2wshAddress(m, pubkeys, hrp);
151
+ }
152
+ ```
153
+
154
+ ## Bitcoin Opcodes
155
+
156
+ The `BitcoinOpcodes` class provides all standard Bitcoin opcodes:
157
+
158
+ ```typescript
159
+ import { BitcoinOpcodes } from '@btc-vision/btc-runtime/runtime';
160
+
161
+ // Stack operations
162
+ BitcoinOpcodes.OP_DUP // 118 - Duplicate top stack item
163
+ BitcoinOpcodes.OP_DROP // 117 - Remove top stack item
164
+ BitcoinOpcodes.OP_SWAP // 124 - Swap top two items
165
+
166
+ // Crypto operations
167
+ BitcoinOpcodes.OP_HASH160 // 169 - RIPEMD160(SHA256(x))
168
+ BitcoinOpcodes.OP_SHA256 // 168 - SHA256(x)
169
+ BitcoinOpcodes.OP_CHECKSIG // 172 - Verify signature
170
+ BitcoinOpcodes.OP_CHECKMULTISIG // 174 - Verify multiple signatures
171
+
172
+ // Control flow
173
+ BitcoinOpcodes.OP_IF // 99 - Conditional execution
174
+ BitcoinOpcodes.OP_ELSE // 103
175
+ BitcoinOpcodes.OP_ENDIF // 104
176
+ BitcoinOpcodes.OP_VERIFY // 105 - Verify top is truthy
177
+ BitcoinOpcodes.OP_RETURN // 106 - Mark output as unspendable
178
+
179
+ // Timelocks
180
+ BitcoinOpcodes.OP_CHECKLOCKTIMEVERIFY // 177 - nLockTime check
181
+ BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY // 178 - Relative timelock
182
+
183
+ // Number opcodes (OP_0 through OP_16)
184
+ BitcoinOpcodes.OP_0 // 0 - Push empty/zero
185
+ BitcoinOpcodes.OP_1 // 81 - Push 1
186
+ BitcoinOpcodes.OP_2 // 82 - Push 2
187
+ BitcoinOpcodes.OP_3 // 83 - Push 3
188
+ // ... up to OP_16
189
+
190
+ // Get OP_N dynamically
191
+ BitcoinOpcodes.opN(5); // Returns OP_5 (85)
192
+ ```
193
+
194
+ ## Script Building
195
+
196
+ ### Building Scripts with BytesWriter
197
+
198
+ ```typescript
199
+ import { BytesWriter, BitcoinOpcodes, ScriptIO, ScriptNumber } from '@btc-vision/btc-runtime/runtime';
200
+
201
+ // Build a simple P2PKH-style script
202
+ public buildP2PKHScript(pubkeyHash: Uint8Array): Uint8Array {
203
+ const script = new BytesWriter(25);
204
+ script.writeU8(BitcoinOpcodes.OP_DUP);
205
+ script.writeU8(BitcoinOpcodes.OP_HASH160);
206
+ script.writeU8(20); // Push 20 bytes
207
+ script.writeBytes(pubkeyHash);
208
+ script.writeU8(BitcoinOpcodes.OP_EQUALVERIFY);
209
+ script.writeU8(BitcoinOpcodes.OP_CHECKSIG);
210
+ return script.getBuffer();
211
+ }
212
+ ```
213
+
214
+ ### Building Multisig Scripts
215
+
216
+ Use the built-in `BitcoinScript.multisig()` method:
217
+
218
+ ```typescript
219
+ import { BitcoinScript } from '@btc-vision/btc-runtime/runtime';
220
+
221
+ // Build a 2-of-3 multisig script
222
+ public buildMultisigScript(
223
+ pubkey1: Uint8Array,
224
+ pubkey2: Uint8Array,
225
+ pubkey3: Uint8Array
226
+ ): Uint8Array {
227
+ const pubkeys = new Array<Uint8Array>(3);
228
+ pubkeys[0] = pubkey1;
229
+ pubkeys[1] = pubkey2;
230
+ pubkeys[2] = pubkey3;
231
+
232
+ // m = 2 (required signatures), n = 3 (total keys)
233
+ return BitcoinScript.multisig(2, pubkeys);
234
+ }
235
+ ```
236
+
237
+ Manual multisig script building:
238
+
239
+ ```typescript
240
+ public buildMultisigManual(
241
+ pubkey1: Uint8Array,
242
+ pubkey2: Uint8Array,
243
+ pubkey3: Uint8Array
244
+ ): Uint8Array {
245
+ const script = new BytesWriter(105);
246
+
247
+ // OP_2 - require 2 signatures
248
+ script.writeU8(BitcoinOpcodes.OP_2);
249
+
250
+ // Push pubkey1 (33 bytes compressed)
251
+ script.writeU8(33);
252
+ script.writeBytes(pubkey1);
253
+
254
+ // Push pubkey2
255
+ script.writeU8(33);
256
+ script.writeBytes(pubkey2);
257
+
258
+ // Push pubkey3
259
+ script.writeU8(33);
260
+ script.writeBytes(pubkey3);
261
+
262
+ // OP_3 - 3 total pubkeys
263
+ script.writeU8(BitcoinOpcodes.OP_3);
264
+
265
+ // OP_CHECKMULTISIG
266
+ script.writeU8(BitcoinOpcodes.OP_CHECKMULTISIG);
267
+
268
+ return script.getBuffer();
269
+ }
270
+ ```
271
+
272
+ ## Timelock Scripts
273
+
274
+ ### CSV (CheckSequenceVerify) - Relative Timelock
275
+
276
+ CSV enables relative timelocks based on block count or time since the UTXO was confirmed:
277
+
278
+ ```mermaid
279
+ ---
280
+ config:
281
+ theme: dark
282
+ ---
283
+ flowchart LR
284
+ CSV1["Transaction Input<br/>nSequence field"] --> CSV2{"Timelock Type?"}
285
+ CSV2 -->|"Block-based"| CSV3["Blocks since confirmation"]
286
+ CSV2 -->|"Time-based"| CSV4["512-second units + FLAG"]
287
+ CSV3 --> CSV5["OP_CSV verifies:<br/>nSequence >= CSV_VALUE"]
288
+ CSV4 --> CSV5
289
+ CSV5 --> CSV6{"Valid?"}
290
+ CSV6 -->|"Yes"| CSV7["Continue execution"]
291
+ CSV6 -->|"No"| CSV8["Transaction invalid"]
292
+ ```
293
+
294
+ OPNet provides a built-in method for CSV scripts:
295
+
296
+ ```typescript
297
+ import { BitcoinScript, BitcoinAddresses, Network, Networks } from '@btc-vision/btc-runtime/runtime';
298
+
299
+ // Create a CSV timelock script (144 blocks = ~1 day)
300
+ public createCSVScript(pubkey: Uint8Array, lockBlocks: i32): Uint8Array {
301
+ // lockBlocks must be 0-65535
302
+ return BitcoinScript.csvTimelock(pubkey, lockBlocks);
303
+ }
304
+
305
+ // Create a P2WSH address with CSV timelock
306
+ public createCSVAddress(pubkey: Uint8Array, lockBlocks: i32): CsvP2wshResult {
307
+ const hrp = Network.hrp(Networks.Mainnet);
308
+ return BitcoinAddresses.csvP2wshAddress(pubkey, lockBlocks, hrp);
309
+ }
310
+
311
+ // Verify a CSV P2WSH address
312
+ public verifyCSVAddress(
313
+ pubkey: Uint8Array,
314
+ lockBlocks: i32,
315
+ address: string
316
+ ): bool {
317
+ const hrp = Network.hrp(Networks.Mainnet);
318
+ return BitcoinAddresses.verifyCsvP2wshAddress(pubkey, lockBlocks, address, hrp);
319
+ }
320
+ ```
321
+
322
+ ### Manual CSV Script Building
323
+
324
+ ```typescript
325
+ import { BytesWriter, BitcoinOpcodes, ScriptNumber, ScriptIO } from '@btc-vision/btc-runtime/runtime';
326
+
327
+ public buildCSVScriptManual(pubkey: Uint8Array, lockBlocks: i32): Uint8Array {
328
+ const script = new BytesWriter(50);
329
+
330
+ // Push lock value using proper encoding
331
+ if (lockBlocks == 0) {
332
+ script.writeU8(BitcoinOpcodes.OP_0);
333
+ } else if (lockBlocks <= 16) {
334
+ // Use OP_1 through OP_16
335
+ script.writeU8(BitcoinOpcodes.opN(lockBlocks));
336
+ } else {
337
+ // Encode as script number and push
338
+ const encoded = ScriptNumber.encode(lockBlocks);
339
+ ScriptIO.writePush(script, encoded);
340
+ }
341
+
342
+ // CSV check
343
+ script.writeU8(BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY);
344
+ script.writeU8(BitcoinOpcodes.OP_DROP);
345
+
346
+ // Then normal sig check
347
+ ScriptIO.writePush(script, pubkey);
348
+ script.writeU8(BitcoinOpcodes.OP_CHECKSIG);
349
+
350
+ return script.getBuffer();
351
+ }
352
+ ```
353
+
354
+ ### Time-Based Timelock
355
+
356
+ ```typescript
357
+ // Lock for ~1 week (in 512-second units)
358
+ // Bit 22 set indicates time-based
359
+ const TIME_FLAG: u32 = 0x400000;
360
+ const SECONDS_PER_UNIT: u32 = 512;
361
+
362
+ public buildTimeCSV(pubkey: Uint8Array, seconds: u32): Uint8Array {
363
+ const units = seconds / SECONDS_PER_UNIT;
364
+ const csvValue = TIME_FLAG | units;
365
+
366
+ const script = new BytesWriter(50);
367
+
368
+ // Encode the time-based CSV value
369
+ const encoded = ScriptNumber.encode(csvValue);
370
+ ScriptIO.writePush(script, encoded);
371
+
372
+ script.writeU8(BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY);
373
+ script.writeU8(BitcoinOpcodes.OP_DROP);
374
+ ScriptIO.writePush(script, pubkey);
375
+ script.writeU8(BitcoinOpcodes.OP_CHECKSIG);
376
+
377
+ return script.getBuffer();
378
+ }
379
+ ```
380
+
381
+ ### CLTV (CheckLockTimeVerify) - Absolute Timelock
382
+
383
+ CLTV enables absolute timelocks based on block height or Unix timestamp:
384
+
385
+ ```mermaid
386
+ ---
387
+ config:
388
+ theme: dark
389
+ ---
390
+ flowchart LR
391
+ CLTV1["Transaction<br/>nLockTime field"] --> CLTV2{"Timelock Type?"}
392
+ CLTV2 -->|"Block-based"| CLTV3["Block height<br/>Value < 500000000"]
393
+ CLTV2 -->|"Time-based"| CLTV4["Unix timestamp<br/>Value >= 500000000"]
394
+ CLTV3 --> CLTV5["OP_CLTV verifies:<br/>nLockTime >= CLTV_VALUE"]
395
+ CLTV4 --> CLTV5
396
+ CLTV5 --> CLTV6{"Valid?"}
397
+ CLTV6 -->|"Yes"| CLTV7["Continue execution"]
398
+ CLTV6 -->|"No"| CLTV8["Transaction invalid"]
399
+ ```
400
+
401
+ ## Script Number Encoding
402
+
403
+ Bitcoin Script uses a unique number encoding format:
404
+
405
+ ```typescript
406
+ import { ScriptNumber } from '@btc-vision/btc-runtime/runtime';
407
+
408
+ // Encode a number for script
409
+ const encoded: Uint8Array = ScriptNumber.encode(144);
410
+
411
+ // Get encoded length without encoding
412
+ const len: i32 = ScriptNumber.encodedLen(144);
413
+
414
+ // Decode a script number
415
+ const decoded: i64 = ScriptNumber.decode(encoded);
416
+
417
+ // Safe decoding with result type
418
+ const result = ScriptNumber.decodeResult(encoded, true);
419
+ if (result.success) {
420
+ const value = result.value;
421
+ } else {
422
+ const error = result.error;
423
+ }
424
+ ```
425
+
426
+ ## Script Recognition
427
+
428
+ OPNet can parse and recognize common script patterns:
429
+
430
+ ### Recognize CSV Timelock
431
+
432
+ ```typescript
433
+ import { BitcoinScript } from '@btc-vision/btc-runtime/runtime';
434
+
435
+ public parseCSVScript(script: Uint8Array): void {
436
+ const result = BitcoinScript.recognizeCsvTimelock(script);
437
+
438
+ if (result.ok) {
439
+ const csvBlocks: i64 = result.csvBlocks;
440
+ const pubkey: Uint8Array | null = result.pubkey;
441
+ // Process recognized script
442
+ }
443
+ }
444
+ ```
445
+
446
+ ### Recognize Multisig
447
+
448
+ ```typescript
449
+ public parseMultisigScript(script: Uint8Array): void {
450
+ const result = BitcoinScript.recognizeMultisig(script);
451
+
452
+ if (result.ok) {
453
+ const m: i32 = result.m; // Required signatures
454
+ const n: i32 = result.n; // Total keys
455
+ const keys: Array<Uint8Array> | null = result.pubkeys;
456
+ }
457
+ }
458
+ ```
459
+
460
+ ## Transaction Parsing
461
+
462
+ ### Reading Transaction Outputs
463
+
464
+ ```typescript
465
+ // Access current transaction outputs
466
+ public analyzeOutputs(): void {
467
+ const outputs = Blockchain.tx.outputs;
468
+
469
+ for (let i = 0; i < outputs.length; i++) {
470
+ const output = outputs[i];
471
+
472
+ // Output value in satoshis
473
+ const value: u64 = output.value;
474
+
475
+ // Output script (may be null)
476
+ const script: Uint8Array | null = output.scriptPublicKey;
477
+
478
+ // Check if output has script before parsing
479
+ if (script !== null) {
480
+ // Parse script type
481
+ if (this.isP2TR(script)) {
482
+ // Taproot output
483
+ } else if (this.isP2WSH(script)) {
484
+ // Witness script hash
485
+ }
486
+ }
487
+ }
488
+ }
489
+
490
+ private isP2TR(script: Uint8Array): bool {
491
+ // P2TR: OP_1 <32-byte key>
492
+ return script.length == 34 && script[0] == 0x51 && script[1] == 0x20;
493
+ }
494
+
495
+ private isP2WSH(script: Uint8Array): bool {
496
+ // P2WSH: OP_0 <32-byte hash>
497
+ return script.length == 34 && script[0] == 0x00 && script[1] == 0x20;
498
+ }
499
+
500
+ private isP2WPKH(script: Uint8Array): bool {
501
+ // P2WPKH: OP_0 <20-byte hash>
502
+ return script.length == 22 && script[0] == 0x00 && script[1] == 0x14;
503
+ }
504
+ ```
505
+
506
+ ### Parsing Inputs
507
+
508
+ ```typescript
509
+ // Access transaction inputs
510
+ public analyzeInputs(): void {
511
+ const inputs = Blockchain.tx.inputs;
512
+
513
+ for (let i = 0; i < inputs.length; i++) {
514
+ const input = inputs[i];
515
+
516
+ // Previous transaction hash
517
+ const txId: Uint8Array = input.txId;
518
+
519
+ // Output index being spent (u16)
520
+ const outputIndex: u16 = input.outputIndex;
521
+
522
+ // Script signature
523
+ const scriptSig: Uint8Array = input.scriptSig;
524
+
525
+ // Witness data (may be null)
526
+ const witnesses: Uint8Array[] | null = input.witnesses;
527
+
528
+ // Check if input has witnesses
529
+ if (input.hasWitnesses && witnesses !== null) {
530
+ // Process witness data
531
+ }
532
+
533
+ // Check if this is a coinbase input
534
+ if (input.isCoinbase) {
535
+ // This is a coinbase transaction
536
+ }
537
+ }
538
+ }
539
+ ```
540
+
541
+ ## Common Patterns
542
+
543
+ ### Escrow with CSV Timeout
544
+
545
+ ```typescript
546
+ // Build escrow script: Either both parties agree, or timeout to sender
547
+ public buildEscrowScript(
548
+ senderPubkey: Uint8Array,
549
+ recipientPubkey: Uint8Array,
550
+ timeoutBlocks: i32
551
+ ): Uint8Array {
552
+ const script = new BytesWriter(150);
553
+
554
+ // Path 1: Both signatures
555
+ script.writeU8(BitcoinOpcodes.OP_IF);
556
+ script.writeU8(BitcoinOpcodes.OP_2);
557
+ script.writeU8(33);
558
+ script.writeBytes(senderPubkey);
559
+ script.writeU8(33);
560
+ script.writeBytes(recipientPubkey);
561
+ script.writeU8(BitcoinOpcodes.OP_2);
562
+ script.writeU8(BitcoinOpcodes.OP_CHECKMULTISIG);
563
+
564
+ // Path 2: Timeout to sender
565
+ script.writeU8(BitcoinOpcodes.OP_ELSE);
566
+
567
+ // Push CSV blocks
568
+ if (timeoutBlocks <= 16) {
569
+ script.writeU8(BitcoinOpcodes.opN(timeoutBlocks));
570
+ } else {
571
+ const encoded = ScriptNumber.encode(timeoutBlocks);
572
+ ScriptIO.writePush(script, encoded);
573
+ }
574
+
575
+ script.writeU8(BitcoinOpcodes.OP_CHECKSEQUENCEVERIFY);
576
+ script.writeU8(BitcoinOpcodes.OP_DROP);
577
+ script.writeU8(33);
578
+ script.writeBytes(senderPubkey);
579
+ script.writeU8(BitcoinOpcodes.OP_CHECKSIG);
580
+ script.writeU8(BitcoinOpcodes.OP_ENDIF);
581
+
582
+ return script.getBuffer();
583
+ }
584
+ ```
585
+
586
+ ### Verifying Output to Contract
587
+
588
+ ```typescript
589
+ // Verify transaction sends to this contract
590
+ public verifyPaymentToContract(requiredAmount: u64): bool {
591
+ const contractAddress = Blockchain.contract.address;
592
+ const outputs = Blockchain.tx.outputs;
593
+
594
+ for (let i = 0; i < outputs.length; i++) {
595
+ const output = outputs[i];
596
+
597
+ if (output.value >= requiredAmount) {
598
+ // Check if output is to contract address
599
+ if (this.outputMatchesAddress(output.script, contractAddress)) {
600
+ return true;
601
+ }
602
+ }
603
+ }
604
+
605
+ return false;
606
+ }
607
+
608
+ private outputMatchesAddress(script: Uint8Array, address: Address): bool {
609
+ // Implementation depends on script type
610
+ // For P2TR, compare the 32-byte key in script[2..34] with address bytes
611
+ if (script.length == 34 && script[0] == 0x51 && script[1] == 0x20) {
612
+ for (let i: i32 = 0; i < 32; i++) {
613
+ if (script[i + 2] != address[i]) return false;
614
+ }
615
+ return true;
616
+ }
617
+ return false;
618
+ }
619
+ ```
620
+
621
+ ### Create OP_RETURN Data
622
+
623
+ ```typescript
624
+ // Embed data in an OP_RETURN output
625
+ public buildOpReturnScript(data: Uint8Array): Uint8Array {
626
+ if (data.length > 80) {
627
+ throw new Revert('OP_RETURN data too large');
628
+ }
629
+
630
+ const script = new BytesWriter(data.length + 2);
631
+ script.writeU8(BitcoinOpcodes.OP_RETURN);
632
+ script.writeU8(u8(data.length));
633
+ script.writeBytes(data);
634
+
635
+ return script.getBuffer();
636
+ }
637
+ ```
638
+
639
+ ## Solidity vs OPNet: Bitcoin Scripts Comparison
640
+
641
+ Bitcoin scripting is fundamentally different from Solidity, as it operates on UTXOs rather than account balances. OPNet uniquely bridges smart contract functionality with native Bitcoin scripting capabilities.
642
+
643
+ ### Feature Comparison Table
644
+
645
+ | Feature | Solidity/EVM | OPNet | OPNet Advantage |
646
+ |---------|--------------|-------|-----------------|
647
+ | **Bitcoin Script Support** | Not supported | Full support via `BitcoinOpcodes` | Native Bitcoin integration |
648
+ | **Address Types** | Single type (20 bytes) | P2PKH, P2SH, P2WPKH, P2WSH, P2TR | Full Bitcoin address compatibility |
649
+ | **Native Timelocks** | Custom implementation required | OP_CLTV, OP_CSV built-in | Consensus-enforced timelocks |
650
+ | **Multi-signature** | Custom contract logic | Native OP_CHECKMULTISIG | Bitcoin-native security |
651
+ | **Script Execution Model** | Turing-complete EVM | Stack-based Bitcoin Script | Predictable, secure execution |
652
+ | **Data Embedding** | Events, storage (expensive) | OP_RETURN (80 bytes) | Immutable on-chain data |
653
+ | **Taproot Support** | Not applicable | Full P2TR support | Schnorr-based privacy |
654
+ | **Witness Scripts** | Not applicable | P2WSH, SegWit support | Lower transaction fees |
655
+ | **Network Awareness** | Chain ID only | Mainnet/Testnet/Regtest support | Full Bitcoin network support |
656
+ | **UTXO Introspection** | Not possible | Full transaction input/output access | Bitcoin transaction analysis |
657
+
658
+ ### Capability Matrix
659
+
660
+ | Capability | Solidity | OPNet |
661
+ |------------|:--------:|:-----:|
662
+ | Create P2PKH addresses | No | Yes |
663
+ | Create P2SH addresses | No | Yes |
664
+ | Create P2WPKH (SegWit) addresses | No | Yes |
665
+ | Create P2WSH (Witness Script) addresses | No | Yes |
666
+ | Create P2TR (Taproot) addresses | No | Yes |
667
+ | Build multisig scripts | No | Yes |
668
+ | Build timelock scripts (CSV) | No | Yes |
669
+ | Build absolute timelock scripts (CLTV) | No | Yes |
670
+ | Parse/recognize Bitcoin scripts | No | Yes |
671
+ | Access transaction inputs | No | Yes |
672
+ | Access transaction outputs | No | Yes |
673
+ | Verify Bitcoin script patterns | No | Yes |
674
+ | Create OP_RETURN data | No | Yes |
675
+
676
+ ### Timelock Comparison
677
+
678
+ #### Solidity Approach (Custom Implementation)
679
+
680
+ ```solidity
681
+ // Solidity - time-based lock (requires custom implementation)
682
+ contract TimeLock {
683
+ uint256 public unlockTime;
684
+ mapping(address => uint256) public deposits;
685
+
686
+ constructor(uint256 _lockDuration) {
687
+ unlockTime = block.timestamp + _lockDuration;
688
+ }
689
+
690
+ function deposit() external payable {
691
+ deposits[msg.sender] += msg.value;
692
+ }
693
+
694
+ function withdraw() external {
695
+ require(block.timestamp >= unlockTime, "Still locked");
696
+ uint256 amount = deposits[msg.sender];
697
+ deposits[msg.sender] = 0;
698
+ payable(msg.sender).transfer(amount);
699
+ }
700
+
701
+ // Limitations:
702
+ // - Relies on block.timestamp (can be manipulated by miners)
703
+ // - No relative timelocks
704
+ // - Must implement custom logic
705
+ // - No Bitcoin script compatibility
706
+ }
707
+ ```
708
+
709
+ #### OPNet Approach (Consensus-Enforced)
710
+
711
+ ```typescript
712
+ // OPNet - CSV relative timelock (consensus-enforced)
713
+ import { BitcoinScript, BitcoinAddresses, Network, Networks } from '@btc-vision/btc-runtime/runtime';
714
+
715
+ // Create a timelock script - 144 blocks = ~1 day
716
+ const csvScript = BitcoinScript.csvTimelock(pubkey, 144);
717
+
718
+ // Create P2WSH address with CSV timelock
719
+ const result = BitcoinAddresses.csvP2wshAddress(pubkey, 144, Network.hrp(Networks.Mainnet));
720
+ const address = result.address;
721
+ const witnessScript = result.witnessScript;
722
+
723
+ // Advantages:
724
+ // - Enforced by Bitcoin consensus (not contract logic)
725
+ // - Cannot be manipulated by miners
726
+ // - Supports both relative (CSV) and absolute (CLTV) timelocks
727
+ // - Native to Bitcoin - no custom implementation needed
728
+ // - Works with any Bitcoin wallet
729
+ ```
730
+
731
+ ### Multisig Comparison
732
+
733
+ #### Solidity Approach (Custom Contract)
734
+
735
+ ```solidity
736
+ // Solidity - custom multisig (complex, resource-intensive)
737
+ contract MultiSig {
738
+ mapping(address => bool) public owners;
739
+ uint256 public required;
740
+ uint256 public transactionCount;
741
+
742
+ struct Transaction {
743
+ address to;
744
+ uint256 value;
745
+ bytes data;
746
+ bool executed;
747
+ uint256 confirmations;
748
+ }
749
+
750
+ mapping(uint256 => Transaction) public transactions;
751
+ mapping(uint256 => mapping(address => bool)) public confirmations;
752
+
753
+ function submitTransaction(address to, uint256 value, bytes memory data) public returns (uint256) {
754
+ // ... complex submission logic
755
+ }
756
+
757
+ function confirmTransaction(uint256 transactionId) public {
758
+ require(owners[msg.sender], "Not owner");
759
+ confirmations[transactionId][msg.sender] = true;
760
+ // ... confirmation logic
761
+ }
762
+
763
+ function executeTransaction(uint256 transactionId) public {
764
+ Transaction storage txn = transactions[transactionId];
765
+ require(txn.confirmations >= required, "Not enough confirmations");
766
+ // ... execution logic
767
+ }
768
+
769
+ // Limitations:
770
+ // - High storage costs
771
+ // - Complex signature verification
772
+ // - Must manually count confirmations
773
+ // - No native Bitcoin integration
774
+ }
775
+ ```
776
+
777
+ #### OPNet Approach (Native Bitcoin Multisig)
778
+
779
+ ```typescript
780
+ // OPNet - native Bitcoin multisig (simple, consensus-enforced)
781
+ import { BitcoinScript, BitcoinAddresses, Network, Networks } from '@btc-vision/btc-runtime/runtime';
782
+
783
+ // Build a 2-of-3 multisig script - one line of code!
784
+ const multisigScript = BitcoinScript.multisig(2, [pubkey1, pubkey2, pubkey3]);
785
+
786
+ // Create P2WSH address for the multisig
787
+ const result = BitcoinAddresses.multisigP2wshAddress(2, [pubkey1, pubkey2, pubkey3], Network.hrp(Networks.Mainnet));
788
+ const address = result.address;
789
+
790
+ // Verify and parse existing multisig scripts
791
+ const recognized = BitcoinScript.recognizeMultisig(someScript);
792
+ if (recognized.ok) {
793
+ const requiredSigs = recognized.m; // e.g., 2
794
+ const totalKeys = recognized.n; // e.g., 3
795
+ const publicKeys = recognized.pubkeys;
796
+ }
797
+
798
+ // Advantages:
799
+ // - No custom signature verification needed
800
+ // - Bitcoin consensus handles signature counting
801
+ // - Works with any Bitcoin wallet supporting multisig
802
+ // - No storage costs for signature storage
803
+ // - Native OP_CHECKMULTISIG opcode
804
+ ```
805
+
806
+ ### Script Building Comparison
807
+
808
+ | Task | Solidity | OPNet |
809
+ |------|----------|-------|
810
+ | Build P2PKH script | Not possible | `buildP2PKHScript(pubkeyHash)` |
811
+ | Build multisig script | Custom contract | `BitcoinScript.multisig(m, pubkeys)` |
812
+ | Build CSV timelock | Custom logic | `BitcoinScript.csvTimelock(pubkey, blocks)` |
813
+ | Create witness address | Not possible | `Segwit.p2wsh(hrp, script)` |
814
+ | Parse script patterns | Not possible | `BitcoinScript.recognizeCsvTimelock(script)` |
815
+ | Encode script numbers | Not needed | `ScriptNumber.encode(value)` |
816
+ | Access Bitcoin opcodes | Not available | All opcodes via `BitcoinOpcodes` |
817
+
818
+ ### Transaction Introspection
819
+
820
+ | Feature | Solidity | OPNet |
821
+ |---------|----------|-------|
822
+ | Access tx outputs | `msg.value` only | `Blockchain.tx.outputs` (full array) |
823
+ | Access tx inputs | Not possible | `Blockchain.tx.inputs` (full array) |
824
+ | Get output value | Limited | `output.value` (satoshis) |
825
+ | Get output script | Not possible | `output.scriptPublicKey` (full script bytes, nullable) |
826
+ | Get input txid | Not possible | `input.txId` |
827
+ | Get input output index | Not possible | `input.outputIndex` (u16) |
828
+ | Get witness data | Not possible | `input.witnesses` (array, nullable) |
829
+ | Get script signature | Not possible | `input.scriptSig` |
830
+ | Check coinbase input | Not possible | `input.isCoinbase` |
831
+ | Parse script type | Not possible | Pattern matching on script bytes |
832
+
833
+ ### Data Embedding Comparison
834
+
835
+ ```solidity
836
+ // Solidity - Events (expensive, not part of state)
837
+ contract DataEmbed {
838
+ event DataStored(bytes32 indexed hash, bytes data);
839
+
840
+ function storeData(bytes memory data) external {
841
+ emit DataStored(keccak256(data), data);
842
+ // Cost scales with data size
843
+ // Data is not part of UTXO set
844
+ }
845
+ }
846
+ ```
847
+
848
+ ```typescript
849
+ // OPNet - OP_RETURN (native Bitcoin, permanent)
850
+ import { BytesWriter, BitcoinOpcodes } from '@btc-vision/btc-runtime/runtime';
851
+
852
+ function buildOpReturnScript(data: Uint8Array): Uint8Array {
853
+ if (data.length > 80) {
854
+ throw new Revert('OP_RETURN data too large');
855
+ }
856
+
857
+ const script = new BytesWriter(data.length + 2);
858
+ script.writeU8(BitcoinOpcodes.OP_RETURN);
859
+ script.writeU8(u8(data.length));
860
+ script.writeBytes(data);
861
+
862
+ return script.getBuffer();
863
+ }
864
+
865
+ // Advantages:
866
+ // - Standard Bitcoin OP_RETURN
867
+ // - Permanent, immutable storage
868
+ // - Recognized by all Bitcoin explorers
869
+ // - No complex event parsing needed
870
+ ```
871
+
872
+ ### Why OPNet for Bitcoin Integration?
873
+
874
+ | Solidity Limitation | OPNet Solution |
875
+ |---------------------|----------------|
876
+ | Cannot interact with Bitcoin | Full Bitcoin script support |
877
+ | No UTXO awareness | Complete transaction introspection |
878
+ | Single address format | All Bitcoin address types |
879
+ | No native timelocks | CSV and CLTV support |
880
+ | Custom multisig required | Native OP_CHECKMULTISIG |
881
+ | No Taproot support | Full P2TR integration |
882
+ | EVM-only execution | Bitcoin consensus enforcement |
883
+
884
+ ## Best Practices
885
+
886
+ ### 1. Validate All Script Inputs
887
+
888
+ ```typescript
889
+ public processScript(script: Uint8Array): void {
890
+ // Check minimum length
891
+ if (script.length < 1) {
892
+ throw new Revert('Empty script');
893
+ }
894
+
895
+ // Check maximum length
896
+ if (script.length > 10000) {
897
+ throw new Revert('Script too large');
898
+ }
899
+
900
+ // Validate script structure
901
+ // ...
902
+ }
903
+ ```
904
+
905
+ ### 2. Use Named Opcode Constants
906
+
907
+ ```typescript
908
+ // Good: Named constants from BitcoinOpcodes
909
+ if (script[0] == BitcoinOpcodes.OP_RETURN) { }
910
+
911
+ // Bad: Magic numbers
912
+ if (script[0] == 0x6a) { }
913
+ ```
914
+
915
+ ### 3. Use Networks Enum for Network Selection
916
+
917
+ ```typescript
918
+ // Good: Use Networks enum
919
+ const hrp = Network.hrp(Networks.Mainnet);
920
+
921
+ // Bad: Hardcoded strings
922
+ const hrp = 'bc';
923
+ ```
924
+
925
+ ### 4. Use Built-in Script Builders
926
+
927
+ ```typescript
928
+ // Good: Use BitcoinScript methods
929
+ const csvScript = BitcoinScript.csvTimelock(pubkey, 144);
930
+ const multisigScript = BitcoinScript.multisig(2, pubkeys);
931
+
932
+ // Only build manually when needed for custom scripts
933
+ ```
934
+
935
+ ---
936
+
937
+ **Navigation:**
938
+ - Previous: [Quantum Resistance](./quantum-resistance.md)
939
+ - Next: [Plugins](./plugins.md)