@btc-vision/bitcoin 6.4.0 → 6.4.2

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 (45) hide show
  1. package/browser/address.d.ts +1 -0
  2. package/browser/index.js +1 -1
  3. package/browser/payments/index.d.ts +4 -0
  4. package/browser/payments/p2op.d.ts +2 -0
  5. package/browser/psbt/psbtutils.d.ts +1 -0
  6. package/build/address.d.ts +1 -0
  7. package/build/address.js +56 -8
  8. package/build/networks.js +6 -6
  9. package/build/payments/index.d.ts +4 -0
  10. package/build/payments/index.js +1 -0
  11. package/build/payments/p2op.d.ts +2 -0
  12. package/build/payments/p2op.js +108 -0
  13. package/build/psbt/psbtutils.d.ts +1 -0
  14. package/build/psbt/psbtutils.js +2 -0
  15. package/package.json +1 -1
  16. package/src/address.ts +68 -9
  17. package/src/block.ts +233 -233
  18. package/src/bufferutils.ts +188 -188
  19. package/src/networks.ts +6 -6
  20. package/src/payments/index.ts +68 -62
  21. package/src/payments/p2op.ts +136 -0
  22. package/src/psbt/psbtutils.ts +2 -0
  23. package/src/psbt.ts +2187 -2187
  24. package/test/address.spec.ts +155 -155
  25. package/test/bitcoin.core.spec.ts +212 -212
  26. package/test/block.spec.ts +171 -171
  27. package/test/bufferutils.spec.ts +450 -450
  28. package/test/crypto.spec.ts +49 -49
  29. package/test/integration/addresses.spec.ts +142 -142
  30. package/test/integration/bip32.spec.ts +130 -130
  31. package/test/integration/blocks.spec.ts +28 -28
  32. package/test/integration/cltv.spec.ts +241 -241
  33. package/test/integration/csv.spec.ts +452 -452
  34. package/test/integration/payments.spec.ts +110 -110
  35. package/test/integration/taproot.spec.ts +663 -663
  36. package/test/integration/transactions.spec.ts +668 -668
  37. package/test/payments.spec.ts +114 -114
  38. package/test/payments.utils.ts +165 -165
  39. package/test/psbt.spec.ts +1285 -1285
  40. package/test/script.spec.ts +186 -186
  41. package/test/script_number.spec.ts +26 -26
  42. package/test/script_signature.spec.ts +66 -66
  43. package/test/transaction.spec.ts +337 -337
  44. package/test/ts-node-register.js +7 -7
  45. package/test/types.spec.ts +53 -53
package/src/block.ts CHANGED
@@ -1,233 +1,233 @@
1
- import { BufferReader, BufferWriter, reverseBuffer, varuint } from './bufferutils.js';
2
- import * as bcrypto from './crypto.js';
3
- import { fastMerkleRoot } from './merkle.js';
4
- import { Transaction } from './transaction.js';
5
- import * as types from './types.js';
6
-
7
- const { typeforce } = types;
8
-
9
- const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions');
10
- const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block');
11
-
12
- export class Block {
13
- version: number = 1;
14
- prevHash?: Buffer = undefined;
15
- merkleRoot?: Buffer = undefined;
16
- timestamp: number = 0;
17
- witnessCommit?: Buffer = undefined;
18
- bits: number = 0;
19
- nonce: number = 0;
20
- transactions?: Transaction[] = undefined;
21
-
22
- static fromBuffer(buffer: Buffer): Block {
23
- if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
24
-
25
- const bufferReader = new BufferReader(buffer);
26
-
27
- const block = new Block();
28
- block.version = bufferReader.readInt32();
29
- block.prevHash = bufferReader.readSlice(32);
30
- block.merkleRoot = bufferReader.readSlice(32);
31
- block.timestamp = bufferReader.readUInt32();
32
- block.bits = bufferReader.readUInt32();
33
- block.nonce = bufferReader.readUInt32();
34
-
35
- if (buffer.length === 80) return block;
36
-
37
- const readTransaction = (): any => {
38
- const tx = Transaction.fromBuffer(bufferReader.buffer.slice(bufferReader.offset), true);
39
- bufferReader.offset += tx.byteLength();
40
- return tx;
41
- };
42
-
43
- const nTransactions = bufferReader.readVarInt();
44
- block.transactions = [];
45
-
46
- for (let i = 0; i < nTransactions; ++i) {
47
- const tx = readTransaction();
48
- block.transactions.push(tx);
49
- }
50
-
51
- const witnessCommit = block.getWitnessCommit();
52
- // This Block contains a witness commit
53
- if (witnessCommit) block.witnessCommit = witnessCommit;
54
-
55
- return block;
56
- }
57
-
58
- static fromHex(hex: string): Block {
59
- return Block.fromBuffer(Buffer.from(hex, 'hex'));
60
- }
61
-
62
- static calculateTarget(bits: number): Buffer {
63
- const exponent = ((bits & 0xff000000) >> 24) - 3;
64
- const mantissa = bits & 0x007fffff;
65
- const target = Buffer.alloc(32, 0);
66
- target.writeUIntBE(mantissa, 29 - exponent, 3);
67
- return target;
68
- }
69
-
70
- static calculateMerkleRoot(transactions: Transaction[], forWitness?: boolean): Buffer {
71
- typeforce([{ getHash: types.Function }], transactions);
72
- if (transactions.length === 0) throw errorMerkleNoTxes;
73
- if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit;
74
-
75
- const hashes = transactions.map((transaction) => transaction.getHash(forWitness!));
76
-
77
- const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
78
-
79
- return forWitness
80
- ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]))
81
- : rootHash;
82
- }
83
-
84
- getWitnessCommit(): Buffer | null {
85
- if (!txesHaveWitnessCommit(this.transactions!)) return null;
86
-
87
- // The merkle root for the witness data is in an OP_RETURN output.
88
- // There is no rule for the index of the output, so use filter to find it.
89
- // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
90
- // If multiple commits are found, the output with highest index is assumed.
91
- const witnessCommits = this.transactions![0].outs.filter((out) =>
92
- out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
93
- ).map((out) => out.script.slice(6, 38));
94
- if (witnessCommits.length === 0) return null;
95
- // Use the commit with the highest output (should only be one though)
96
- const result = witnessCommits[witnessCommits.length - 1];
97
-
98
- if (!(result instanceof Buffer && result.length === 32)) return null;
99
- return result;
100
- }
101
-
102
- hasWitnessCommit(): boolean {
103
- if (this.witnessCommit instanceof Buffer && this.witnessCommit.length === 32) return true;
104
- if (this.getWitnessCommit() !== null) return true;
105
- return false;
106
- }
107
-
108
- hasWitness(): boolean {
109
- return anyTxHasWitness(this.transactions!);
110
- }
111
-
112
- weight(): number {
113
- const base = this.byteLength(false, false);
114
- const total = this.byteLength(false, true);
115
- return base * 3 + total;
116
- }
117
-
118
- byteLength(headersOnly?: boolean, allowWitness: boolean = true): number {
119
- if (headersOnly || !this.transactions) return 80;
120
-
121
- return (
122
- 80 +
123
- varuint.encodingLength(this.transactions.length) +
124
- this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0)
125
- );
126
- }
127
-
128
- getHash(): Buffer {
129
- return bcrypto.hash256(this.toBuffer(true));
130
- }
131
-
132
- getId(): string {
133
- return reverseBuffer(this.getHash()).toString('hex');
134
- }
135
-
136
- getUTCDate(): Date {
137
- const date = new Date(0); // epoch
138
- date.setUTCSeconds(this.timestamp);
139
-
140
- return date;
141
- }
142
-
143
- // TODO: buffer, offset compatibility
144
- toBuffer(headersOnly?: boolean): Buffer {
145
- const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
146
-
147
- const bufferWriter = new BufferWriter(buffer);
148
-
149
- bufferWriter.writeInt32(this.version);
150
- bufferWriter.writeSlice(this.prevHash!);
151
- bufferWriter.writeSlice(this.merkleRoot!);
152
- bufferWriter.writeUInt32(this.timestamp);
153
- bufferWriter.writeUInt32(this.bits);
154
- bufferWriter.writeUInt32(this.nonce);
155
-
156
- if (headersOnly || !this.transactions) return buffer;
157
-
158
- const encoded = varuint.encode(this.transactions.length, buffer, bufferWriter.offset);
159
- bufferWriter.offset += encoded.bytes;
160
-
161
- this.transactions.forEach((tx) => {
162
- const txSize = tx.byteLength(); // TODO: extract from toBuffer?
163
- tx.toBuffer(buffer, bufferWriter.offset);
164
- bufferWriter.offset += txSize;
165
- });
166
-
167
- return buffer;
168
- }
169
-
170
- toHex(headersOnly?: boolean): string {
171
- return this.toBuffer(headersOnly).toString('hex');
172
- }
173
-
174
- checkTxRoots(): boolean {
175
- // If the Block has segwit transactions but no witness commit,
176
- // there's no way it can be valid, so fail the check.
177
- const hasWitnessCommit = this.hasWitnessCommit();
178
- if (!hasWitnessCommit && this.hasWitness()) return false;
179
- return this.__checkMerkleRoot() && (hasWitnessCommit ? this.__checkWitnessCommit() : true);
180
- }
181
-
182
- checkProofOfWork(): boolean {
183
- const hash: Buffer = reverseBuffer(this.getHash());
184
- const target = Block.calculateTarget(this.bits);
185
-
186
- return hash.compare(target) <= 0;
187
- }
188
-
189
- private __checkMerkleRoot(): boolean {
190
- if (!this.transactions) throw errorMerkleNoTxes;
191
-
192
- const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
193
- return this.merkleRoot!.compare(actualMerkleRoot) === 0;
194
- }
195
-
196
- private __checkWitnessCommit(): boolean {
197
- if (!this.transactions) throw errorMerkleNoTxes;
198
- if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit;
199
-
200
- const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true);
201
- return this.witnessCommit!.compare(actualWitnessCommit) === 0;
202
- }
203
- }
204
-
205
- function txesHaveWitnessCommit(transactions: Transaction[]): boolean {
206
- return (
207
- transactions instanceof Array &&
208
- transactions[0] &&
209
- transactions[0].ins &&
210
- transactions[0].ins instanceof Array &&
211
- transactions[0].ins[0] &&
212
- transactions[0].ins[0].witness &&
213
- transactions[0].ins[0].witness instanceof Array &&
214
- transactions[0].ins[0].witness.length > 0
215
- );
216
- }
217
-
218
- function anyTxHasWitness(transactions: Transaction[]): boolean {
219
- return (
220
- transactions instanceof Array &&
221
- transactions.some(
222
- (tx) =>
223
- typeof tx === 'object' &&
224
- tx.ins instanceof Array &&
225
- tx.ins.some(
226
- (input) =>
227
- typeof input === 'object' &&
228
- input.witness instanceof Array &&
229
- input.witness.length > 0,
230
- ),
231
- )
232
- );
233
- }
1
+ import { BufferReader, BufferWriter, reverseBuffer, varuint } from './bufferutils.js';
2
+ import * as bcrypto from './crypto.js';
3
+ import { fastMerkleRoot } from './merkle.js';
4
+ import { Transaction } from './transaction.js';
5
+ import * as types from './types.js';
6
+
7
+ const { typeforce } = types;
8
+
9
+ const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions');
10
+ const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block');
11
+
12
+ export class Block {
13
+ version: number = 1;
14
+ prevHash?: Buffer = undefined;
15
+ merkleRoot?: Buffer = undefined;
16
+ timestamp: number = 0;
17
+ witnessCommit?: Buffer = undefined;
18
+ bits: number = 0;
19
+ nonce: number = 0;
20
+ transactions?: Transaction[] = undefined;
21
+
22
+ static fromBuffer(buffer: Buffer): Block {
23
+ if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
24
+
25
+ const bufferReader = new BufferReader(buffer);
26
+
27
+ const block = new Block();
28
+ block.version = bufferReader.readInt32();
29
+ block.prevHash = bufferReader.readSlice(32);
30
+ block.merkleRoot = bufferReader.readSlice(32);
31
+ block.timestamp = bufferReader.readUInt32();
32
+ block.bits = bufferReader.readUInt32();
33
+ block.nonce = bufferReader.readUInt32();
34
+
35
+ if (buffer.length === 80) return block;
36
+
37
+ const readTransaction = (): any => {
38
+ const tx = Transaction.fromBuffer(bufferReader.buffer.slice(bufferReader.offset), true);
39
+ bufferReader.offset += tx.byteLength();
40
+ return tx;
41
+ };
42
+
43
+ const nTransactions = bufferReader.readVarInt();
44
+ block.transactions = [];
45
+
46
+ for (let i = 0; i < nTransactions; ++i) {
47
+ const tx = readTransaction();
48
+ block.transactions.push(tx);
49
+ }
50
+
51
+ const witnessCommit = block.getWitnessCommit();
52
+ // This Block contains a witness commit
53
+ if (witnessCommit) block.witnessCommit = witnessCommit;
54
+
55
+ return block;
56
+ }
57
+
58
+ static fromHex(hex: string): Block {
59
+ return Block.fromBuffer(Buffer.from(hex, 'hex'));
60
+ }
61
+
62
+ static calculateTarget(bits: number): Buffer {
63
+ const exponent = ((bits & 0xff000000) >> 24) - 3;
64
+ const mantissa = bits & 0x007fffff;
65
+ const target = Buffer.alloc(32, 0);
66
+ target.writeUIntBE(mantissa, 29 - exponent, 3);
67
+ return target;
68
+ }
69
+
70
+ static calculateMerkleRoot(transactions: Transaction[], forWitness?: boolean): Buffer {
71
+ typeforce([{ getHash: types.Function }], transactions);
72
+ if (transactions.length === 0) throw errorMerkleNoTxes;
73
+ if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit;
74
+
75
+ const hashes = transactions.map((transaction) => transaction.getHash(forWitness!));
76
+
77
+ const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
78
+
79
+ return forWitness
80
+ ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]))
81
+ : rootHash;
82
+ }
83
+
84
+ getWitnessCommit(): Buffer | null {
85
+ if (!txesHaveWitnessCommit(this.transactions!)) return null;
86
+
87
+ // The merkle root for the witness data is in an OP_RETURN output.
88
+ // There is no rule for the index of the output, so use filter to find it.
89
+ // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
90
+ // If multiple commits are found, the output with highest index is assumed.
91
+ const witnessCommits = this.transactions![0].outs.filter((out) =>
92
+ out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
93
+ ).map((out) => out.script.slice(6, 38));
94
+ if (witnessCommits.length === 0) return null;
95
+ // Use the commit with the highest output (should only be one though)
96
+ const result = witnessCommits[witnessCommits.length - 1];
97
+
98
+ if (!(result instanceof Buffer && result.length === 32)) return null;
99
+ return result;
100
+ }
101
+
102
+ hasWitnessCommit(): boolean {
103
+ if (this.witnessCommit instanceof Buffer && this.witnessCommit.length === 32) return true;
104
+ if (this.getWitnessCommit() !== null) return true;
105
+ return false;
106
+ }
107
+
108
+ hasWitness(): boolean {
109
+ return anyTxHasWitness(this.transactions!);
110
+ }
111
+
112
+ weight(): number {
113
+ const base = this.byteLength(false, false);
114
+ const total = this.byteLength(false, true);
115
+ return base * 3 + total;
116
+ }
117
+
118
+ byteLength(headersOnly?: boolean, allowWitness: boolean = true): number {
119
+ if (headersOnly || !this.transactions) return 80;
120
+
121
+ return (
122
+ 80 +
123
+ varuint.encodingLength(this.transactions.length) +
124
+ this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0)
125
+ );
126
+ }
127
+
128
+ getHash(): Buffer {
129
+ return bcrypto.hash256(this.toBuffer(true));
130
+ }
131
+
132
+ getId(): string {
133
+ return reverseBuffer(this.getHash()).toString('hex');
134
+ }
135
+
136
+ getUTCDate(): Date {
137
+ const date = new Date(0); // epoch
138
+ date.setUTCSeconds(this.timestamp);
139
+
140
+ return date;
141
+ }
142
+
143
+ // TODO: buffer, offset compatibility
144
+ toBuffer(headersOnly?: boolean): Buffer {
145
+ const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
146
+
147
+ const bufferWriter = new BufferWriter(buffer);
148
+
149
+ bufferWriter.writeInt32(this.version);
150
+ bufferWriter.writeSlice(this.prevHash!);
151
+ bufferWriter.writeSlice(this.merkleRoot!);
152
+ bufferWriter.writeUInt32(this.timestamp);
153
+ bufferWriter.writeUInt32(this.bits);
154
+ bufferWriter.writeUInt32(this.nonce);
155
+
156
+ if (headersOnly || !this.transactions) return buffer;
157
+
158
+ const encoded = varuint.encode(this.transactions.length, buffer, bufferWriter.offset);
159
+ bufferWriter.offset += encoded.bytes;
160
+
161
+ this.transactions.forEach((tx) => {
162
+ const txSize = tx.byteLength(); // TODO: extract from toBuffer?
163
+ tx.toBuffer(buffer, bufferWriter.offset);
164
+ bufferWriter.offset += txSize;
165
+ });
166
+
167
+ return buffer;
168
+ }
169
+
170
+ toHex(headersOnly?: boolean): string {
171
+ return this.toBuffer(headersOnly).toString('hex');
172
+ }
173
+
174
+ checkTxRoots(): boolean {
175
+ // If the Block has segwit transactions but no witness commit,
176
+ // there's no way it can be valid, so fail the check.
177
+ const hasWitnessCommit = this.hasWitnessCommit();
178
+ if (!hasWitnessCommit && this.hasWitness()) return false;
179
+ return this.__checkMerkleRoot() && (hasWitnessCommit ? this.__checkWitnessCommit() : true);
180
+ }
181
+
182
+ checkProofOfWork(): boolean {
183
+ const hash: Buffer = reverseBuffer(this.getHash());
184
+ const target = Block.calculateTarget(this.bits);
185
+
186
+ return hash.compare(target) <= 0;
187
+ }
188
+
189
+ private __checkMerkleRoot(): boolean {
190
+ if (!this.transactions) throw errorMerkleNoTxes;
191
+
192
+ const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
193
+ return this.merkleRoot!.compare(actualMerkleRoot) === 0;
194
+ }
195
+
196
+ private __checkWitnessCommit(): boolean {
197
+ if (!this.transactions) throw errorMerkleNoTxes;
198
+ if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit;
199
+
200
+ const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true);
201
+ return this.witnessCommit!.compare(actualWitnessCommit) === 0;
202
+ }
203
+ }
204
+
205
+ function txesHaveWitnessCommit(transactions: Transaction[]): boolean {
206
+ return (
207
+ transactions instanceof Array &&
208
+ transactions[0] &&
209
+ transactions[0].ins &&
210
+ transactions[0].ins instanceof Array &&
211
+ transactions[0].ins[0] &&
212
+ transactions[0].ins[0].witness &&
213
+ transactions[0].ins[0].witness instanceof Array &&
214
+ transactions[0].ins[0].witness.length > 0
215
+ );
216
+ }
217
+
218
+ function anyTxHasWitness(transactions: Transaction[]): boolean {
219
+ return (
220
+ transactions instanceof Array &&
221
+ transactions.some(
222
+ (tx) =>
223
+ typeof tx === 'object' &&
224
+ tx.ins instanceof Array &&
225
+ tx.ins.some(
226
+ (input) =>
227
+ typeof input === 'object' &&
228
+ input.witness instanceof Array &&
229
+ input.witness.length > 0,
230
+ ),
231
+ )
232
+ );
233
+ }