@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.
- package/CHANGELOG.md +108 -0
- package/README.md +38 -38
- package/bsv-gdaf.min.js +38 -38
- package/bsv-ltp.min.js +38 -38
- package/bsv-smartcontract.min.js +8 -8
- package/bsv.bundle.js +38 -38
- package/bsv.min.js +38 -38
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/lib/opcode.js +26 -10
- package/lib/script/interpreter.js +9 -6
- package/lib/smart_contract/opcode_map.js +9 -36
- package/lib/smart_contract/pushtx.js +22 -2
- package/lib/transaction/transaction.js +1 -1
- package/package.json +1 -1
- package/test/opcode.js +28 -3
- package/test/regressions.js +180 -0
- package/test/script/interpreter.js +43 -4
- package/test/script/script.js +5 -4
- package/version.js +1 -1
|
@@ -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
|
-
// -
|
|
622
|
-
//
|
|
623
|
-
//
|
|
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
|
-
|
|
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 () {
|
package/test/script/script.js
CHANGED
|
@@ -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
|
-
//
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
Script().add(1).add(10).add(
|
|
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