@bsv/sdk 2.0.11 → 2.0.13
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/dist/cjs/package.json +1 -1
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js +827 -0
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +654 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
- package/dist/cjs/src/overlay-tools/HostReputationTracker.js +21 -13
- package/dist/cjs/src/overlay-tools/HostReputationTracker.js.map +1 -1
- package/dist/cjs/src/primitives/PrivateKey.js +3 -3
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +17 -9
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/storage/StorageDownloader.js +6 -6
- package/dist/cjs/src/storage/StorageDownloader.js.map +1 -1
- package/dist/cjs/src/storage/StorageUtils.js +1 -1
- package/dist/cjs/src/storage/StorageUtils.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +168 -27
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js +825 -0
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +619 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
- package/dist/esm/src/overlay-tools/HostReputationTracker.js +21 -13
- package/dist/esm/src/overlay-tools/HostReputationTracker.js.map +1 -1
- package/dist/esm/src/primitives/PrivateKey.js +3 -3
- package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
- package/dist/esm/src/script/Spend.js +17 -9
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/storage/StorageDownloader.js +6 -6
- package/dist/esm/src/storage/StorageDownloader.js.map +1 -1
- package/dist/esm/src/storage/StorageUtils.js +1 -1
- package/dist/esm/src/storage/StorageUtils.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +168 -27
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts +21 -0
- package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts.map +1 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts +2 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/HostReputationTracker.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts +27 -0
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/docs/reference/storage.md +1 -1
- package/docs/reference/transaction.md +40 -0
- package/package.json +1 -1
- package/src/auth/clients/__tests__/AuthFetch.additional.test.ts +1131 -0
- package/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.ts +770 -0
- package/src/auth/utils/__tests/validateCertificates.test.ts +12 -9
- package/src/compat/__tests/Mnemonic.additional.test.ts +64 -0
- package/src/identity/__tests/IdentityClient.additional.test.ts +767 -0
- package/src/kvstore/__tests/LocalKVStore.additional.test.ts +611 -0
- package/src/kvstore/__tests/LocalKVStore.test.ts +4 -6
- package/src/kvstore/__tests/kvStoreInterpreter.test.ts +327 -0
- package/src/overlay-tools/HostReputationTracker.ts +17 -14
- package/src/overlay-tools/__tests/HostReputationTracker.additional.test.ts +561 -0
- package/src/overlay-tools/__tests/LookupResolver.additional.test.ts +612 -0
- package/src/overlay-tools/__tests/withDoubleSpendRetry.test.ts +278 -0
- package/src/primitives/PrivateKey.ts +3 -3
- package/src/primitives/__tests/BigNumber.additional.test.ts +79 -0
- package/src/primitives/__tests/Curve.additional.test.ts +208 -0
- package/src/primitives/__tests/ECDSA.additional.test.ts +122 -0
- package/src/primitives/__tests/Hash.additional.test.ts +59 -0
- package/src/primitives/__tests/JacobianPoint.test.ts +308 -0
- package/src/primitives/__tests/Point.additional.test.ts +503 -0
- package/src/primitives/__tests/PublicKey.additional.test.ts +383 -0
- package/src/primitives/__tests/Random.additional.test.ts +262 -0
- package/src/primitives/__tests/Signature.test.ts +333 -0
- package/src/primitives/__tests/TransactionSignature.additional.test.ts +241 -0
- package/src/registry/__tests/RegistryClient.additional.test.ts +750 -0
- package/src/remittance/__tests/BasicBRC29.additional.test.ts +657 -0
- package/src/remittance/__tests/RemittanceManager.additional.test.ts +1272 -0
- package/src/script/Spend.ts +19 -11
- package/src/script/__tests/LockingUnlockingScript.test.ts +79 -0
- package/src/script/__tests/Script.additional.test.ts +100 -0
- package/src/script/__tests/ScriptEvaluationError.test.ts +98 -0
- package/src/script/__tests/Spend.additional.test.ts +837 -0
- package/src/script/templates/__tests/RPuzzle.test.ts +134 -0
- package/src/storage/StorageDownloader.ts +6 -6
- package/src/storage/StorageUtils.ts +1 -1
- package/src/transaction/MerklePath.ts +196 -36
- package/src/transaction/__tests/BeefParty.additional.test.ts +22 -0
- package/src/transaction/__tests/Broadcaster.test.ts +159 -0
- package/src/transaction/__tests/MerklePath.bench.test.ts +105 -0
- package/src/transaction/__tests/MerklePath.test.ts +232 -21
- package/src/transaction/__tests/Transaction.additional.test.ts +225 -0
- package/src/transaction/broadcasters/__tests/ARC.additional.test.ts +585 -0
- package/src/transaction/broadcasters/__tests/Teranode.test.ts +349 -0
- package/src/transaction/chaintrackers/__tests/BlockHeadersService.test.ts +253 -0
- package/src/transaction/chaintrackers/__tests/DefaultChainTracker.test.ts +44 -0
- package/src/transaction/chaintrackers/__tests/WhatsOnChain.additional.test.ts +193 -0
- package/src/transaction/fee-models/__tests/SatoshisPerKilobyte.test.ts +262 -0
- package/src/transaction/http/__tests/BinaryFetchClient.test.ts +212 -0
- package/src/transaction/http/__tests/DefaultHttpClient.additional.test.ts +192 -0
- package/src/transaction/http/__tests/DefaultHttpClient.test.ts +71 -0
- package/src/wallet/__tests/ProtoWallet.additional.test.ts +134 -0
- package/src/wallet/__tests/WERR.test.ts +212 -0
- package/src/wallet/__tests/WalletClient.additional.test.ts +699 -0
- package/src/wallet/__tests/WalletClient.substrate.test.ts +759 -0
- package/src/wallet/__tests/WalletError.test.ts +290 -0
- package/src/wallet/__tests/validationHelpers.test.ts +1218 -0
- package/src/wallet/substrates/__tests/HTTPWalletJSON.test.ts +496 -0
- package/src/wallet/substrates/__tests/HTTPWalletWire.test.ts +273 -0
package/src/script/Spend.ts
CHANGED
|
@@ -266,7 +266,11 @@ export default class Spend {
|
|
|
266
266
|
if (this.stack.length === 0) {
|
|
267
267
|
this.scriptEvaluationError('Attempted to pop from an empty stack.')
|
|
268
268
|
}
|
|
269
|
-
const item = this.stack.pop()
|
|
269
|
+
const item = this.stack.pop()
|
|
270
|
+
if (item === undefined) {
|
|
271
|
+
this.scriptEvaluationError('Attempted to pop from an empty stack.')
|
|
272
|
+
return [] // unreachable; scriptEvaluationError always throws
|
|
273
|
+
}
|
|
270
274
|
this.stackMem -= item.length
|
|
271
275
|
return item
|
|
272
276
|
}
|
|
@@ -290,7 +294,11 @@ export default class Spend {
|
|
|
290
294
|
if (this.altStack.length === 0) {
|
|
291
295
|
this.scriptEvaluationError('Attempted to pop from an empty alt stack.')
|
|
292
296
|
}
|
|
293
|
-
const item = this.altStack.pop()
|
|
297
|
+
const item = this.altStack.pop()
|
|
298
|
+
if (item === undefined) {
|
|
299
|
+
this.scriptEvaluationError('Attempted to pop from an empty alt stack.')
|
|
300
|
+
return [] // unreachable; scriptEvaluationError always throws
|
|
301
|
+
}
|
|
294
302
|
this.altStackMem -= item.length
|
|
295
303
|
return item
|
|
296
304
|
}
|
|
@@ -308,7 +316,7 @@ export default class Spend {
|
|
|
308
316
|
this.scriptEvaluationError('The signature must have a low S value.')
|
|
309
317
|
return false
|
|
310
318
|
}
|
|
311
|
-
} catch
|
|
319
|
+
} catch {
|
|
312
320
|
this.scriptEvaluationError('The signature format is invalid.')
|
|
313
321
|
return false
|
|
314
322
|
}
|
|
@@ -340,7 +348,7 @@ export default class Spend {
|
|
|
340
348
|
}
|
|
341
349
|
try {
|
|
342
350
|
PublicKey.fromDER(buf as number[]) // This can throw for stricter DER rules
|
|
343
|
-
} catch
|
|
351
|
+
} catch {
|
|
344
352
|
this.scriptEvaluationError('The public key is in an unknown format.')
|
|
345
353
|
return false
|
|
346
354
|
}
|
|
@@ -395,7 +403,7 @@ export default class Spend {
|
|
|
395
403
|
const operation = currentScript.chunks[this.programCounter]
|
|
396
404
|
|
|
397
405
|
const currentOpcode = operation.op
|
|
398
|
-
if (
|
|
406
|
+
if (currentOpcode === undefined) {
|
|
399
407
|
this.scriptEvaluationError(`Missing opcode in ${this.context} at pc=${this.programCounter}.`) // Error thrown
|
|
400
408
|
}
|
|
401
409
|
if (Array.isArray(operation.data) && operation.data.length > maxScriptElementSize) {
|
|
@@ -547,7 +555,7 @@ export default class Spend {
|
|
|
547
555
|
break
|
|
548
556
|
case OP.OP_ELSE:
|
|
549
557
|
if (this.ifStack.length === 0) this.scriptEvaluationError('OP_ELSE requires a preceeding OP_IF.')
|
|
550
|
-
this.ifStack[this.ifStack.length - 1] = !this.ifStack[this.ifStack.length - 1]
|
|
558
|
+
this.ifStack[this.ifStack.length - 1] = !(this.ifStack[this.ifStack.length - 1])
|
|
551
559
|
break
|
|
552
560
|
case OP.OP_ENDIF:
|
|
553
561
|
if (this.ifStack.length === 0) this.scriptEvaluationError('OP_ENDIF requires a preceeding OP_IF.')
|
|
@@ -682,7 +690,7 @@ export default class Spend {
|
|
|
682
690
|
// stack is [... rest, x1, x2]
|
|
683
691
|
// We want [... rest, x2_copy, x1, x2]
|
|
684
692
|
this.ensureStackMem(buf1.length)
|
|
685
|
-
this.stack.splice(
|
|
693
|
+
this.stack.splice(-2, 0, buf1.slice()) // Insert copy of x2 before x1
|
|
686
694
|
this.stackMem += buf1.length // Account for the new copy
|
|
687
695
|
break
|
|
688
696
|
case OP.OP_SIZE:
|
|
@@ -769,7 +777,7 @@ export default class Spend {
|
|
|
769
777
|
case OP.OP_NEGATE: bn = bn.neg(); break
|
|
770
778
|
case OP.OP_ABS: if (bn.isNeg()) bn = bn.neg(); break
|
|
771
779
|
case OP.OP_NOT: bn = new BigNumber(bn.cmpn(0) === 0 ? 1 : 0); break
|
|
772
|
-
case OP.OP_0NOTEQUAL: bn = new BigNumber(bn.cmpn(0)
|
|
780
|
+
case OP.OP_0NOTEQUAL: bn = new BigNumber(bn.cmpn(0) === 0 ? 0 : 1); break
|
|
773
781
|
}
|
|
774
782
|
this.pushStack(bn.toScriptNum())
|
|
775
783
|
break
|
|
@@ -812,7 +820,7 @@ export default class Spend {
|
|
|
812
820
|
case OP.OP_BOOLOR: resultBnArithmetic = new BigNumber((bn1.cmpn(0) !== 0 || bn2.cmpn(0) !== 0) ? 1 : 0); break
|
|
813
821
|
case OP.OP_NUMEQUAL: resultBnArithmetic = new BigNumber(bn1.cmp(bn2) === 0 ? 1 : 0); break
|
|
814
822
|
case OP.OP_NUMEQUALVERIFY: resultBnArithmetic = new BigNumber(bn1.cmp(bn2) === 0 ? 1 : 0); break
|
|
815
|
-
case OP.OP_NUMNOTEQUAL: resultBnArithmetic = new BigNumber(bn1.cmp(bn2)
|
|
823
|
+
case OP.OP_NUMNOTEQUAL: resultBnArithmetic = new BigNumber(bn1.cmp(bn2) === 0 ? 0 : 1); break
|
|
816
824
|
case OP.OP_LESSTHAN: resultBnArithmetic = new BigNumber(bn1.cmp(bn2) < 0 ? 1 : 0); break
|
|
817
825
|
case OP.OP_GREATERTHAN: resultBnArithmetic = new BigNumber(bn1.cmp(bn2) > 0 ? 1 : 0); break
|
|
818
826
|
case OP.OP_LESSTHANOREQUAL: resultBnArithmetic = new BigNumber(bn1.cmp(bn2) <= 0 ? 1 : 0); break
|
|
@@ -875,7 +883,7 @@ export default class Spend {
|
|
|
875
883
|
|
|
876
884
|
pubkey = PublicKey.fromDER(bufPubkey)
|
|
877
885
|
fSuccess = this.verifySignature(sig, pubkey, subscript)
|
|
878
|
-
} catch
|
|
886
|
+
} catch {
|
|
879
887
|
fSuccess = false
|
|
880
888
|
}
|
|
881
889
|
}
|
|
@@ -949,7 +957,7 @@ export default class Spend {
|
|
|
949
957
|
sig = TransactionSignature.fromChecksigFormat(bufSig)
|
|
950
958
|
pubkey = PublicKey.fromDER(bufPubkey)
|
|
951
959
|
fOk = this.verifySignature(sig, pubkey, subscript)
|
|
952
|
-
} catch
|
|
960
|
+
} catch {
|
|
953
961
|
fOk = false
|
|
954
962
|
}
|
|
955
963
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import LockingScript from '../../script/LockingScript'
|
|
2
|
+
import UnlockingScript from '../../script/UnlockingScript'
|
|
3
|
+
import Script from '../../script/Script'
|
|
4
|
+
|
|
5
|
+
describe('LockingScript', () => {
|
|
6
|
+
it('isLockingScript() returns true', () => {
|
|
7
|
+
const script = new LockingScript()
|
|
8
|
+
expect(script.isLockingScript()).toBe(true)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('isUnlockingScript() returns false', () => {
|
|
12
|
+
const script = new LockingScript()
|
|
13
|
+
expect(script.isUnlockingScript()).toBe(false)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('extends Script', () => {
|
|
17
|
+
const script = new LockingScript()
|
|
18
|
+
expect(script).toBeInstanceOf(Script)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('can be constructed from hex via inherited fromHex', () => {
|
|
22
|
+
const hex = '76a914' + '00'.repeat(20) + '88ac'
|
|
23
|
+
const script = Script.fromHex(hex)
|
|
24
|
+
const locking = new LockingScript()
|
|
25
|
+
locking.chunks = script.chunks
|
|
26
|
+
expect(locking.toHex()).toBe(hex)
|
|
27
|
+
expect(locking.isLockingScript()).toBe(true)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('fromHex returns a Script instance that can be used to populate a LockingScript', () => {
|
|
31
|
+
const hex = '51' // OP_1
|
|
32
|
+
const base = Script.fromHex(hex)
|
|
33
|
+
expect(base.toHex()).toBe(hex)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('UnlockingScript', () => {
|
|
38
|
+
it('isLockingScript() returns false', () => {
|
|
39
|
+
const script = new UnlockingScript()
|
|
40
|
+
expect(script.isLockingScript()).toBe(false)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('isUnlockingScript() returns true', () => {
|
|
44
|
+
const script = new UnlockingScript()
|
|
45
|
+
expect(script.isUnlockingScript()).toBe(true)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('extends Script', () => {
|
|
49
|
+
const script = new UnlockingScript()
|
|
50
|
+
expect(script).toBeInstanceOf(Script)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('can be constructed from hex via inherited fromHex', () => {
|
|
54
|
+
const hex = '4830450221'
|
|
55
|
+
const script = Script.fromHex(hex)
|
|
56
|
+
const unlocking = new UnlockingScript()
|
|
57
|
+
unlocking.chunks = script.chunks
|
|
58
|
+
expect(unlocking.isUnlockingScript()).toBe(true)
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('LockingScript vs UnlockingScript', () => {
|
|
63
|
+
it('LockingScript and UnlockingScript return opposite values for isLockingScript', () => {
|
|
64
|
+
const locking = new LockingScript()
|
|
65
|
+
const unlocking = new UnlockingScript()
|
|
66
|
+
expect(locking.isLockingScript()).not.toBe(unlocking.isLockingScript())
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('LockingScript and UnlockingScript return opposite values for isUnlockingScript', () => {
|
|
70
|
+
const locking = new LockingScript()
|
|
71
|
+
const unlocking = new UnlockingScript()
|
|
72
|
+
expect(locking.isUnlockingScript()).not.toBe(unlocking.isUnlockingScript())
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('both are instances of Script', () => {
|
|
76
|
+
expect(new LockingScript()).toBeInstanceOf(Script)
|
|
77
|
+
expect(new UnlockingScript()).toBeInstanceOf(Script)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import Script from '../Script'
|
|
2
|
+
import OP from '../OP'
|
|
3
|
+
import BigNumber from '../../primitives/BigNumber'
|
|
4
|
+
|
|
5
|
+
describe('Script – additional coverage', () => {
|
|
6
|
+
describe('fromHex', () => {
|
|
7
|
+
it('throws for odd-length hex string', () => {
|
|
8
|
+
expect(() => Script.fromHex('abc')).toThrow()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('throws for non-hex characters', () => {
|
|
12
|
+
expect(() => Script.fromHex('gggg')).toThrow()
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('isLockingScript / isUnlockingScript on base Script', () => {
|
|
17
|
+
it('throws NotImplemented for isLockingScript', () => {
|
|
18
|
+
const script = new Script()
|
|
19
|
+
expect(() => script.isLockingScript()).toThrow('Not implemented')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('throws NotImplemented for isUnlockingScript', () => {
|
|
23
|
+
const script = new Script()
|
|
24
|
+
expect(() => script.isUnlockingScript()).toThrow('Not implemented')
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('writeScript', () => {
|
|
29
|
+
it('appends all chunks from another script', () => {
|
|
30
|
+
const s1 = Script.fromASM('OP_1 OP_2')
|
|
31
|
+
const s2 = Script.fromASM('OP_3')
|
|
32
|
+
s1.writeScript(s2)
|
|
33
|
+
expect(s1.toASM()).toBe('OP_1 OP_2 OP_3')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('setChunkOpCode', () => {
|
|
38
|
+
it('replaces the opcode at the given index', () => {
|
|
39
|
+
const script = Script.fromASM('OP_1 OP_2 OP_3')
|
|
40
|
+
script.setChunkOpCode(1, OP.OP_NOP)
|
|
41
|
+
expect(script.chunks[1].op).toBe(OP.OP_NOP)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('writeBn', () => {
|
|
46
|
+
it('pushes OP_0 for zero', () => {
|
|
47
|
+
const script = new Script().writeBn(new BigNumber(0))
|
|
48
|
+
expect(script.chunks[0].op).toBe(OP.OP_0)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('pushes OP_1NEGATE for -1', () => {
|
|
52
|
+
const script = new Script().writeBn(new BigNumber(-1))
|
|
53
|
+
expect(script.chunks[0].op).toBe(OP.OP_1NEGATE)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('pushes OP_1..OP_16 for 1..16', () => {
|
|
57
|
+
for (let n = 1; n <= 16; n++) {
|
|
58
|
+
const script = new Script().writeBn(new BigNumber(n))
|
|
59
|
+
expect(script.chunks[0].op).toBe(OP.OP_1 + (n - 1))
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('pushes encoded binary for numbers > 16', () => {
|
|
64
|
+
const script = new Script().writeBn(new BigNumber(1000))
|
|
65
|
+
expect(script.chunks[0].data).toBeDefined()
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('writeNumber', () => {
|
|
70
|
+
it('writes a number to the script', () => {
|
|
71
|
+
const script = new Script().writeNumber(5)
|
|
72
|
+
expect(script.chunks[0].op).toBe(OP.OP_5)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('writeBin', () => {
|
|
77
|
+
it('uses OP_PUSHDATA1 for data 76..255 bytes', () => {
|
|
78
|
+
const data = new Array(76).fill(0x01)
|
|
79
|
+
const script = new Script().writeBin(data)
|
|
80
|
+
expect(script.chunks[0].op).toBe(OP.OP_PUSHDATA1)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('uses OP_PUSHDATA2 for data 256..65535 bytes', () => {
|
|
84
|
+
const data = new Array(256).fill(0x02)
|
|
85
|
+
const script = new Script().writeBin(data)
|
|
86
|
+
expect(script.chunks[0].op).toBe(OP.OP_PUSHDATA2)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('findAndDelete – PUSHDATA1 target', () => {
|
|
91
|
+
it('deletes chunks encoded with OP_PUSHDATA1 (76-255 bytes)', () => {
|
|
92
|
+
const data = new Array(76).fill(0xab)
|
|
93
|
+
const target = new Script().writeBin(data)
|
|
94
|
+
const source = new Script().writeBin(data).writeBin(data).writeOpCode(OP.OP_1)
|
|
95
|
+
source.findAndDelete(target)
|
|
96
|
+
expect(source.chunks).toHaveLength(1)
|
|
97
|
+
expect(source.chunks[0].op).toBe(OP.OP_1)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
})
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import ScriptEvaluationError from '../ScriptEvaluationError'
|
|
2
|
+
|
|
3
|
+
const baseParams = {
|
|
4
|
+
message: 'test error',
|
|
5
|
+
txid: 'a'.repeat(64),
|
|
6
|
+
outputIndex: 0,
|
|
7
|
+
context: 'LockingScript' as const,
|
|
8
|
+
programCounter: 3,
|
|
9
|
+
stackState: [] as number[][],
|
|
10
|
+
altStackState: [] as number[][],
|
|
11
|
+
ifStackState: [] as boolean[],
|
|
12
|
+
stackMem: 0,
|
|
13
|
+
altStackMem: 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('ScriptEvaluationError', () => {
|
|
17
|
+
it('constructs with empty stacks', () => {
|
|
18
|
+
const err = new ScriptEvaluationError(baseParams)
|
|
19
|
+
expect(err).toBeInstanceOf(Error)
|
|
20
|
+
expect(err.message).toContain('test error')
|
|
21
|
+
expect(err.txid).toBe('a'.repeat(64))
|
|
22
|
+
expect(err.outputIndex).toBe(0)
|
|
23
|
+
expect(err.context).toBe('LockingScript')
|
|
24
|
+
expect(err.programCounter).toBe(3)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('renders valid stack items as hex in the message (toHex branch)', () => {
|
|
28
|
+
const err = new ScriptEvaluationError({
|
|
29
|
+
...baseParams,
|
|
30
|
+
stackState: [[0xde, 0xad], [0xbe, 0xef]],
|
|
31
|
+
altStackState: [[0x01]]
|
|
32
|
+
})
|
|
33
|
+
expect(err.message).toContain('dead')
|
|
34
|
+
expect(err.message).toContain('beef')
|
|
35
|
+
expect(err.message).toContain('01')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('renders null stack item as null/undef (null/undef branch)', () => {
|
|
39
|
+
// The message is built before the deep-copy, so null/undef branch is executed
|
|
40
|
+
// even though the constructor later throws when trying to slice null.
|
|
41
|
+
expect(() => {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
return new ScriptEvaluationError({ ...baseParams, stackState: [null as any] })
|
|
44
|
+
}).toThrow()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('renders undefined stack item as null/undef (undefined branch)', () => {
|
|
48
|
+
expect(() => {
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
return new ScriptEvaluationError({ ...baseParams, stackState: [undefined as any] })
|
|
51
|
+
}).toThrow()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('renders non-array stack item as INVALID_STACK_ITEM', () => {
|
|
55
|
+
expect(() => {
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
return new ScriptEvaluationError({ ...baseParams, stackState: [{ notAnArray: true } as any] })
|
|
58
|
+
}).toThrow()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('renders null alt-stack item as null/undef (covers altStack map branches)', () => {
|
|
62
|
+
expect(() => {
|
|
63
|
+
return new ScriptEvaluationError({
|
|
64
|
+
...baseParams,
|
|
65
|
+
stackState: [[0x01]],
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
altStackState: [null as any]
|
|
68
|
+
})
|
|
69
|
+
}).toThrow()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('includes ifStack entries in the message', () => {
|
|
73
|
+
const err = new ScriptEvaluationError({
|
|
74
|
+
...baseParams,
|
|
75
|
+
ifStackState: [true, false]
|
|
76
|
+
})
|
|
77
|
+
expect(err.message).toContain('true, false')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('includes UnlockingScript context in the message', () => {
|
|
81
|
+
const err = new ScriptEvaluationError({
|
|
82
|
+
...baseParams,
|
|
83
|
+
context: 'UnlockingScript'
|
|
84
|
+
})
|
|
85
|
+
expect(err.message).toContain('UnlockingScript')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('deep-copies stackState and altStackState to prevent mutation', () => {
|
|
89
|
+
const originalStack = [[1, 2, 3]]
|
|
90
|
+
const err = new ScriptEvaluationError({
|
|
91
|
+
...baseParams,
|
|
92
|
+
stackState: originalStack
|
|
93
|
+
})
|
|
94
|
+
// Mutate original — error should keep its own copy
|
|
95
|
+
originalStack[0].push(4)
|
|
96
|
+
expect(err.stackState[0]).toHaveLength(3)
|
|
97
|
+
})
|
|
98
|
+
})
|