@smartledger/bsv 7.4.0 → 7.5.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 +128 -0
- package/README.md +38 -38
- package/bsv-gdaf.min.js +33 -33
- package/bsv-ltp.min.js +33 -33
- package/bsv-smartcontract.min.js +1 -1
- package/bsv.bundle.js +33 -33
- package/bsv.d.ts +63 -16
- package/bsv.min.js +33 -33
- 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/gdaf/index.js +29 -6
- package/lib/gdaf/smartledger-anchor.js +31 -1
- package/lib/privatekey.js +59 -10
- package/lib/script/interpreter.js +47 -6
- package/package.json +1 -1
- package/test/gdaf/anchor_no_key_leak.js +88 -0
- package/test/ordinals/inscription.js +94 -0
- package/test/ordinals/ordlock.js +10 -20
- package/test/privatekey.js +79 -2
- package/test/script/genesis_limits.js +44 -8
- package/test/smart_contract/covenants.js +2 -4
- package/test/smart_contract/dsl_debugger.js +2 -4
- package/test/smart_contract/ordinal_transfer.js +2 -4
- package/test/smart_contract/sighash_marketplace.js +2 -4
- package/test/smart_contract/token_generalized.js +2 -4
- package/version.js +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* global describe, it */
|
|
4
|
+
|
|
5
|
+
// The GDAF anchoring wrappers take (payload, privateKey, options) while the underlying
|
|
6
|
+
// SmartLedgerAnchor methods take (payload, metadata, utxos). Every wrapper forwarded
|
|
7
|
+
// `privateKey` into a slot that is not a key. For anchorCredential/anchorBatch it landed
|
|
8
|
+
// in `metadata`, which is JSON.stringify-ed straight into the OP_RETURN — so the secret
|
|
9
|
+
// scalar was broadcast, and the key was recoverable from chain data with an identical WIF.
|
|
10
|
+
|
|
11
|
+
require('chai').should()
|
|
12
|
+
var bsv = require('../..')
|
|
13
|
+
|
|
14
|
+
describe('GDAF anchoring never publishes key material', function () {
|
|
15
|
+
var key = bsv.PrivateKey.fromBuffer(Buffer.alloc(32, 7))
|
|
16
|
+
var utxos = [{
|
|
17
|
+
txId: '11'.repeat(32),
|
|
18
|
+
outputIndex: 0,
|
|
19
|
+
script: bsv.Script.buildPublicKeyHashOut(key.toAddress()).toHex(),
|
|
20
|
+
satoshis: 100000
|
|
21
|
+
}]
|
|
22
|
+
var HASH = 'ab'.repeat(32)
|
|
23
|
+
|
|
24
|
+
function gdaf () { return new bsv.GDAF() }
|
|
25
|
+
|
|
26
|
+
// Pull every OP_RETURN payload out of a built transaction, as UTF-8.
|
|
27
|
+
function payloadsOf (result) {
|
|
28
|
+
var tx = new bsv.Transaction((result.transaction || result.tx || result).toString())
|
|
29
|
+
return tx.outputs.map(function (o) {
|
|
30
|
+
var asm = o.script.toASM().split(' ')
|
|
31
|
+
return Buffer.from(asm[asm.length - 1], 'hex').toString('utf8')
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertNoSecret (result) {
|
|
36
|
+
var scalarHex = key.bn.toString('hex')
|
|
37
|
+
var raw = (result.transaction || result.tx || result).toString()
|
|
38
|
+
raw.indexOf(Buffer.from(scalarHex, 'utf8').toString('hex')).should.equal(-1)
|
|
39
|
+
raw.indexOf(Buffer.from('"bn"', 'utf8').toString('hex')).should.equal(-1)
|
|
40
|
+
payloadsOf(result).forEach(function (p) {
|
|
41
|
+
p.indexOf(scalarHex).should.equal(-1)
|
|
42
|
+
p.indexOf('"bn"').should.equal(-1)
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
it('anchorCredential does not put the key in the OP_RETURN', function () {
|
|
47
|
+
return gdaf().anchorCredential(HASH, key, utxos).then(assertNoSecret)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('anchorBatch does not put the key in the OP_RETURN', function () {
|
|
51
|
+
return gdaf().anchorBatch([HASH, 'cd'.repeat(32)], key, utxos).then(assertNoSecret)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('accepts the documented { utxos, metadata } options shape', function () {
|
|
55
|
+
return gdaf().anchorCredential(HASH, key, { utxos: utxos, metadata: { issuer: 'did:web:example' } })
|
|
56
|
+
.then(function (r) {
|
|
57
|
+
assertNoSecret(r)
|
|
58
|
+
// The caller's own metadata still reaches the chain.
|
|
59
|
+
payloadsOf(r).join('').indexOf('did:web:example').should.not.equal(-1)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('still accepts a bare UTXO array, the only shape that used to work', function () {
|
|
64
|
+
return gdaf().anchorCredential(HASH, key, utxos).then(function (r) {
|
|
65
|
+
r.should.be.an('object')
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Defence in depth: even reaching the payload builder with key material must fail
|
|
70
|
+
// loudly rather than broadcast it.
|
|
71
|
+
it('refuses to anchor metadata containing a PrivateKey instance', function () {
|
|
72
|
+
return gdaf().anchorCredential(HASH, key, { utxos: utxos, metadata: { signer: key } })
|
|
73
|
+
.then(function () { throw new Error('should not have anchored') },
|
|
74
|
+
function (err) { err.message.should.match(/Refusing to anchor/) })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('refuses to anchor metadata carrying a key-shaped field', function () {
|
|
78
|
+
return gdaf().anchorCredential(HASH, key, { utxos: utxos, metadata: { wif: key.toWIF() } })
|
|
79
|
+
.then(function () { throw new Error('should not have anchored') },
|
|
80
|
+
function (err) { err.message.should.match(/looks like key material/) })
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('leaves ordinary metadata alone', function () {
|
|
84
|
+
return gdaf().anchorCredential(HASH, key, {
|
|
85
|
+
utxos: utxos, metadata: { id: 'urn:uuid:1', nested: { note: 'fine' } }
|
|
86
|
+
}).then(function (r) { assertNoSecret(r) })
|
|
87
|
+
})
|
|
88
|
+
})
|
|
@@ -222,4 +222,98 @@ describe('Ordinals inscriptions', function () {
|
|
|
222
222
|
}).satoshis.should.equal(1)
|
|
223
223
|
})
|
|
224
224
|
})
|
|
225
|
+
|
|
226
|
+
// The case that motivated making the script-size cap configurable: transferring a
|
|
227
|
+
// large inscription. The envelope is inert, so the spend is an ordinary P2PKH — but
|
|
228
|
+
// the interpreter could not even load the script, because total script size was a
|
|
229
|
+
// hard-coded 10,000 bytes that useGenesisLimits() did not lift.
|
|
230
|
+
describe('transferring a large inscription', function () {
|
|
231
|
+
var I = bsv.Script.Interpreter
|
|
232
|
+
var Sighash = bsv.Transaction.Sighash
|
|
233
|
+
var BN = bsv.crypto.BN
|
|
234
|
+
var SIGHASH = bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID
|
|
235
|
+
var FLAGS = I.SCRIPT_VERIFY_STRICTENC | I.SCRIPT_ENABLE_SIGHASH_FORKID
|
|
236
|
+
var saved
|
|
237
|
+
|
|
238
|
+
beforeEach(function () { saved = I.getLimits() })
|
|
239
|
+
afterEach(function () { I.setLimits(saved) })
|
|
240
|
+
|
|
241
|
+
// A signed transfer of a `kb`-sized inscription to a new owner.
|
|
242
|
+
function transfer (kb) {
|
|
243
|
+
var owner = bsv.PrivateKey.fromBuffer(Buffer.alloc(32, 11))
|
|
244
|
+
var recipient = bsv.PrivateKey.fromBuffer(Buffer.alloc(32, 22))
|
|
245
|
+
var lock = Ord.buildInscription({
|
|
246
|
+
address: owner.toAddress(),
|
|
247
|
+
contentType: 'image/png',
|
|
248
|
+
content: bsv.crypto.Random.getRandomBuffer(kb * 1024)
|
|
249
|
+
})
|
|
250
|
+
var spend = new bsv.Transaction()
|
|
251
|
+
spend.addInput(new bsv.Transaction.Input({
|
|
252
|
+
prevTxId: 'aa'.repeat(32), outputIndex: 0, script: bsv.Script.empty()
|
|
253
|
+
}), lock, 1)
|
|
254
|
+
spend.addOutput(new bsv.Transaction.Output({
|
|
255
|
+
script: bsv.Script.buildPublicKeyHashOut(recipient.toAddress()), satoshis: 1
|
|
256
|
+
}))
|
|
257
|
+
// Sign over the FULL previous locking script — envelope included. That is the
|
|
258
|
+
// script code the network uses; signing the base lock alone yields a signature
|
|
259
|
+
// that verifies against its own preimage and nothing else.
|
|
260
|
+
var sig = Sighash.sign(spend, owner, SIGHASH, 0, lock, new BN(1), FLAGS)
|
|
261
|
+
var unlock = new bsv.Script()
|
|
262
|
+
.add(Buffer.concat([sig.toDER(), Buffer.from([SIGHASH])]))
|
|
263
|
+
.add(owner.toPublicKey().toBuffer())
|
|
264
|
+
spend.inputs[0].setScript(unlock)
|
|
265
|
+
return { lock: lock, unlock: unlock, spend: spend, sig: sig, owner: owner }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function evaluate (t) {
|
|
269
|
+
var interp = new I()
|
|
270
|
+
var ok = interp.verify(t.unlock, t.lock, t.spend, 0, FLAGS, new BN(1))
|
|
271
|
+
return { ok: ok, err: interp.errstr }
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
it('cannot be evaluated under pre-Genesis limits — two separate caps bite', function () {
|
|
275
|
+
// Under 10 KB the script loads and the 520-byte push cap rejects the content...
|
|
276
|
+
var small = evaluate(transfer(3))
|
|
277
|
+
small.ok.should.equal(false)
|
|
278
|
+
small.err.should.equal('SCRIPT_ERR_PUSH_SIZE')
|
|
279
|
+
// ...over 10 KB it never gets that far: evaluate() refuses the script outright.
|
|
280
|
+
// This is the cap useGenesisLimits() could not lift.
|
|
281
|
+
var big = evaluate(transfer(50))
|
|
282
|
+
big.ok.should.equal(false)
|
|
283
|
+
big.err.should.equal('SCRIPT_ERR_SCRIPT_SIZE')
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('evaluates once Genesis limits are enabled', function () {
|
|
287
|
+
bsv.SmartContract.enableGenesis()
|
|
288
|
+
var t = transfer(50)
|
|
289
|
+
t.lock.toBuffer().length.should.be.above(10000) // past the old hard-coded cap
|
|
290
|
+
evaluate(t).ok.should.equal(true)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('agrees with a direct signature check against the sighash', function () {
|
|
294
|
+
// The fallback used when the interpreter cannot run: verify the signature over
|
|
295
|
+
// the sighash directly. It must agree with the interpreter where both work.
|
|
296
|
+
bsv.SmartContract.enableGenesis()
|
|
297
|
+
var t = transfer(50)
|
|
298
|
+
var hash = Sighash.sighash(t.spend, SIGHASH, 0, t.lock, new BN(1), FLAGS)
|
|
299
|
+
var sigOk = bsv.crypto.ECDSA.verify(hash, t.sig, t.owner.toPublicKey(), 'little')
|
|
300
|
+
sigOk.should.equal(true)
|
|
301
|
+
evaluate(t).ok.should.equal(true)
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('rejects a signature made over the base lock instead of the full script', function () {
|
|
305
|
+
bsv.SmartContract.enableGenesis()
|
|
306
|
+
var t = transfer(50)
|
|
307
|
+
var baseLock = bsv.Script.buildPublicKeyHashOut(t.owner.toAddress())
|
|
308
|
+
var wrong = Sighash.sign(t.spend, t.owner, SIGHASH, 0, baseLock, new BN(1), FLAGS)
|
|
309
|
+
var hash = Sighash.sighash(t.spend, SIGHASH, 0, t.lock, new BN(1), FLAGS)
|
|
310
|
+
// Verifies against its own (wrong) preimage, but not the one the network checks.
|
|
311
|
+
bsv.crypto.ECDSA.verify(hash, wrong, t.owner.toPublicKey(), 'little').should.equal(false)
|
|
312
|
+
t.spend.inputs[0].setScript(new bsv.Script()
|
|
313
|
+
.add(Buffer.concat([wrong.toDER(), Buffer.from([SIGHASH])]))
|
|
314
|
+
.add(t.owner.toPublicKey().toBuffer()))
|
|
315
|
+
var interp = new I()
|
|
316
|
+
interp.verify(t.spend.inputs[0].script, t.lock, t.spend, 0, FLAGS, new BN(1)).should.equal(false)
|
|
317
|
+
})
|
|
318
|
+
})
|
|
225
319
|
})
|
package/test/ordinals/ordlock.js
CHANGED
|
@@ -28,13 +28,11 @@ describe('Ordinals OrdLock marketplace', function () {
|
|
|
28
28
|
var saved
|
|
29
29
|
|
|
30
30
|
before(function () {
|
|
31
|
-
saved =
|
|
31
|
+
saved = I.getLimits()
|
|
32
32
|
SC.enableGenesis()
|
|
33
33
|
})
|
|
34
34
|
after(function () {
|
|
35
|
-
I.
|
|
36
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
37
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
35
|
+
I.setLimits(saved)
|
|
38
36
|
})
|
|
39
37
|
|
|
40
38
|
var seller = PrivateKey.fromRandom()
|
|
@@ -173,13 +171,11 @@ describe('Ordinals OrdLock — multi-output payments (royalties + marketplace fe
|
|
|
173
171
|
var I = bsv.Script.Interpreter
|
|
174
172
|
var saved
|
|
175
173
|
before(function () {
|
|
176
|
-
saved =
|
|
174
|
+
saved = I.getLimits()
|
|
177
175
|
SC.enableGenesis()
|
|
178
176
|
})
|
|
179
177
|
after(function () {
|
|
180
|
-
I.
|
|
181
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
182
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
178
|
+
I.setLimits(saved)
|
|
183
179
|
})
|
|
184
180
|
|
|
185
181
|
var seller = PrivateKey.fromRandom()
|
|
@@ -290,13 +286,11 @@ describe('Ordinals OrdLock — buildPurchaseTx (end-to-end)', function () {
|
|
|
290
286
|
var I = bsv.Script.Interpreter
|
|
291
287
|
var saved
|
|
292
288
|
before(function () {
|
|
293
|
-
saved =
|
|
289
|
+
saved = I.getLimits()
|
|
294
290
|
SC.enableGenesis()
|
|
295
291
|
})
|
|
296
292
|
after(function () {
|
|
297
|
-
I.
|
|
298
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
299
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
293
|
+
I.setLimits(saved)
|
|
300
294
|
})
|
|
301
295
|
|
|
302
296
|
var seller = PrivateKey.fromRandom()
|
|
@@ -386,13 +380,11 @@ describe('Ordinals OrdLock — full lifecycle (list -> buy / cancel)', function
|
|
|
386
380
|
var I = bsv.Script.Interpreter
|
|
387
381
|
var saved
|
|
388
382
|
before(function () {
|
|
389
|
-
saved =
|
|
383
|
+
saved = I.getLimits()
|
|
390
384
|
SC.enableGenesis()
|
|
391
385
|
})
|
|
392
386
|
after(function () {
|
|
393
|
-
I.
|
|
394
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
395
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
387
|
+
I.setLimits(saved)
|
|
396
388
|
})
|
|
397
389
|
|
|
398
390
|
var owner = PrivateKey.fromRandom() // current ordinal owner / seller
|
|
@@ -486,13 +478,11 @@ describe('Ordinals OrdLock — code-review regressions', function () {
|
|
|
486
478
|
var I = bsv.Script.Interpreter
|
|
487
479
|
var saved
|
|
488
480
|
before(function () {
|
|
489
|
-
saved =
|
|
481
|
+
saved = I.getLimits()
|
|
490
482
|
SC.enableGenesis()
|
|
491
483
|
})
|
|
492
484
|
after(function () {
|
|
493
|
-
I.
|
|
494
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
495
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
485
|
+
I.setLimits(saved)
|
|
496
486
|
})
|
|
497
487
|
|
|
498
488
|
var owner = PrivateKey.fromRandom()
|
package/test/privatekey.js
CHANGED
|
@@ -205,7 +205,13 @@ describe('PrivateKey', function () {
|
|
|
205
205
|
network: 'livenet'
|
|
206
206
|
})
|
|
207
207
|
var key = PrivateKey.fromObject(JSON.parse(json))
|
|
208
|
-
|
|
208
|
+
// toObject() is the deliberate export and still round-trips exactly...
|
|
209
|
+
JSON.stringify(key.toObject()).should.equal(json)
|
|
210
|
+
// ...but JSON.stringify() must NOT emit the secret. This test used to assert that
|
|
211
|
+
// it did, which is how a private key reached an OP_RETURN via the GDAF anchor path:
|
|
212
|
+
// anything that stringified an object holding a key published the scalar.
|
|
213
|
+
JSON.parse(JSON.stringify(key)).bn.should.equal('[REDACTED]')
|
|
214
|
+
JSON.stringify(key).indexOf('96c13222').should.equal(-1)
|
|
209
215
|
})
|
|
210
216
|
|
|
211
217
|
it('should input/output json', function () {
|
|
@@ -215,7 +221,13 @@ describe('PrivateKey', function () {
|
|
|
215
221
|
network: 'livenet'
|
|
216
222
|
})
|
|
217
223
|
var key = PrivateKey.fromJSON(JSON.parse(json))
|
|
218
|
-
|
|
224
|
+
// toObject() is the deliberate export and still round-trips exactly...
|
|
225
|
+
JSON.stringify(key.toObject()).should.equal(json)
|
|
226
|
+
// ...but JSON.stringify() must NOT emit the secret. This test used to assert that
|
|
227
|
+
// it did, which is how a private key reached an OP_RETURN via the GDAF anchor path:
|
|
228
|
+
// anything that stringified an object holding a key published the scalar.
|
|
229
|
+
JSON.parse(JSON.stringify(key)).bn.should.equal('[REDACTED]')
|
|
230
|
+
JSON.stringify(key).indexOf('96c13222').should.equal(-1)
|
|
219
231
|
})
|
|
220
232
|
|
|
221
233
|
it('input json should correctly initialize network field', function () {
|
|
@@ -427,4 +439,69 @@ describe('PrivateKey', function () {
|
|
|
427
439
|
var privkey = new PrivateKey('92VYMmwFLXRwXn5688edGxYYgMFsc3fUXYhGp17WocQhU6zG1kd')
|
|
428
440
|
privkey.publicKey.toAddress().toString().should.equal('moiAvLUw16qgrwhFGo1eDnXHC2wPMYiv7Y')
|
|
429
441
|
})
|
|
442
|
+
// Reported from the field against 7.4.0 and reproduced here. Each of these returned a
|
|
443
|
+
// DIFFERENT key or address than the caller asked for, silently.
|
|
444
|
+
describe('argument handling that used to produce the wrong key', function () {
|
|
445
|
+
var HEX = '0000000000000000000000000000000000000000000000000000000000000001'
|
|
446
|
+
|
|
447
|
+
it('honours the network passed to fromString instead of discarding it', function () {
|
|
448
|
+
// Returned a livenet key — and therefore a MAINNET address — so funds sent to it
|
|
449
|
+
// landed on the wrong network.
|
|
450
|
+
var k = PrivateKey.fromString(HEX, 'testnet')
|
|
451
|
+
k.network.name.should.equal('testnet')
|
|
452
|
+
k.toAddress().toString().should.equal(new PrivateKey(HEX, 'testnet').toAddress().toString())
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
it('agrees with the constructor for WIF input', function () {
|
|
456
|
+
var wif = new PrivateKey(HEX, 'testnet').toWIF()
|
|
457
|
+
PrivateKey.fromString(wif).toWIF().should.equal(wif)
|
|
458
|
+
PrivateKey.fromString(wif, 'testnet').toWIF().should.equal(wif)
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
it('rejects a network that contradicts the WIF rather than ignoring it', function () {
|
|
462
|
+
var wif = new PrivateKey(HEX, 'testnet').toWIF()
|
|
463
|
+
;(function () { PrivateKey.fromString(wif, 'livenet') }).should.throw()
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it('fromHex and the constructor return the SAME key', function () {
|
|
467
|
+
// These disagreed on the compression flag, so the same input produced two different
|
|
468
|
+
// addresses and two different WIFs. Restore by the wrong route, derive an address
|
|
469
|
+
// you never funded.
|
|
470
|
+
var a = new PrivateKey(HEX)
|
|
471
|
+
var b = PrivateKey.fromHex(HEX)
|
|
472
|
+
var c = PrivateKey.fromBuffer(Buffer.from(HEX, 'hex'))
|
|
473
|
+
b.compressed.should.equal(a.compressed)
|
|
474
|
+
b.toWIF().should.equal(a.toWIF())
|
|
475
|
+
b.toAddress().toString().should.equal(a.toAddress().toString())
|
|
476
|
+
c.toWIF().should.equal(a.toWIF())
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
it('still allows the legacy uncompressed form explicitly', function () {
|
|
480
|
+
PrivateKey.fromHex(HEX, null, false).compressed.should.equal(false)
|
|
481
|
+
PrivateKey.fromBuffer(Buffer.from(HEX, 'hex'), null, false).compressed.should.equal(false)
|
|
482
|
+
})
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
// JSON.stringify() on a key — or on any object holding one — used to emit the secret
|
|
486
|
+
// scalar. That is how a private key reached an OP_RETURN through the GDAF anchor path.
|
|
487
|
+
describe('does not leak the secret through JSON', function () {
|
|
488
|
+
it('redacts bn from JSON.stringify', function () {
|
|
489
|
+
var k = new PrivateKey()
|
|
490
|
+
var scalar = k.bn.toString('hex')
|
|
491
|
+
JSON.stringify(k).indexOf(scalar).should.equal(-1)
|
|
492
|
+
JSON.parse(JSON.stringify(k)).bn.should.equal('[REDACTED]')
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
it('redacts it when the key is nested inside another object', function () {
|
|
496
|
+
var k = new PrivateKey()
|
|
497
|
+
var payload = JSON.stringify({ context: { signer: k } })
|
|
498
|
+
payload.indexOf(k.bn.toString('hex')).should.equal(-1)
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
it('keeps toObject() exact, so deliberate export still round-trips', function () {
|
|
502
|
+
var k = new PrivateKey()
|
|
503
|
+
k.toObject().bn.should.equal(k.bn.toString('hex'))
|
|
504
|
+
PrivateKey.fromObject(k.toObject()).toWIF().should.equal(k.toWIF())
|
|
505
|
+
})
|
|
506
|
+
})
|
|
430
507
|
})
|
|
@@ -15,16 +15,10 @@ describe('Interpreter post-Genesis limits', function () {
|
|
|
15
15
|
var saved
|
|
16
16
|
|
|
17
17
|
beforeEach(function () {
|
|
18
|
-
saved =
|
|
19
|
-
el: Interpreter.MAX_SCRIPT_ELEMENT_SIZE,
|
|
20
|
-
num: Interpreter.MAXIMUM_ELEMENT_SIZE,
|
|
21
|
-
ops: Interpreter.MAX_OPS_PER_SCRIPT
|
|
22
|
-
}
|
|
18
|
+
saved = Interpreter.getLimits()
|
|
23
19
|
})
|
|
24
20
|
afterEach(function () {
|
|
25
|
-
Interpreter.
|
|
26
|
-
Interpreter.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
27
|
-
Interpreter.MAX_OPS_PER_SCRIPT = saved.ops
|
|
21
|
+
Interpreter.setLimits(saved)
|
|
28
22
|
})
|
|
29
23
|
|
|
30
24
|
it('keeps pre-Genesis defaults out of the box', function () {
|
|
@@ -77,4 +71,46 @@ describe('Interpreter post-Genesis limits', function () {
|
|
|
77
71
|
Interpreter.useGenesisLimits()
|
|
78
72
|
run(manyOps).should.equal(true)
|
|
79
73
|
})
|
|
74
|
+
// Total script size was a hard-coded 10,000 in evaluate(), so useGenesisLimits()
|
|
75
|
+
// could not lift it: any script over 10 KB failed SCRIPT_ERR_SCRIPT_SIZE regardless
|
|
76
|
+
// of what the caller asked for. That put every sizeable 1Sat Ordinals inscription
|
|
77
|
+
// out of reach of the interpreter entirely.
|
|
78
|
+
function scriptOfSize (bytes) {
|
|
79
|
+
// OP_DROP a single large push, then OP_1 — trivially true, but big.
|
|
80
|
+
return new Script()
|
|
81
|
+
.add(Buffer.alloc(bytes))
|
|
82
|
+
.add(Opcode.OP_DROP)
|
|
83
|
+
.add(Opcode.OP_1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
it('exposes the total script size cap as a constant, defaulting to pre-Genesis', function () {
|
|
87
|
+
Interpreter.MAX_SCRIPT_SIZE.should.equal(10000)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('rejects a >10 KB script under default limits', function () {
|
|
91
|
+
Interpreter.useGenesisLimits() // lift element/ops caps so SIZE is what is under test
|
|
92
|
+
Interpreter.MAX_SCRIPT_SIZE = 10000 // ...but keep the pre-Genesis size cap
|
|
93
|
+
var interp = new Interpreter()
|
|
94
|
+
interp.verify(new Script(), scriptOfSize(20 * 1024)).should.equal(false)
|
|
95
|
+
interp.errstr.should.equal('SCRIPT_ERR_SCRIPT_SIZE')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('allows a >10 KB script after useGenesisLimits()', function () {
|
|
99
|
+
Interpreter.useGenesisLimits()
|
|
100
|
+
run(scriptOfSize(20 * 1024)).should.equal(true)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('useGenesisLimits raises all four caps, and getLimits/setLimits round-trip', function () {
|
|
104
|
+
var before = Interpreter.getLimits()
|
|
105
|
+
before.maxScriptSize.should.equal(10000)
|
|
106
|
+
Interpreter.useGenesisLimits(64 * 1024)
|
|
107
|
+
var after = Interpreter.getLimits()
|
|
108
|
+
after.maxScriptElementSize.should.equal(64 * 1024)
|
|
109
|
+
after.maximumElementSize.should.equal(64 * 1024)
|
|
110
|
+
after.maxOpsPerScript.should.equal(64 * 1024)
|
|
111
|
+
after.maxScriptSize.should.equal(64 * 1024)
|
|
112
|
+
Interpreter.setLimits(before)
|
|
113
|
+
Interpreter.getLimits().maxScriptSize.should.equal(10000)
|
|
114
|
+
Interpreter.MAX_SCRIPT_ELEMENT_SIZE.should.equal(520)
|
|
115
|
+
})
|
|
80
116
|
})
|
|
@@ -24,13 +24,11 @@ describe('SmartContract covenants (v4.2.0)', function () {
|
|
|
24
24
|
var saved
|
|
25
25
|
|
|
26
26
|
before(function () {
|
|
27
|
-
saved =
|
|
27
|
+
saved = I.getLimits()
|
|
28
28
|
SC.enableGenesis() // OP_PUSH_TX covenants need post-Genesis limits
|
|
29
29
|
})
|
|
30
30
|
after(function () {
|
|
31
|
-
I.
|
|
32
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
33
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
31
|
+
I.setLimits(saved)
|
|
34
32
|
})
|
|
35
33
|
|
|
36
34
|
var alice = PrivateKey.fromRandom()
|
|
@@ -17,13 +17,11 @@ describe('SmartContract covenant DSL + debugger (v4.5.0)', function () {
|
|
|
17
17
|
var I = bsv.Script.Interpreter
|
|
18
18
|
var saved
|
|
19
19
|
before(function () {
|
|
20
|
-
saved =
|
|
20
|
+
saved = I.getLimits()
|
|
21
21
|
SC.enableGenesis()
|
|
22
22
|
})
|
|
23
23
|
after(function () {
|
|
24
|
-
I.
|
|
25
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
26
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
24
|
+
I.setLimits(saved)
|
|
27
25
|
})
|
|
28
26
|
|
|
29
27
|
var alice = PrivateKey.fromRandom()
|
|
@@ -29,13 +29,11 @@ describe('Ordinal-safe transfer (1-sat, SIGHASH_ALL)', function () {
|
|
|
29
29
|
var bob = PrivateKey.fromRandom()
|
|
30
30
|
|
|
31
31
|
before(function () {
|
|
32
|
-
saved =
|
|
32
|
+
saved = I.getLimits()
|
|
33
33
|
SC.enableGenesis()
|
|
34
34
|
})
|
|
35
35
|
after(function () {
|
|
36
|
-
I.
|
|
37
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
38
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
36
|
+
I.setLimits(saved)
|
|
39
37
|
})
|
|
40
38
|
|
|
41
39
|
it('transferOrdinal recreates the 1-sat token among funding outputs and verifies', function () {
|
|
@@ -32,13 +32,11 @@ describe('Configurable SIGHASH — SINGLE|ANYONECANPAY marketplace covenant', fu
|
|
|
32
32
|
var flag = P.SIGHASH_SINGLE_ANYONECANPAY_FORKID // 0xc3
|
|
33
33
|
|
|
34
34
|
before(function () {
|
|
35
|
-
saved =
|
|
35
|
+
saved = I.getLimits()
|
|
36
36
|
SC.enableGenesis()
|
|
37
37
|
})
|
|
38
38
|
after(function () {
|
|
39
|
-
I.
|
|
40
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
41
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
39
|
+
I.setLimits(saved)
|
|
42
40
|
})
|
|
43
41
|
|
|
44
42
|
// Lock: authenticate the pushed preimage under `flag`, assert the flag, succeed.
|
|
@@ -24,13 +24,11 @@ describe('SmartContract generalized token (pluggable auth + multi-output)', func
|
|
|
24
24
|
var saved
|
|
25
25
|
|
|
26
26
|
before(function () {
|
|
27
|
-
saved =
|
|
27
|
+
saved = I.getLimits()
|
|
28
28
|
SC.enableGenesis()
|
|
29
29
|
})
|
|
30
30
|
after(function () {
|
|
31
|
-
I.
|
|
32
|
-
I.MAXIMUM_ELEMENT_SIZE = saved.num
|
|
33
|
-
I.MAX_OPS_PER_SCRIPT = saved.ops
|
|
31
|
+
I.setLimits(saved)
|
|
34
32
|
})
|
|
35
33
|
|
|
36
34
|
function spendOf (lock, sats, outputs) {
|
package/version.js
CHANGED