@smartledger/bsv 7.5.8 → 7.6.1

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.
@@ -0,0 +1,180 @@
1
+ 'use strict'
2
+
3
+ /* global describe, it */
4
+
5
+ // Regressions for three wrong calls that shipped through 7.5.8, each of which
6
+ // no test exercised.
7
+ //
8
+ // Two were live: Transaction#fromObject threw on an input its own
9
+ // checkArgument accepts, and OP_CHECKSEQUENCEVERIFY failed every script with a
10
+ // misleading error. Those two tests fail on the unfixed code.
11
+ //
12
+ // The third — sFromPreimage's toBuffer call — is LATENT, and the tests say so
13
+ // rather than pretending otherwise. See the note on that block.
14
+
15
+ require('chai').should()
16
+ var bsv = require('../')
17
+ var BN = bsv.crypto.BN
18
+
19
+ describe('regressions', function () {
20
+ describe('Transaction#fromObject with a Transaction', function () {
21
+ // `fromObject` read `transaction.toObject()` where it meant
22
+ // `arg.toObject()`, so the branch that exists specifically to accept a
23
+ // Transaction was the only branch that could not work. checkArgument
24
+ // admits the input; the next line threw TypeError on it.
25
+ it('accepts a Transaction, which its own checkArgument allows', function () {
26
+ var key = bsv.PrivateKey.fromRandom()
27
+ var source = new bsv.Transaction().to(key.toAddress(), 1000)
28
+
29
+ var copy = new bsv.Transaction().fromObject(source)
30
+
31
+ copy.outputs.length.should.equal(1)
32
+ copy.outputs[0].satoshis.should.equal(1000)
33
+ copy.toObject().should.deep.equal(source.toObject())
34
+ })
35
+
36
+ it('still accepts the plain object form', function () {
37
+ var key = bsv.PrivateKey.fromRandom()
38
+ var source = new bsv.Transaction().to(key.toAddress(), 1000)
39
+
40
+ var copy = new bsv.Transaction().fromObject(source.toObject())
41
+
42
+ copy.toObject().should.deep.equal(source.toObject())
43
+ })
44
+ })
45
+
46
+ describe('BN#toBuffer with the native signature', function () {
47
+ // lib/crypto/bn.js REPLACES toBuffer with an options-object form. Calling
48
+ // it with bn.js's native (endian, length) signature silently ignores the
49
+ // length: 'be' lands in `opts`, `opts.size` is undefined, and the result
50
+ // is the natural-length buffer.
51
+ it('pads to the requested size when given the options form', function () {
52
+ // Leading zero byte, so the natural encoding is shorter than 32.
53
+ var n = new BN('00ff' + '11'.repeat(30), 'hex')
54
+
55
+ n.toBuffer({ size: 32 }).length.should.equal(32)
56
+ })
57
+
58
+ it('does NOT honour a positional length, which is why the options form is required', function () {
59
+ var n = new BN('00ff' + '11'.repeat(30), 'hex')
60
+
61
+ // Documents the trap rather than endorsing it: this is the call shape
62
+ // that produced a 31-byte s inside the OP_PUSH_TX grind.
63
+ n.toBuffer('be', 32).length.should.equal(31)
64
+ })
65
+ })
66
+
67
+ describe('PushTx.sFromPreimage uses the right toBuffer signature', function () {
68
+ // sFromPreimage called `s.toBuffer('be', 32)` — bn.js's NATIVE signature,
69
+ // against a toBuffer that lib/crypto/bn.js has replaced with an
70
+ // options-object form. The length argument was therefore ignored.
71
+ //
72
+ // This is a latent defect, NOT a live one, and the distinction is worth
73
+ // stating precisely: the truncation is currently unreachable. `s` is
74
+ // (HASH256(preimage) + Gx) mod n, and the caller has already required the
75
+ // hash's leading byte to be 0x01..0x7f, so z is in [2**248, 2**255).
76
+ // Adding Gx (about 0.476 * 2**256) never wraps, and the low-S filter caps
77
+ // the result at n/2 — confining s to a narrow band whose leading byte is
78
+ // always 0x7a..0x7f. It can never be zero, so the natural encoding is
79
+ // always 32 bytes anyway.
80
+ //
81
+ // The fix is worth making because the correctness of the output currently
82
+ // depends on two upstream filters that have nothing to do with buffer
83
+ // length. Change the MINIMALDATA range or drop the low-S requirement and
84
+ // the truncation becomes reachable, silently, in a covenant.
85
+ it('always returns exactly 32 bytes', function () {
86
+ var PushTx = bsv.SmartContract.PushTx
87
+ var accepted = 0
88
+
89
+ for (var i = 0; i < 4000; i++) {
90
+ var preimage = bsv.crypto.Hash.sha256(Buffer.from('grind-' + i))
91
+ var sBE = PushTx.sFromPreimage(preimage)
92
+ if (sBE) {
93
+ accepted++
94
+ sBE.length.should.equal(32)
95
+ }
96
+ }
97
+
98
+ accepted.should.be.above(0)
99
+ })
100
+
101
+ it('keeps s inside the band that makes the length safe', function () {
102
+ // Pins the property the paragraph above relies on. If this ever fails,
103
+ // the toBuffer call shape matters again — and the fix is already in.
104
+ var PushTx = bsv.SmartContract.PushTx
105
+
106
+ for (var i = 0; i < 2000; i++) {
107
+ var preimage = bsv.crypto.Hash.sha256(Buffer.from('band-' + i))
108
+ var sBE = PushTx.sFromPreimage(preimage)
109
+ if (sBE) {
110
+ sBE[0].should.be.within(0x01, 0x7f)
111
+ }
112
+ }
113
+ })
114
+ })
115
+
116
+ describe('OP_CHECKSEQUENCEVERIFY', function () {
117
+ // checkSequence masked with `nSequence.and(nLockTimeMask)` where the mask
118
+ // is a plain number. bn.js `and` requires a BN, so it threw
119
+ // 'num.clone is not a function'; the interpreter's try/catch swallowed
120
+ // that and reported SCRIPT_ERR_UNKNOWN_ERROR, making every CSV script fail
121
+ // with a misleading error.
122
+ //
123
+ // Note this is only reachable with SCRIPT_VERIFY_CHECKSEQUENCEVERIFY set:
124
+ // post-Genesis BSV reverted CSV to OP_NOP3 and the flag is off by default.
125
+ it('evaluates instead of failing with SCRIPT_ERR_UNKNOWN_ERROR', function () {
126
+ var Interpreter = bsv.Script.Interpreter
127
+ var key = bsv.PrivateKey.fromRandom()
128
+ var address = key.toAddress()
129
+ var prev = new bsv.Transaction().to(address, 1000)
130
+ var spend = new bsv.Transaction().from({
131
+ txId: prev.hash,
132
+ outputIndex: 0,
133
+ script: bsv.Script.buildPublicKeyHashOut(address).toHex(),
134
+ satoshis: 1000
135
+ })
136
+ spend.version = 2
137
+ spend.inputs[0].sequenceNumber = 20
138
+
139
+ var interpreter = new Interpreter()
140
+ var verified = interpreter.verify(
141
+ bsv.Script.fromASM('OP_1'),
142
+ bsv.Script.fromASM('OP_10 OP_CHECKSEQUENCEVERIFY'),
143
+ spend, 0,
144
+ Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
145
+ new BN(1000)
146
+ )
147
+
148
+ interpreter.errstr.should.equal('')
149
+ verified.should.equal(true)
150
+ })
151
+
152
+ it('still rejects a sequence that does not satisfy the requirement', function () {
153
+ var Interpreter = bsv.Script.Interpreter
154
+ var key = bsv.PrivateKey.fromRandom()
155
+ var address = key.toAddress()
156
+ var prev = new bsv.Transaction().to(address, 1000)
157
+ var spend = new bsv.Transaction().from({
158
+ txId: prev.hash,
159
+ outputIndex: 0,
160
+ script: bsv.Script.buildPublicKeyHashOut(address).toHex(),
161
+ satoshis: 1000
162
+ })
163
+ spend.version = 2
164
+ // Below the required relative locktime of 10.
165
+ spend.inputs[0].sequenceNumber = 5
166
+
167
+ var interpreter = new Interpreter()
168
+ var verified = interpreter.verify(
169
+ bsv.Script.fromASM('OP_1'),
170
+ bsv.Script.fromASM('OP_10 OP_CHECKSEQUENCEVERIFY'),
171
+ spend, 0,
172
+ Interpreter.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
173
+ new BN(1000)
174
+ )
175
+
176
+ verified.should.equal(false)
177
+ interpreter.errstr.should.equal('SCRIPT_ERR_UNSATISFIED_LOCKTIME')
178
+ })
179
+ })
180
+ })
@@ -15,6 +15,26 @@ var scriptTests = require('../data/bitcoind/script_tests')
15
15
  var txValid = require('../data/bitcoind/tx_valid')
16
16
  var txInvalid = require('../data/bitcoind/tx_invalid')
17
17
 
18
+ /**
19
+ * Bitcoin Core names for the bytes BSV reassigned at Chronicle.
20
+ *
21
+ * The vendored script_tests.json is a faithful upstream copy and is never edited, so it
22
+ * still says NOP4..NOP8 — names this library no longer defines, because 0xb3-0xb7 now
23
+ * carry OP_SUBSTR/OP_LEFT/OP_RIGHT/OP_LSHIFTNUM/OP_RSHIFTNUM.
24
+ *
25
+ * Resolving them to Core's BYTES keeps each vector assembling the exact script Core
26
+ * meant, so the comparison stays honest: same bytes in, different BSV verdict out.
27
+ * Letting them fail to parse would turn a documented consensus divergence into an
28
+ * opaque harness error.
29
+ */
30
+ var CORE_ONLY_OPCODES = {
31
+ OP_NOP4: 0xb3,
32
+ OP_NOP5: 0xb4,
33
+ OP_NOP6: 0xb5,
34
+ OP_NOP7: 0xb6,
35
+ OP_NOP8: 0xb7
36
+ }
37
+
18
38
  // the script string format used in bitcoind data tests
19
39
  Script.fromBitcoindString = function (str) {
20
40
  var bw = new BufferWriter()
@@ -40,6 +60,8 @@ Script.fromBitcoindString = function (str) {
40
60
  opstr = 'OP_' + token
41
61
  opcodenum = Opcode[opstr]
42
62
  bw.writeUInt8(opcodenum)
63
+ } else if (typeof CORE_ONLY_OPCODES['OP_' + token] === 'number') {
64
+ bw.writeUInt8(CORE_ONLY_OPCODES['OP_' + token])
43
65
  } else if (typeof Opcode[token] === 'number') {
44
66
  opstr = token
45
67
  opcodenum = Opcode[opstr]
@@ -618,9 +640,15 @@ describe('Interpreter', function () {
618
640
  // the surrounding `0 IF <op> ELSE 1 ENDIF` form fails under upstream only
619
641
  // because disabled opcodes are rejected even in unexecuted branches; once
620
642
  // enabled, the branch is simply skipped and the script is 'OK'.)
621
- // - 0xba is OP_NOP8 in this build's opcode table (the NOPs are shifted to
622
- // make room for the OP_SUBSTR/OP_LEFT/OP_RIGHT string ops at 0xb3-0xb5;
623
- // see lib/opcode.js), so `1 0xba` is a valid no-op, not 'BAD_OPCODE'.
643
+ // - Chronicle reassigned 0xb3-0xb7 to OP_SUBSTR/OP_LEFT/OP_RIGHT/OP_LSHIFTNUM/
644
+ // OP_RSHIFTNUM, so Core's NOP4..NOP8 no longer exist as no-ops. Vectors that
645
+ // expect those bytes to do nothing now fail here: 0xb3-0xb5 consume stack as
646
+ // string ops (INVALID_STACK_OPERATION), and 0xb6/0xb7 are rejected outright
647
+ // (BAD_OPCODE) because the shift SEMANTICS are deliberately unimplemented —
648
+ // the spec underdetermines them, and validating wrongly is worse than
649
+ // refusing. This is a real consensus divergence from Core, not a quirk.
650
+ // (0xba is no longer a NOP either; it is unassigned, which now AGREES with
651
+ // Core, so its former override has been removed.)
624
652
  var BSV_DIVERGENCES = {
625
653
  "'a' 'b'|CAT|P2SH,STRICTENC": 'OK',
626
654
  "'a' 'b' 0|IF CAT ELSE 1 ENDIF|P2SH,STRICTENC": 'OK',
@@ -635,7 +663,18 @@ describe('Interpreter', function () {
635
663
  '2 2 0 IF MOD ELSE 1 ENDIF|NOP|P2SH,STRICTENC': 'OK',
636
664
  '2 DUP DIV|1 EQUAL|P2SH,STRICTENC': 'OK',
637
665
  '7 3 MOD|1 EQUAL|P2SH,STRICTENC': 'OK',
638
- '1|0xba|P2SH,STRICTENC': 'OK'
666
+
667
+ // Chronicle byte reassignment (see the note above). Values are the BSV error,
668
+ // recorded for documentation; the harness only distinguishes 'OK' from not-'OK'.
669
+ '1|NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL|P2SH,STRICTENC': 'SCRIPT_ERR_INVALID_STACK_OPERATION',
670
+ "'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10|'NOP_1_to_10' EQUAL|P2SH,STRICTENC": 'SCRIPT_ERR_INVALID_STACK_OPERATION',
671
+ 'NOP|NOP4 1|P2SH,STRICTENC': 'SCRIPT_ERR_INVALID_STACK_OPERATION',
672
+ 'NOP|NOP5 1|P2SH,STRICTENC': 'SCRIPT_ERR_INVALID_STACK_OPERATION',
673
+ 'NOP|NOP6 1|P2SH,STRICTENC': 'SCRIPT_ERR_INVALID_STACK_OPERATION',
674
+ 'NOP|NOP7 1|P2SH,STRICTENC': 'SCRIPT_ERR_BAD_OPCODE',
675
+ 'NOP|NOP8 1|P2SH,STRICTENC': 'SCRIPT_ERR_BAD_OPCODE',
676
+ '0x47 0x3044022018a2a81a93add5cb5f5da76305718e4ea66045ec4888b28d84cb22fae7f4645b02201e6daa5ed5d2e4b2b2027cf7ffd43d8d9844dd49f74ef86899ec8e669dfd39aa01 NOP8 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac|HASH160 0x14 0x215640c2f72f0d16b4eced26762035a42ffed39a EQUAL|': 'SCRIPT_ERR_BAD_OPCODE',
677
+ '0x47 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb125101 NOP8|0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG|': 'SCRIPT_ERR_BAD_OPCODE'
639
678
  }
640
679
 
641
680
  describe('bitcoind script evaluation fixtures', function () {
@@ -701,10 +701,11 @@ describe('Script', function () {
701
701
 
702
702
  describe('#add and #prepend', function () {
703
703
  it('should add these ops', function () {
704
- // 186 (0xba) is OP_NOP8 in this build's opcode table — the NOPs are
705
- // shifted up by 3 to make room for the OP_SUBSTR/OP_LEFT/OP_RIGHT string
706
- // ops at 0xb3-0xb5 (see lib/opcode.js) — so it disassembles by name.
707
- Script().add(1).add(10).add(186).toString().should.equal('0x01 0x0a OP_NOP8')
704
+ // 185 (0xb9) is OP_NOP10, the highest byte Chronicle still assigns, so it
705
+ // disassembles by name. 186 is unassigned and renders as a raw byte — the
706
+ // NOP names are no longer slid upward past 185 (see lib/opcode.js).
707
+ Script().add(1).add(10).add(185).toString().should.equal('0x01 0x0a OP_NOP10')
708
+ Script().add(1).add(10).add(186).toString().should.equal('0x01 0x0a 0xba')
708
709
  Script().add(1000).toString().should.equal('0x03e8')
709
710
  Script().add('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG')
710
711
  Script().add('OP_1').add('OP_2').toString().should.equal('OP_1 OP_2')
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  'use strict'
2
2
  // GENERATED by scripts/sync-version.js on `npm version` — do not edit.
3
- module.exports = '7.5.8'
3
+ module.exports = '7.6.1'