@bsv/sdk 1.0.32 → 1.0.34
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.
- package/README.md +1 -3
- package/dist/cjs/mod.js +1 -0
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/PublicKey.js +36 -0
- package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
- package/dist/cjs/src/primitives/Signature.js +6 -6
- package/dist/cjs/src/primitives/Signature.js.map +1 -1
- package/dist/cjs/src/script/Script.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +5 -3
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/ARC.js +39 -57
- package/dist/cjs/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/DefaultBroadcaster.js +12 -0
- package/dist/cjs/src/transaction/broadcasters/DefaultBroadcaster.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/WhatsOnChainBroadcaster.js +66 -0
- package/dist/cjs/src/transaction/broadcasters/WhatsOnChainBroadcaster.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/index.js +5 -1
- package/dist/cjs/src/transaction/broadcasters/index.js.map +1 -1
- package/dist/cjs/src/transaction/chaintrackers/DefaultChainTracker.js +12 -0
- package/dist/cjs/src/transaction/chaintrackers/DefaultChainTracker.js.map +1 -0
- package/dist/cjs/src/transaction/chaintrackers/WhatsOnChain.js +49 -0
- package/dist/cjs/src/transaction/chaintrackers/WhatsOnChain.js.map +1 -0
- package/dist/cjs/src/transaction/chaintrackers/index.js +11 -0
- package/dist/cjs/src/transaction/chaintrackers/index.js.map +1 -0
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js +37 -0
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/FetchHttpClient.js +29 -0
- package/dist/cjs/src/transaction/http/FetchHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/HttpClient.js +3 -0
- package/dist/cjs/src/transaction/http/HttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/NodejsHttpClient.js +41 -0
- package/dist/cjs/src/transaction/http/NodejsHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/index.js +10 -0
- package/dist/cjs/src/transaction/http/index.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +1 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/primitives/PublicKey.js +36 -0
- package/dist/esm/src/primitives/PublicKey.js.map +1 -1
- package/dist/esm/src/primitives/Signature.js +6 -6
- package/dist/esm/src/primitives/Signature.js.map +1 -1
- package/dist/esm/src/script/Script.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +5 -3
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/ARC.js +37 -57
- package/dist/esm/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/DefaultBroadcaster.js +5 -0
- package/dist/esm/src/transaction/broadcasters/DefaultBroadcaster.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/WhatsOnChainBroadcaster.js +65 -0
- package/dist/esm/src/transaction/broadcasters/WhatsOnChainBroadcaster.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/index.js +2 -0
- package/dist/esm/src/transaction/broadcasters/index.js.map +1 -1
- package/dist/esm/src/transaction/chaintrackers/DefaultChainTracker.js +5 -0
- package/dist/esm/src/transaction/chaintrackers/DefaultChainTracker.js.map +1 -0
- package/dist/esm/src/transaction/chaintrackers/WhatsOnChain.js +50 -0
- package/dist/esm/src/transaction/chaintrackers/WhatsOnChain.js.map +1 -0
- package/dist/esm/src/transaction/chaintrackers/index.js +3 -0
- package/dist/esm/src/transaction/chaintrackers/index.js.map +1 -0
- package/dist/esm/src/transaction/http/DefaultHttpClient.js +33 -0
- package/dist/esm/src/transaction/http/DefaultHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/FetchHttpClient.js +26 -0
- package/dist/esm/src/transaction/http/FetchHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/HttpClient.js +2 -0
- package/dist/esm/src/transaction/http/HttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/NodejsHttpClient.js +38 -0
- package/dist/esm/src/transaction/http/NodejsHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/index.js +4 -0
- package/dist/esm/src/transaction/http/index.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +1 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/primitives/PublicKey.d.ts +18 -0
- package/dist/types/src/primitives/PublicKey.d.ts.map +1 -1
- package/dist/types/src/primitives/Signature.d.ts +3 -3
- package/dist/types/src/script/Script.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +7 -7
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/ARC.d.ts +23 -7
- package/dist/types/src/transaction/broadcasters/ARC.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/DefaultBroadcaster.d.ts +3 -0
- package/dist/types/src/transaction/broadcasters/DefaultBroadcaster.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/WhatsOnChainBroadcaster.d.ts +26 -0
- package/dist/types/src/transaction/broadcasters/WhatsOnChainBroadcaster.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/index.d.ts +3 -0
- package/dist/types/src/transaction/broadcasters/index.d.ts.map +1 -1
- package/dist/types/src/transaction/chaintrackers/DefaultChainTracker.d.ts +3 -0
- package/dist/types/src/transaction/chaintrackers/DefaultChainTracker.d.ts.map +1 -0
- package/dist/types/src/transaction/chaintrackers/WhatsOnChain.d.ts +28 -0
- package/dist/types/src/transaction/chaintrackers/WhatsOnChain.d.ts.map +1 -0
- package/dist/types/src/transaction/chaintrackers/index.d.ts +4 -0
- package/dist/types/src/transaction/chaintrackers/index.d.ts.map +1 -0
- package/dist/types/src/transaction/http/DefaultHttpClient.d.ts +8 -0
- package/dist/types/src/transaction/http/DefaultHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/FetchHttpClient.d.ts +31 -0
- package/dist/types/src/transaction/http/FetchHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/HttpClient.d.ts +43 -0
- package/dist/types/src/transaction/http/HttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/NodejsHttpClient.d.ts +21 -0
- package/dist/types/src/transaction/http/NodejsHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/index.d.ts +7 -0
- package/dist/types/src/transaction/http/index.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/examples/EXAMPLE_BUILDING_CUSTOM_TX_BROADCASTER.md +2 -0
- package/docs/examples/EXAMPLE_COMPLEX_TX.md +2 -4
- package/docs/examples/EXAMPLE_SIMPLE_TX.md +83 -2
- package/docs/examples/EXAMPLE_VERIFYING_BEEF.md +29 -22
- package/docs/examples/EXAMPLE_VERIFYING_ROOTS.md +18 -63
- package/docs/examples/GETTING_STARTED_NODE_CJS.md +2 -4
- package/docs/examples/GETTING_STARTED_REACT.md +2 -4
- package/docs/primitives.md +36 -3
- package/docs/transaction.md +48 -0
- package/mod.ts +2 -1
- package/package.json +21 -1
- package/src/primitives/PublicKey.ts +39 -0
- package/src/primitives/Signature.ts +6 -6
- package/src/script/Script.ts +19 -19
- package/src/transaction/MerklePath.ts +9 -9
- package/src/transaction/Transaction.ts +13 -13
- package/src/transaction/__tests/Transaction.test.ts +62 -0
- package/src/transaction/broadcasters/ARC.ts +71 -57
- package/src/transaction/broadcasters/DefaultBroadcaster.ts +6 -0
- package/src/transaction/broadcasters/WhatsOnChainBroadcaster.ts +70 -0
- package/src/transaction/broadcasters/__tests/ARC.test.ts +152 -41
- package/src/transaction/broadcasters/__tests/WhatsOnChainBroadcaster.test.ts +165 -0
- package/src/transaction/broadcasters/index.ts +3 -0
- package/src/transaction/chaintrackers/DefaultChainTracker.ts +6 -0
- package/src/transaction/chaintrackers/WhatsOnChain.ts +70 -0
- package/src/transaction/chaintrackers/__tests/WhatsOnChainChainTracker.test.ts +135 -0
- package/src/transaction/chaintrackers/index.ts +3 -0
- package/src/transaction/http/DefaultHttpClient.ts +32 -0
- package/src/transaction/http/FetchHttpClient.ts +50 -0
- package/src/transaction/http/HttpClient.ts +45 -0
- package/src/transaction/http/NodejsHttpClient.ts +55 -0
- package/src/transaction/http/index.ts +6 -0
package/src/script/Script.ts
CHANGED
|
@@ -21,7 +21,7 @@ export default class Script {
|
|
|
21
21
|
* @example
|
|
22
22
|
* const script = Script.fromASM("OP_DUP OP_HASH160 abcd... OP_EQUALVERIFY OP_CHECKSIG")
|
|
23
23
|
*/
|
|
24
|
-
static fromASM(asm: string): Script {
|
|
24
|
+
static fromASM (asm: string): Script {
|
|
25
25
|
const chunks: ScriptChunk[] = []
|
|
26
26
|
const tokens = asm.split(' ')
|
|
27
27
|
let i = 0
|
|
@@ -100,7 +100,7 @@ export default class Script {
|
|
|
100
100
|
* @example
|
|
101
101
|
* const script = Script.fromHex("76a9...");
|
|
102
102
|
*/
|
|
103
|
-
static fromHex(hex: string): Script {
|
|
103
|
+
static fromHex (hex: string): Script {
|
|
104
104
|
if (hex.length === 0) return Script.fromBinary([])
|
|
105
105
|
if (hex.length % 2 !== 0) throw new Error('There is an uneven number of characters in the string which suggests it is not hex encoded.')
|
|
106
106
|
if (!/^[0-9a-fA-F]+$/.test(hex)) throw new Error('Some elements in this string are not hex encoded.')
|
|
@@ -115,7 +115,7 @@ export default class Script {
|
|
|
115
115
|
* @example
|
|
116
116
|
* const script = Script.fromBinary([0x76, 0xa9, ...])
|
|
117
117
|
*/
|
|
118
|
-
static fromBinary(bin: number[]): Script {
|
|
118
|
+
static fromBinary (bin: number[]): Script {
|
|
119
119
|
bin = [...bin]
|
|
120
120
|
const chunks: ScriptChunk[] = []
|
|
121
121
|
|
|
@@ -179,7 +179,7 @@ export default class Script {
|
|
|
179
179
|
* Constructs a new Script object.
|
|
180
180
|
* @param chunks=[] - An array of script chunks to directly initialize the script.
|
|
181
181
|
*/
|
|
182
|
-
constructor(chunks: ScriptChunk[] = []) {
|
|
182
|
+
constructor (chunks: ScriptChunk[] = []) {
|
|
183
183
|
this.chunks = chunks
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -188,7 +188,7 @@ export default class Script {
|
|
|
188
188
|
* Serializes the script to an ASM formatted string.
|
|
189
189
|
* @returns The script in ASM string format.
|
|
190
190
|
*/
|
|
191
|
-
toASM(): string {
|
|
191
|
+
toASM (): string {
|
|
192
192
|
let str = ''
|
|
193
193
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
194
194
|
const chunk = this.chunks[i]
|
|
@@ -203,7 +203,7 @@ export default class Script {
|
|
|
203
203
|
* Serializes the script to a hexadecimal string.
|
|
204
204
|
* @returns The script in hexadecimal format.
|
|
205
205
|
*/
|
|
206
|
-
toHex(): string {
|
|
206
|
+
toHex (): string {
|
|
207
207
|
return encode(this.toBinary(), 'hex') as string
|
|
208
208
|
}
|
|
209
209
|
|
|
@@ -212,7 +212,7 @@ export default class Script {
|
|
|
212
212
|
* Serializes the script to a binary array.
|
|
213
213
|
* @returns The script in binary array format.
|
|
214
214
|
*/
|
|
215
|
-
toBinary(): number[] {
|
|
215
|
+
toBinary (): number[] {
|
|
216
216
|
const writer = new Writer()
|
|
217
217
|
|
|
218
218
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
@@ -244,7 +244,7 @@ export default class Script {
|
|
|
244
244
|
* @param script - The script to append.
|
|
245
245
|
* @returns This script instance for chaining.
|
|
246
246
|
*/
|
|
247
|
-
writeScript(script: Script): Script {
|
|
247
|
+
writeScript (script: Script): Script {
|
|
248
248
|
this.chunks = this.chunks.concat(script.chunks)
|
|
249
249
|
return this
|
|
250
250
|
}
|
|
@@ -255,7 +255,7 @@ export default class Script {
|
|
|
255
255
|
* @param op - The opcode to append.
|
|
256
256
|
* @returns This script instance for chaining.
|
|
257
257
|
*/
|
|
258
|
-
writeOpCode(op: number): Script {
|
|
258
|
+
writeOpCode (op: number): Script {
|
|
259
259
|
this.chunks.push({ op })
|
|
260
260
|
return this
|
|
261
261
|
}
|
|
@@ -267,7 +267,7 @@ export default class Script {
|
|
|
267
267
|
* @param op - The opcode to set.
|
|
268
268
|
* @returns This script instance for chaining.
|
|
269
269
|
*/
|
|
270
|
-
setChunkOpCode(i: number, op: number): Script {
|
|
270
|
+
setChunkOpCode (i: number, op: number): Script {
|
|
271
271
|
this.chunks[i] = { op }
|
|
272
272
|
return this
|
|
273
273
|
}
|
|
@@ -278,7 +278,7 @@ export default class Script {
|
|
|
278
278
|
* @param bn - The BigNumber to append.
|
|
279
279
|
* @returns This script instance for chaining.
|
|
280
280
|
*/
|
|
281
|
-
writeBn(bn: BigNumber): Script {
|
|
281
|
+
writeBn (bn: BigNumber): Script {
|
|
282
282
|
if (bn.cmpn(0) === OP.OP_0) {
|
|
283
283
|
this.chunks.push({
|
|
284
284
|
op: OP.OP_0
|
|
@@ -306,7 +306,7 @@ export default class Script {
|
|
|
306
306
|
* @returns This script instance for chaining.
|
|
307
307
|
* @throws {Error} Throws an error if the data is too large to be pushed.
|
|
308
308
|
*/
|
|
309
|
-
writeBin(bin: number[]): Script {
|
|
309
|
+
writeBin (bin: number[]): Script {
|
|
310
310
|
let op
|
|
311
311
|
if (bin.length > 0 && bin.length < OP.OP_PUSHDATA1) {
|
|
312
312
|
op = bin.length
|
|
@@ -334,7 +334,7 @@ export default class Script {
|
|
|
334
334
|
* @param num - The number to append.
|
|
335
335
|
* @returns This script instance for chaining.
|
|
336
336
|
*/
|
|
337
|
-
writeNumber(num: number): Script {
|
|
337
|
+
writeNumber (num: number): Script {
|
|
338
338
|
this.writeBn(new BigNumber(num))
|
|
339
339
|
return this
|
|
340
340
|
}
|
|
@@ -344,7 +344,7 @@ export default class Script {
|
|
|
344
344
|
* Removes all OP_CODESEPARATOR opcodes from the script.
|
|
345
345
|
* @returns This script instance for chaining.
|
|
346
346
|
*/
|
|
347
|
-
removeCodeseparators(): Script {
|
|
347
|
+
removeCodeseparators (): Script {
|
|
348
348
|
const chunks = []
|
|
349
349
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
350
350
|
if (this.chunks[i].op !== OP.OP_CODESEPARATOR) {
|
|
@@ -362,7 +362,7 @@ export default class Script {
|
|
|
362
362
|
*
|
|
363
363
|
* @returns This script instance for chaining.
|
|
364
364
|
*/
|
|
365
|
-
findAndDelete(script: Script): Script {
|
|
365
|
+
findAndDelete (script: Script): Script {
|
|
366
366
|
const buf = script.toHex()
|
|
367
367
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
368
368
|
const script2 = new Script([this.chunks[i]])
|
|
@@ -379,7 +379,7 @@ export default class Script {
|
|
|
379
379
|
* Checks if the script contains only push data operations.
|
|
380
380
|
* @returns True if the script is push-only, otherwise false.
|
|
381
381
|
*/
|
|
382
|
-
isPushOnly(): boolean {
|
|
382
|
+
isPushOnly (): boolean {
|
|
383
383
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
384
384
|
const chunk = this.chunks[i]
|
|
385
385
|
const opCodeNum = chunk.op
|
|
@@ -395,7 +395,7 @@ export default class Script {
|
|
|
395
395
|
* Determines if the script is a locking script.
|
|
396
396
|
* @returns True if the script is a locking script, otherwise false.
|
|
397
397
|
*/
|
|
398
|
-
isLockingScript(): boolean {
|
|
398
|
+
isLockingScript (): boolean {
|
|
399
399
|
throw new Error('Not implemented')
|
|
400
400
|
}
|
|
401
401
|
|
|
@@ -404,7 +404,7 @@ export default class Script {
|
|
|
404
404
|
* Determines if the script is an unlocking script.
|
|
405
405
|
* @returns True if the script is an unlocking script, otherwise false.
|
|
406
406
|
*/
|
|
407
|
-
isUnlockingScript(): boolean {
|
|
407
|
+
isUnlockingScript (): boolean {
|
|
408
408
|
throw new Error('Not implemented')
|
|
409
409
|
}
|
|
410
410
|
|
|
@@ -415,7 +415,7 @@ export default class Script {
|
|
|
415
415
|
* @param chunk - The script chunk.
|
|
416
416
|
* @returns The string representation of the chunk.
|
|
417
417
|
*/
|
|
418
|
-
private _chunkToString(chunk: ScriptChunk): string {
|
|
418
|
+
private _chunkToString (chunk: ScriptChunk): string {
|
|
419
419
|
const op = chunk.op
|
|
420
420
|
let str = ''
|
|
421
421
|
if (typeof chunk.data === 'undefined') {
|
|
@@ -39,11 +39,11 @@ export default class MerklePath {
|
|
|
39
39
|
* @param {string} hex - The hexadecimal string representation of the Merkle Path.
|
|
40
40
|
* @returns {MerklePath} - A new MerklePath instance.
|
|
41
41
|
*/
|
|
42
|
-
static fromHex(hex: string): MerklePath {
|
|
42
|
+
static fromHex (hex: string): MerklePath {
|
|
43
43
|
return MerklePath.fromBinary(toArray(hex, 'hex'))
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
static fromReader(reader: Reader): MerklePath {
|
|
46
|
+
static fromReader (reader: Reader): MerklePath {
|
|
47
47
|
const blockHeight = reader.readVarIntNum()
|
|
48
48
|
const treeHeight = reader.readUInt8()
|
|
49
49
|
const path = Array(treeHeight).fill(0).map(() => ([]))
|
|
@@ -82,12 +82,12 @@ export default class MerklePath {
|
|
|
82
82
|
* @param {number[]} bump - The binary array representation of the Merkle Path.
|
|
83
83
|
* @returns {MerklePath} - A new MerklePath instance.
|
|
84
84
|
*/
|
|
85
|
-
static fromBinary(bump: number[]): MerklePath {
|
|
85
|
+
static fromBinary (bump: number[]): MerklePath {
|
|
86
86
|
const reader = new Reader(bump)
|
|
87
87
|
return MerklePath.fromReader(reader)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
constructor(blockHeight: number, path: Array<Array<{
|
|
90
|
+
constructor (blockHeight: number, path: Array<Array<{
|
|
91
91
|
offset: number
|
|
92
92
|
hash?: string
|
|
93
93
|
txid?: boolean
|
|
@@ -135,7 +135,7 @@ export default class MerklePath {
|
|
|
135
135
|
*
|
|
136
136
|
* @returns {number[]} - The binary array representation of the Merkle Path.
|
|
137
137
|
*/
|
|
138
|
-
toBinary(): number[] {
|
|
138
|
+
toBinary (): number[] {
|
|
139
139
|
const writer = new Writer()
|
|
140
140
|
writer.writeVarIntNum(this.blockHeight)
|
|
141
141
|
const treeHeight = this.path.length
|
|
@@ -166,7 +166,7 @@ export default class MerklePath {
|
|
|
166
166
|
*
|
|
167
167
|
* @returns {string} - The hexadecimal string representation of the Merkle Path.
|
|
168
168
|
*/
|
|
169
|
-
toHex(): string {
|
|
169
|
+
toHex (): string {
|
|
170
170
|
return toHex(this.toBinary())
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -177,7 +177,7 @@ export default class MerklePath {
|
|
|
177
177
|
* @returns {string} - The computed Merkle root as a hexadecimal string.
|
|
178
178
|
* @throws {Error} - If the transaction ID is not part of the Merkle Path.
|
|
179
179
|
*/
|
|
180
|
-
computeRoot(txid?: string): string {
|
|
180
|
+
computeRoot (txid?: string): string {
|
|
181
181
|
if (typeof txid !== 'string') {
|
|
182
182
|
txid = this.path[0].find(leaf => Boolean(leaf?.hash)).hash
|
|
183
183
|
}
|
|
@@ -216,7 +216,7 @@ export default class MerklePath {
|
|
|
216
216
|
* @param {ChainTracker} chainTracker - The ChainTracker instance used to verify the Merkle root.
|
|
217
217
|
* @returns {boolean} - True if the transaction ID is valid within the Merkle Path at the specified block height.
|
|
218
218
|
*/
|
|
219
|
-
async verify(txid: string, chainTracker: ChainTracker): Promise<boolean> {
|
|
219
|
+
async verify (txid: string, chainTracker: ChainTracker): Promise<boolean> {
|
|
220
220
|
const root = this.computeRoot(txid)
|
|
221
221
|
// Use the chain tracker to determine whether this is a valid merkle root at the given block height
|
|
222
222
|
return await chainTracker.isValidRootForHeight(root, this.blockHeight)
|
|
@@ -228,7 +228,7 @@ export default class MerklePath {
|
|
|
228
228
|
* @param {MerklePath} other - Another MerklePath to combine with this path.
|
|
229
229
|
* @throws {Error} - If the paths have different block heights or roots.
|
|
230
230
|
*/
|
|
231
|
-
combine(other: MerklePath): void {
|
|
231
|
+
combine (other: MerklePath): void {
|
|
232
232
|
if (this.blockHeight !== other.blockHeight) {
|
|
233
233
|
throw new Error('You cannot combine paths which do not have the same block height.')
|
|
234
234
|
}
|
|
@@ -10,6 +10,8 @@ import { Broadcaster, BroadcastResponse, BroadcastFailure } from './Broadcaster.
|
|
|
10
10
|
import MerklePath from './MerklePath.js'
|
|
11
11
|
import Spend from '../script/Spend.js'
|
|
12
12
|
import ChainTracker from './ChainTracker.js'
|
|
13
|
+
import {defaultBroadcaster} from "./broadcasters/DefaultBroadcaster.js";
|
|
14
|
+
import {defaultChainTracker} from "./chaintrackers/DefaultChainTracker.js";
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Represents a complete Bitcoin transaction. This class encapsulates all the details
|
|
@@ -125,25 +127,23 @@ export default class Transaction {
|
|
|
125
127
|
* any application seeking to validate data in output scripts must store the entire transaction as well.
|
|
126
128
|
* Since the transaction data includes the output script data, saving a second copy of potentially
|
|
127
129
|
* large scripts can bloat application storage requirements.
|
|
128
|
-
*
|
|
130
|
+
*
|
|
129
131
|
* This function efficiently parses binary transaction data to determine the offsets and lengths of each script.
|
|
130
132
|
* This supports the efficient retreival of script data from transaction data.
|
|
131
|
-
*
|
|
133
|
+
*
|
|
132
134
|
* @param bin binary transaction data
|
|
133
135
|
* @returns {
|
|
134
136
|
* inputs: { vin: number, offset: number, length: number }[]
|
|
135
137
|
* outputs: { vout: number, offset: number, length: number }[]
|
|
136
138
|
* }
|
|
137
139
|
*/
|
|
138
|
-
static parseScriptOffsets(bin: number[])
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
{
|
|
140
|
+
static parseScriptOffsets (bin: number[]): {
|
|
141
|
+
inputs: Array<{ vin: number, offset: number, length: number }>
|
|
142
|
+
outputs: Array<{ vout: number, offset: number, length: number }>
|
|
143
|
+
} {
|
|
144
144
|
const br = new Reader(bin)
|
|
145
|
-
const inputs: { vin: number, offset: number, length: number }
|
|
146
|
-
const outputs: { vout: number, offset: number, length: number }
|
|
145
|
+
const inputs: Array<{ vin: number, offset: number, length: number }> = []
|
|
146
|
+
const outputs: Array<{ vout: number, offset: number, length: number }> = []
|
|
147
147
|
|
|
148
148
|
br.pos += 4 // version
|
|
149
149
|
const inputsLength = br.readVarIntNum()
|
|
@@ -384,7 +384,7 @@ export default class Transaction {
|
|
|
384
384
|
* @param broadcaster The Broadcaster instance wwhere the transaction will be sent
|
|
385
385
|
* @returns A BroadcastResponse or BroadcastFailure from the Broadcaster
|
|
386
386
|
*/
|
|
387
|
-
async broadcast (broadcaster: Broadcaster): Promise<BroadcastResponse | BroadcastFailure> {
|
|
387
|
+
async broadcast (broadcaster: Broadcaster = defaultBroadcaster()): Promise<BroadcastResponse | BroadcastFailure> {
|
|
388
388
|
return await broadcaster.broadcast(this)
|
|
389
389
|
}
|
|
390
390
|
|
|
@@ -535,11 +535,11 @@ export default class Transaction {
|
|
|
535
535
|
/**
|
|
536
536
|
* Verifies the legitimacy of the Bitcoin transaction according to the rules of SPV by ensuring all the input transactions link back to valid block headers, the chain of spends for all inputs are valid, and the sum of inputs is not less than the sum of outputs.
|
|
537
537
|
*
|
|
538
|
-
* @param chainTracker - An instance of ChainTracker, a Bitcoin block header tracker. If the value is set to 'scripts only', headers will not be verified.
|
|
538
|
+
* @param chainTracker - An instance of ChainTracker, a Bitcoin block header tracker. If the value is set to 'scripts only', headers will not be verified. If not provided then the default chain tracker will be used.
|
|
539
539
|
*
|
|
540
540
|
* @returns Whether the transaction is valid according to the rules of SPV.
|
|
541
541
|
*/
|
|
542
|
-
async verify (chainTracker: ChainTracker | 'scripts only'): Promise<boolean> {
|
|
542
|
+
async verify (chainTracker: ChainTracker | 'scripts only' = defaultChainTracker()): Promise<boolean> {
|
|
543
543
|
// If the transaction has a valid merkle path, verification is complete.
|
|
544
544
|
if (typeof this.merklePath === 'object' && chainTracker !== 'scripts only') {
|
|
545
545
|
const proofValid = await this.merklePath.verify(
|
|
@@ -16,6 +16,7 @@ import validTransactions from './tx.valid.vectors'
|
|
|
16
16
|
import bigTX from './bigtx.vectors'
|
|
17
17
|
|
|
18
18
|
const BRC62Hex = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000000'
|
|
19
|
+
const MerkleRootFromBEEF = 'bb6f640cc4ee56bf38eb5a1969ac0c16caa2d3d202b22bf3735d10eec0ca6e00'
|
|
19
20
|
|
|
20
21
|
describe('Transaction', () => {
|
|
21
22
|
const txIn = {
|
|
@@ -354,6 +355,40 @@ describe('Transaction', () => {
|
|
|
354
355
|
})
|
|
355
356
|
|
|
356
357
|
describe('Broadcast', () => {
|
|
358
|
+
it('Broadcasts with the default Broadcaster instance', async () => {
|
|
359
|
+
const mockedFetch = jest.fn().mockResolvedValue({
|
|
360
|
+
ok: true,
|
|
361
|
+
status: 200,
|
|
362
|
+
statusText: 'OK',
|
|
363
|
+
headers: {
|
|
364
|
+
get(key: string) {
|
|
365
|
+
if (key === 'Content-Type') {
|
|
366
|
+
return 'application/json'
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
json: async () => ({
|
|
371
|
+
txid: 'mocked_txid',
|
|
372
|
+
txStatus: 'success',
|
|
373
|
+
extraInfo: 'received'
|
|
374
|
+
})
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
(global as any).window = {fetch: mockedFetch} as any
|
|
378
|
+
|
|
379
|
+
const tx = new Transaction()
|
|
380
|
+
const rv = await tx.broadcast()
|
|
381
|
+
|
|
382
|
+
expect(mockedFetch).toHaveBeenCalled()
|
|
383
|
+
const url = (mockedFetch as jest.Mock).mock.calls[0][0] as string
|
|
384
|
+
expect(url).toEqual('https://arc.taal.com/v1/tx')
|
|
385
|
+
expect(rv).toEqual({
|
|
386
|
+
status: 'success',
|
|
387
|
+
txid: 'mocked_txid',
|
|
388
|
+
message: 'success received'
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
|
|
357
392
|
it('Broadcasts with the provided Broadcaster instance', async () => {
|
|
358
393
|
const mockBroadcast = jest.fn(() => 'MOCK_RV')
|
|
359
394
|
const tx = new Transaction()
|
|
@@ -391,6 +426,33 @@ describe('Transaction', () => {
|
|
|
391
426
|
const verified = await tx.verify(alwaysYesChainTracker)
|
|
392
427
|
expect(verified).toBe(true)
|
|
393
428
|
})
|
|
429
|
+
|
|
430
|
+
it('Verifies the transaction from the BEEF spec with a default chain tracker', async () => {
|
|
431
|
+
const mockFetch = jest.fn().mockResolvedValue({
|
|
432
|
+
ok: true,
|
|
433
|
+
status: 200,
|
|
434
|
+
statusText: 'OK',
|
|
435
|
+
headers: {
|
|
436
|
+
get(key: string) {
|
|
437
|
+
if (key === 'Content-Type') {
|
|
438
|
+
return 'application/json'
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
json: async () => ({
|
|
443
|
+
merkleroot: MerkleRootFromBEEF,
|
|
444
|
+
})
|
|
445
|
+
});
|
|
446
|
+
(global as any).window = {fetch: mockFetch}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
const tx = Transaction.fromHexBEEF(BRC62Hex)
|
|
450
|
+
|
|
451
|
+
const verified = await tx.verify()
|
|
452
|
+
|
|
453
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
454
|
+
expect(verified).toBe(true)
|
|
455
|
+
})
|
|
394
456
|
})
|
|
395
457
|
|
|
396
458
|
describe('vectors: a 1mb transaction', () => {
|
|
@@ -1,33 +1,69 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {BroadcastResponse, BroadcastFailure, Broadcaster} from '../Broadcaster.js'
|
|
2
2
|
import Transaction from '../Transaction.js'
|
|
3
|
+
import {HttpClient, HttpClientRequestOptions} from "../http/HttpClient.js";
|
|
4
|
+
import {defaultHttpClient} from "../http/DefaultHttpClient.js";
|
|
5
|
+
import Random from "../../primitives/Random.js";
|
|
6
|
+
import {toHex} from "../../primitives/utils.js";
|
|
7
|
+
|
|
8
|
+
/** Configuration options for the ARC broadcaster. */
|
|
9
|
+
export interface ArcConfig {
|
|
10
|
+
/** Authentication token for the ARC API */
|
|
11
|
+
apiKey?: string
|
|
12
|
+
/** Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set */
|
|
13
|
+
deploymentId?: string
|
|
14
|
+
/** The HTTP client used to make requests to the ARC API. */
|
|
15
|
+
httpClient?: HttpClient
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function defaultDeploymentId() {
|
|
20
|
+
return `ts-sdk-${toHex(Random(16))}`;
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
/**
|
|
5
24
|
* Represents an ARC transaction broadcaster.
|
|
6
25
|
*/
|
|
7
26
|
export default class ARC implements Broadcaster {
|
|
8
|
-
URL: string
|
|
9
|
-
apiKey: string
|
|
27
|
+
readonly URL: string
|
|
28
|
+
readonly apiKey: string | undefined
|
|
29
|
+
readonly deploymentId: string
|
|
30
|
+
private readonly httpClient: HttpClient;
|
|
10
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Constructs an instance of the ARC broadcaster.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} URL - The URL endpoint for the ARC API.
|
|
36
|
+
* @param {ArcConfig} config - Configuration options for the ARC broadcaster.
|
|
37
|
+
*/
|
|
38
|
+
constructor(URL: string, config?: ArcConfig)
|
|
11
39
|
/**
|
|
12
40
|
* Constructs an instance of the ARC broadcaster.
|
|
13
41
|
*
|
|
14
42
|
* @param {string} URL - The URL endpoint for the ARC API.
|
|
15
43
|
* @param {string} apiKey - The API key used for authorization with the ARC API.
|
|
16
44
|
*/
|
|
17
|
-
constructor
|
|
45
|
+
constructor(URL: string, apiKey?: string)
|
|
46
|
+
|
|
47
|
+
constructor(URL: string, config?: string | ArcConfig) {
|
|
18
48
|
this.URL = URL
|
|
19
|
-
|
|
49
|
+
if (typeof config === 'string') {
|
|
50
|
+
this.apiKey = config
|
|
51
|
+
this.httpClient = defaultHttpClient()
|
|
52
|
+
} else {
|
|
53
|
+
const {apiKey, deploymentId, httpClient} = config ?? {} as ArcConfig
|
|
54
|
+
this.httpClient = httpClient ?? defaultHttpClient()
|
|
55
|
+
this.deploymentId = deploymentId ?? defaultDeploymentId()
|
|
56
|
+
this.apiKey = apiKey
|
|
57
|
+
}
|
|
20
58
|
}
|
|
21
59
|
|
|
22
60
|
/**
|
|
23
61
|
* Broadcasts a transaction via ARC.
|
|
24
|
-
* This method will attempt to use `window.fetch` if available (in browser environments).
|
|
25
|
-
* If running in a Node.js environment, it falls back to using the Node.js `https` module.
|
|
26
62
|
*
|
|
27
63
|
* @param {Transaction} tx - The transaction to be broadcasted.
|
|
28
64
|
* @returns {Promise<BroadcastResponse | BroadcastFailure>} A promise that resolves to either a success or failure response.
|
|
29
65
|
*/
|
|
30
|
-
async broadcast
|
|
66
|
+
async broadcast(tx: Transaction): Promise<BroadcastResponse | BroadcastFailure> {
|
|
31
67
|
let rawTx
|
|
32
68
|
try {
|
|
33
69
|
rawTx = tx.toHexEF()
|
|
@@ -37,45 +73,28 @@ export default class ARC implements Broadcaster {
|
|
|
37
73
|
} else {
|
|
38
74
|
throw error
|
|
39
75
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const requestOptions: HttpClientRequestOptions = {
|
|
42
79
|
method: 'POST',
|
|
43
|
-
headers:
|
|
44
|
-
|
|
45
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
46
|
-
},
|
|
47
|
-
body: JSON.stringify({ rawTx })
|
|
80
|
+
headers: this.requestHeaders(),
|
|
81
|
+
data: {rawTx}
|
|
48
82
|
}
|
|
49
83
|
|
|
50
84
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
55
|
-
// Use fetch in a browser environment
|
|
56
|
-
response = await window.fetch(`${this.URL}/v1/tx`, requestOptions)
|
|
57
|
-
data = await response.json()
|
|
58
|
-
} else if (typeof require !== 'undefined') {
|
|
59
|
-
// Use Node.js https module
|
|
60
|
-
// eslint-disable-next-line
|
|
61
|
-
const https = require('https')
|
|
62
|
-
response = await this.nodeFetch(https, requestOptions)
|
|
63
|
-
data = JSON.parse(response)
|
|
64
|
-
} else {
|
|
65
|
-
throw new Error('No method available to perform HTTP request')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (data.txid as boolean || response.ok as boolean || response.statusCode === 200) {
|
|
85
|
+
const response = await this.httpClient.request<ArcResponse>(`${this.URL}/v1/tx`, requestOptions)
|
|
86
|
+
if (response.ok) {
|
|
87
|
+
const {txid, extraInfo, txStatus} = response.data
|
|
69
88
|
return {
|
|
70
89
|
status: 'success',
|
|
71
|
-
txid:
|
|
72
|
-
message:
|
|
90
|
+
txid: txid,
|
|
91
|
+
message: `${txStatus} ${extraInfo}`
|
|
73
92
|
}
|
|
74
93
|
} else {
|
|
75
94
|
return {
|
|
76
95
|
status: 'error',
|
|
77
|
-
code:
|
|
78
|
-
description:
|
|
96
|
+
code: response.status.toString() ?? 'ERR_UNKNOWN',
|
|
97
|
+
description: response.data?.detail ?? 'Unknown error'
|
|
79
98
|
}
|
|
80
99
|
}
|
|
81
100
|
} catch (error) {
|
|
@@ -89,27 +108,22 @@ export default class ARC implements Broadcaster {
|
|
|
89
108
|
}
|
|
90
109
|
}
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
res.on('data', (chunk: string) => {
|
|
98
|
-
data += chunk
|
|
99
|
-
})
|
|
100
|
-
res.on('end', () => {
|
|
101
|
-
resolve(data)
|
|
102
|
-
})
|
|
103
|
-
})
|
|
111
|
+
private requestHeaders() {
|
|
112
|
+
const headers: Record<string, string> = {
|
|
113
|
+
'Content-Type': 'application/json',
|
|
114
|
+
'XDeployment-ID': this.deploymentId,
|
|
115
|
+
}
|
|
104
116
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
if (this.apiKey) {
|
|
118
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`
|
|
119
|
+
}
|
|
108
120
|
|
|
109
|
-
|
|
110
|
-
req.write(requestOptions.body)
|
|
111
|
-
}
|
|
112
|
-
req.end()
|
|
113
|
-
})
|
|
121
|
+
return headers
|
|
114
122
|
}
|
|
115
123
|
}
|
|
124
|
+
|
|
125
|
+
interface ArcResponse {
|
|
126
|
+
txid: string
|
|
127
|
+
extraInfo: string
|
|
128
|
+
txStatus: string
|
|
129
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {BroadcastResponse, BroadcastFailure, Broadcaster} from '../Broadcaster.js'
|
|
2
|
+
import Transaction from '../Transaction.js'
|
|
3
|
+
import {HttpClient} from "../http/HttpClient.js";
|
|
4
|
+
import {defaultHttpClient} from "../http/DefaultHttpClient.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Represents an WhatsOnChain transaction broadcaster.
|
|
8
|
+
*/
|
|
9
|
+
export default class WhatsOnChainBroadcaster implements Broadcaster {
|
|
10
|
+
readonly network: string
|
|
11
|
+
private readonly URL: string
|
|
12
|
+
private readonly httpClient: HttpClient;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Constructs an instance of the WhatsOnChain broadcaster.
|
|
16
|
+
*
|
|
17
|
+
* @param {'main' | 'test' | 'stn'} network - The BSV network to use when calling the WhatsOnChain API.
|
|
18
|
+
* @param {HttpClient} httpClient - The HTTP client used to make requests to the API.
|
|
19
|
+
*/
|
|
20
|
+
constructor(network: 'main' | 'test' | 'stn' = 'main', httpClient: HttpClient = defaultHttpClient()) {
|
|
21
|
+
this.network = network
|
|
22
|
+
this.URL = `https://api.whatsonchain.com/v1/bsv/${network}/tx/raw`
|
|
23
|
+
this.httpClient = httpClient
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Broadcasts a transaction via WhatsOnChain.
|
|
28
|
+
*
|
|
29
|
+
* @param {Transaction} tx - The transaction to be broadcasted.
|
|
30
|
+
* @returns {Promise<BroadcastResponse | BroadcastFailure>} A promise that resolves to either a success or failure response.
|
|
31
|
+
*/
|
|
32
|
+
async broadcast(tx: Transaction): Promise<BroadcastResponse | BroadcastFailure> {
|
|
33
|
+
let rawTx = tx.toHex()
|
|
34
|
+
|
|
35
|
+
const requestOptions = {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'Accept': 'text/plain'
|
|
40
|
+
},
|
|
41
|
+
data: {txhex: rawTx}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const response = await this.httpClient.request<string>(this.URL, requestOptions)
|
|
46
|
+
if (response.ok) {
|
|
47
|
+
const txid = response.data
|
|
48
|
+
return {
|
|
49
|
+
status: 'success',
|
|
50
|
+
txid: txid,
|
|
51
|
+
message: 'broadcast successful'
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
return {
|
|
55
|
+
status: 'error',
|
|
56
|
+
code: response.status.toString() ?? 'ERR_UNKNOWN',
|
|
57
|
+
description: response.data ?? 'Unknown error'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return {
|
|
62
|
+
status: 'error',
|
|
63
|
+
code: '500',
|
|
64
|
+
description: typeof error.message === 'string'
|
|
65
|
+
? error.message
|
|
66
|
+
: 'Internal Server Error'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|