@smartledger/bsv 8.3.1 → 9.1.0
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 +228 -0
- package/README.md +152 -1031
- package/STABILITY.md +134 -0
- package/bsv-gdaf.min.js +58 -53
- package/bsv-ltp.min.js +23 -18
- package/bsv-script-helper.min.js +1 -1
- package/bsv-smartcontract.min.js +21 -21
- package/bsv.bundle.js +58 -53
- package/bsv.d.ts +0 -4
- package/bsv.min.js +58 -53
- package/docs/BRC220_BATCH_LEAF_AMENDMENT.md +82 -7
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/README.md +2 -1
- package/docs/THREAT_MODEL.md +10 -2
- 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/docs/proposals/10.0.0-package-split.md +131 -0
- package/index.js +15 -26
- package/lib/block/merkleblock.js +19 -5
- package/lib/covenant/helpers.js +34 -35
- package/lib/covenant/pushtx.js +2 -2
- package/lib/custom-script-helper.js +0 -16
- package/lib/hdprivatekey.js +15 -0
- package/lib/ordinals/ordlock.js +1 -1
- package/lib/script/interpreter.js +109 -1
- package/lib/smart_contract/builder.js +14 -0
- package/lib/smart_contract/debugger.js +2 -2
- package/lib/smart_contract/dsl.js +2 -2
- package/lib/smart_contract/index.js +0 -3
- package/lib/smart_contract/locks.js +1 -41
- package/lib/smart_contract/pels.js +1 -1
- package/lib/smart_contract/token.js +1 -1
- package/lib/util/deprecate.js +159 -0
- package/package.json +19 -82
- package/tools/gen-brc220-batch-vector.js +205 -0
- package/version.js +1 -1
- package/.mocharc.json +0 -5
- package/test/address.js +0 -629
- package/test/block/block.js +0 -239
- package/test/block/blockheader.js +0 -270
- package/test/block/merkleblock.js +0 -207
- package/test/build/bundle_crypto_shim.js +0 -104
- package/test/build/bundle_externals.js +0 -125
- package/test/build/bundle_smoke.js +0 -55
- package/test/build/esbuild_main.js +0 -65
- package/test/build/esm_wrapper.js +0 -73
- package/test/build/exports_resolution.js +0 -84
- package/test/build/version_sync.js +0 -14
- package/test/cli/smoke.js +0 -261
- package/test/consensus/base58-vectors.js +0 -120
- package/test/consensus/sv-script-vectors.js +0 -81
- package/test/consensus/sv-sighash-vectors.js +0 -51
- package/test/consensus/sv-tx-vectors.js +0 -48
- package/test/credentials-test.js +0 -332
- package/test/crypto/backend_selection.js +0 -126
- package/test/crypto/bn.js +0 -168
- package/test/crypto/ecdsa.js +0 -426
- package/test/crypto/elliptic-fixed.js +0 -61
- package/test/crypto/hash.browser.js +0 -121
- package/test/crypto/hash.js +0 -122
- package/test/crypto/point.js +0 -193
- package/test/crypto/random.js +0 -105
- package/test/crypto/security.js +0 -176
- package/test/crypto/shamir.js +0 -177
- package/test/crypto/shamir_rngcap.js +0 -29
- package/test/crypto/signature.js +0 -399
- package/test/data/bip69.json +0 -215
- package/test/data/bitcoin-sv/README.md +0 -170
- package/test/data/bitcoin-sv/base58_encode_decode.json +0 -14
- package/test/data/bitcoin-sv/base58_keys_invalid.json +0 -152
- package/test/data/bitcoin-sv/base58_keys_valid.json +0 -452
- package/test/data/bitcoin-sv/script_tests.json +0 -2591
- package/test/data/bitcoin-sv/sighash.json +0 -1003
- package/test/data/bitcoin-sv/tx_invalid.json +0 -285
- package/test/data/bitcoin-sv/tx_valid.json +0 -367
- package/test/data/bitcoind/base58_keys_invalid.json +0 -152
- package/test/data/bitcoind/base58_keys_valid.json +0 -452
- package/test/data/bitcoind/blocks.json +0 -27
- package/test/data/bitcoind/script_tests.json +0 -2244
- package/test/data/bitcoind/sig_canonical.json +0 -7
- package/test/data/bitcoind/sig_noncanonical.json +0 -22
- package/test/data/bitcoind/tx_invalid.json +0 -177
- package/test/data/bitcoind/tx_valid.json +0 -224
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +0 -12
- package/test/data/blk86756-testnet.json +0 -684
- package/test/data/ecdsa.json +0 -230
- package/test/data/merkleblocks.js +0 -486
- package/test/data/messages.json +0 -22
- package/test/data/sighash.json +0 -1004
- package/test/data/tx_creation.json +0 -85
- package/test/didweb/relationships.js +0 -144
- package/test/ecies/bitcore-ecies.js +0 -178
- package/test/ecies/electrum-ecies.js +0 -206
- package/test/encoding/base58.js +0 -131
- package/test/encoding/base58check.js +0 -145
- package/test/encoding/bufferreader.js +0 -328
- package/test/encoding/bufferwriter.js +0 -160
- package/test/encoding/varint.js +0 -104
- package/test/gdaf/anchor_no_key_leak.js +0 -146
- package/test/gdaf/anchor_spv.js +0 -163
- package/test/gdaf/canonicalization.js +0 -106
- package/test/gdaf/canonicalize.js +0 -140
- package/test/gdaf/zk_prover.js +0 -204
- package/test/hdkeys.js +0 -365
- package/test/hdprivatekey.js +0 -339
- package/test/hdpublickey.js +0 -293
- package/test/index.js +0 -16
- package/test/ltp/ids.js +0 -74
- package/test/ltp/right.js +0 -198
- package/test/ltp/verify_failclosed.js +0 -134
- package/test/message/message.js +0 -190
- package/test/mnemonic/data/fixtures.json +0 -300
- package/test/mnemonic/mnemonic.js +0 -277
- package/test/mnemonic/mocha.opts +0 -1
- package/test/mnemonic/pbkdf2.test.js +0 -43
- package/test/networks.js +0 -207
- package/test/notaryhash/batch_leaf.js +0 -140
- package/test/notaryhash/certificate.js +0 -249
- package/test/notaryhash/encoding.js +0 -186
- package/test/notaryhash/interop.js +0 -112
- package/test/notaryhash/merkle.js +0 -181
- package/test/notaryhash/script.js +0 -270
- package/test/notaryhash/verify.js +0 -342
- package/test/opcode.js +0 -186
- package/test/ordinals/bsv20.js +0 -337
- package/test/ordinals/inscription.js +0 -319
- package/test/ordinals/ordlock.js +0 -613
- package/test/privatekey.js +0 -540
- package/test/publickey.js +0 -411
- package/test/regressions.js +0 -215
- package/test/script/chronicle.js +0 -543
- package/test/script/defaults.js +0 -160
- package/test/script/genesis_limits.js +0 -203
- package/test/script/interpreter.js +0 -776
- package/test/script/script.js +0 -1259
- package/test/script/string_ops.js +0 -88
- package/test/security/fail_closed_contracts.js +0 -104
- package/test/security/threat_model_coverage.js +0 -31
- package/test/smart_contract/covenants.js +0 -245
- package/test/smart_contract/dsl_debugger.js +0 -101
- package/test/smart_contract/extract_field.js +0 -61
- package/test/smart_contract/nonenforcing_guard.js +0 -44
- package/test/smart_contract/ordinal_transfer.js +0 -81
- package/test/smart_contract/preimage.js +0 -98
- package/test/smart_contract/sighash_marketplace.js +0 -108
- package/test/smart_contract/token_generalized.js +0 -185
- package/test/spv/headerchain.js +0 -86
- package/test/spv/merkleproof.js +0 -133
- package/test/statuslist/failclosed.js +0 -78
- package/test/transaction/deserialize.js +0 -33
- package/test/transaction/input/input.js +0 -92
- package/test/transaction/input/multisig.js +0 -174
- package/test/transaction/input/multisigscripthash.js +0 -111
- package/test/transaction/input/publickey.js +0 -68
- package/test/transaction/input/publickeyhash.js +0 -59
- package/test/transaction/output.js +0 -185
- package/test/transaction/sighash.js +0 -91
- package/test/transaction/signature.js +0 -127
- package/test/transaction/transaction.js +0 -1299
- package/test/transaction/unspentoutput.js +0 -97
- package/test/types/dts_drift.js +0 -124
- package/test/types/surface_honesty.js +0 -102
- package/test/util/id.js +0 -72
- package/test/util/js.js +0 -76
- package/test/util/preconditions.js +0 -79
- package/test/vcjwt/interop.js +0 -126
package/test/opcode.js
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
var chai = require('chai')
|
|
4
|
-
var should = chai.should()
|
|
5
|
-
var expect = chai.expect
|
|
6
|
-
var bsv = require('..')
|
|
7
|
-
var Opcode = bsv.Opcode
|
|
8
|
-
|
|
9
|
-
describe('Opcode', function () {
|
|
10
|
-
it('should create a new Opcode', function () {
|
|
11
|
-
var opcode = new Opcode(5)
|
|
12
|
-
should.exist(opcode)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should convert to a string with this handy syntax', function () {
|
|
16
|
-
Opcode(0).toString().should.equal('OP_0')
|
|
17
|
-
Opcode(96).toString().should.equal('OP_16')
|
|
18
|
-
Opcode(97).toString().should.equal('OP_NOP')
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should convert to a number with this handy syntax', function () {
|
|
22
|
-
Opcode('OP_0').toNumber().should.equal(0)
|
|
23
|
-
Opcode('OP_16').toNumber().should.equal(96)
|
|
24
|
-
Opcode('OP_NOP').toNumber().should.equal(97)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
describe('#fromNumber', function () {
|
|
28
|
-
it('should work for 0', function () {
|
|
29
|
-
Opcode.fromNumber(0).num.should.equal(0)
|
|
30
|
-
})
|
|
31
|
-
it('should fail for non-number', function () {
|
|
32
|
-
Opcode.fromNumber.bind(null, 'a string').should.throw('Invalid Argument')
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
describe('#set', function () {
|
|
37
|
-
it('should work for object', function () {
|
|
38
|
-
Opcode(42).num.should.equal(42)
|
|
39
|
-
})
|
|
40
|
-
it('should fail for empty-object', function () {
|
|
41
|
-
expect(function () {
|
|
42
|
-
Opcode()
|
|
43
|
-
}).to.throw(TypeError)
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
describe('#toNumber', function () {
|
|
48
|
-
it('should work for 0', function () {
|
|
49
|
-
Opcode.fromNumber(0).toNumber().should.equal(0)
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
describe('#buffer', function () {
|
|
54
|
-
it('should correctly input/output a buffer', function () {
|
|
55
|
-
var buf = Buffer.from('a6', 'hex')
|
|
56
|
-
Opcode.fromBuffer(buf).toBuffer().should.deep.equal(buf)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('#fromString', function () {
|
|
61
|
-
it('should work for OP_0', function () {
|
|
62
|
-
Opcode.fromString('OP_0').num.should.equal(0)
|
|
63
|
-
})
|
|
64
|
-
it('should fail for invalid string', function () {
|
|
65
|
-
Opcode.fromString.bind(null, 'OP_SATOSHI').should.throw('Invalid opcodestr')
|
|
66
|
-
Opcode.fromString.bind(null, 'BANANA').should.throw('Invalid opcodestr')
|
|
67
|
-
})
|
|
68
|
-
it('should fail for non-string', function () {
|
|
69
|
-
Opcode.fromString.bind(null, 123).should.throw('Invalid Argument')
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
describe('#toString', function () {
|
|
74
|
-
it('should work for OP_0', function () {
|
|
75
|
-
Opcode.fromString('OP_0').toString().should.equal('OP_0')
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('should not work for non-opcode', function () {
|
|
79
|
-
expect(function () {
|
|
80
|
-
Opcode('OP_NOTACODE').toString()
|
|
81
|
-
}).to.throw('Opcode does not have a string representation')
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
describe('@map', function () {
|
|
86
|
-
it('should have a map containing 118 elements', function () {
|
|
87
|
-
Object.keys(Opcode.map).length.should.equal(118)
|
|
88
|
-
})
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
describe('@reverseMap', function () {
|
|
92
|
-
it('should exist and have op 185', function () {
|
|
93
|
-
should.exist(Opcode.reverseMap)
|
|
94
|
-
Opcode.reverseMap[185].should.equal('OP_NOP10')
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Chronicle reassigns 179-183 and names which NOP each byte used to be, so
|
|
98
|
-
// OP_NOP4..OP_NOP8 cease to exist and OP_NOP9/OP_NOP10 keep their own numbers.
|
|
99
|
-
// Pinning the whole range by name catches a future edit sliding them again —
|
|
100
|
-
// which is how 182 came to parse as a no-op and verify a shift that never ran.
|
|
101
|
-
it('assigns the Chronicle range exactly, with no invented NOPs above it', function () {
|
|
102
|
-
var expected = {
|
|
103
|
-
179: 'OP_SUBSTR',
|
|
104
|
-
180: 'OP_LEFT',
|
|
105
|
-
181: 'OP_RIGHT',
|
|
106
|
-
182: 'OP_LSHIFTNUM',
|
|
107
|
-
183: 'OP_RSHIFTNUM',
|
|
108
|
-
184: 'OP_NOP9',
|
|
109
|
-
185: 'OP_NOP10'
|
|
110
|
-
}
|
|
111
|
-
Object.keys(expected).forEach(function (b) {
|
|
112
|
-
Opcode.reverseMap[b].should.equal(expected[b])
|
|
113
|
-
})
|
|
114
|
-
;[186, 187, 188].forEach(function (b) {
|
|
115
|
-
should.not.exist(Opcode.reverseMap[b])
|
|
116
|
-
})
|
|
117
|
-
;['OP_NOP4', 'OP_NOP5', 'OP_NOP6', 'OP_NOP7', 'OP_NOP8'].forEach(function (n) {
|
|
118
|
-
should.not.exist(Opcode.map[n])
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
var smallints = [
|
|
123
|
-
Opcode('OP_0'),
|
|
124
|
-
Opcode('OP_1'),
|
|
125
|
-
Opcode('OP_2'),
|
|
126
|
-
Opcode('OP_3'),
|
|
127
|
-
Opcode('OP_4'),
|
|
128
|
-
Opcode('OP_5'),
|
|
129
|
-
Opcode('OP_6'),
|
|
130
|
-
Opcode('OP_7'),
|
|
131
|
-
Opcode('OP_8'),
|
|
132
|
-
Opcode('OP_9'),
|
|
133
|
-
Opcode('OP_10'),
|
|
134
|
-
Opcode('OP_11'),
|
|
135
|
-
Opcode('OP_12'),
|
|
136
|
-
Opcode('OP_13'),
|
|
137
|
-
Opcode('OP_14'),
|
|
138
|
-
Opcode('OP_15'),
|
|
139
|
-
Opcode('OP_16')
|
|
140
|
-
]
|
|
141
|
-
|
|
142
|
-
describe('@smallInt', function () {
|
|
143
|
-
var testSmallInt = function (n, op) {
|
|
144
|
-
Opcode.smallInt(n).toString().should.equal(op.toString())
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
for (var i = 0; i < smallints.length; i++) {
|
|
148
|
-
var op = smallints[i]
|
|
149
|
-
it('should work for small int ' + op, testSmallInt.bind(null, i, op))
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
it('with not number', function () {
|
|
153
|
-
Opcode.smallInt.bind(null, '2').should.throw('Invalid Argument')
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('with n equal -1', function () {
|
|
157
|
-
Opcode.smallInt.bind(null, -1).should.throw('Invalid Argument')
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('with n equal 17', function () {
|
|
161
|
-
Opcode.smallInt.bind(null, 17).should.throw('Invalid Argument')
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
describe('@isSmallIntOp', function () {
|
|
165
|
-
var testIsSmallInt = function (op) {
|
|
166
|
-
Opcode.isSmallIntOp(op).should.equal(true)
|
|
167
|
-
}
|
|
168
|
-
for (var i = 0; i < smallints.length; i++) {
|
|
169
|
-
var op = smallints[i]
|
|
170
|
-
it('should work for small int ' + op, testIsSmallInt.bind(null, op))
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
it('should work for non-small ints', function () {
|
|
174
|
-
Opcode.isSmallIntOp(Opcode('OP_RETURN')).should.equal(false)
|
|
175
|
-
Opcode.isSmallIntOp(Opcode('OP_CHECKSIG')).should.equal(false)
|
|
176
|
-
Opcode.isSmallIntOp(Opcode('OP_IF')).should.equal(false)
|
|
177
|
-
Opcode.isSmallIntOp(Opcode('OP_NOP')).should.equal(false)
|
|
178
|
-
})
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
describe('#inspect', function () {
|
|
182
|
-
it('should output opcode by name, hex, and decimal', function () {
|
|
183
|
-
Opcode.fromString('OP_NOP').inspect().should.equal('<Opcode: OP_NOP, hex: 61, decimal: 97>')
|
|
184
|
-
})
|
|
185
|
-
})
|
|
186
|
-
})
|
package/test/ordinals/bsv20.js
DELETED
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// BSV-20 / BSV-21 fungible-token inscriptions: build/parse round-trips + validation.
|
|
6
|
-
|
|
7
|
-
require('chai').should()
|
|
8
|
-
var bsv = require('../..')
|
|
9
|
-
var B = bsv.Ordinals.BSV20
|
|
10
|
-
var Ord = bsv.Ordinals
|
|
11
|
-
|
|
12
|
-
describe('Ordinals BSV-20 fungible tokens', function () {
|
|
13
|
-
var key = bsv.PrivateKey.fromRandom()
|
|
14
|
-
var address = key.toAddress()
|
|
15
|
-
|
|
16
|
-
it('deploys a v1 ticker token and round-trips through parse', function () {
|
|
17
|
-
var s = B.buildDeploy({ address: address, tick: 'ORDI', max: '21000000', lim: '1000', dec: 18 })
|
|
18
|
-
var p = B.parseBsv20(s)
|
|
19
|
-
p.p.should.equal('bsv-20')
|
|
20
|
-
p.op.should.equal('deploy')
|
|
21
|
-
p.tick.should.equal('ORDI')
|
|
22
|
-
p.max.should.equal('21000000')
|
|
23
|
-
p.lim.should.equal('1000')
|
|
24
|
-
p.dec.should.equal('18')
|
|
25
|
-
// The payload rides in an inscription with the bsv-20 content-type.
|
|
26
|
-
Ord.parseInscription(s).contentType.should.equal('application/bsv-20')
|
|
27
|
-
// The base lock is the owner's P2PKH.
|
|
28
|
-
Ord.parseInscription(s).lock.toHex().should.equal(bsv.Script.buildPublicKeyHashOut(address).toHex())
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('mints and transfers a v1 ticker token', function () {
|
|
32
|
-
var mint = B.parseBsv20(B.buildMint({ address: address, tick: 'ORDI', amt: '1000' }))
|
|
33
|
-
mint.op.should.equal('mint')
|
|
34
|
-
mint.tick.should.equal('ORDI')
|
|
35
|
-
mint.amt.should.equal('1000')
|
|
36
|
-
|
|
37
|
-
var xfer = B.parseBsv20(B.buildTransfer({ address: address, tick: 'ORDI', amt: '250' }))
|
|
38
|
-
xfer.op.should.equal('transfer')
|
|
39
|
-
xfer.tick.should.equal('ORDI')
|
|
40
|
-
xfer.amt.should.equal('250')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('deploy+mints a BSV-21 (id-based) token and transfers by id', function () {
|
|
44
|
-
var dm = B.parseBsv20(B.buildDeployMint({ address: address, amt: '1000000', dec: 8, sym: 'XYZ' }))
|
|
45
|
-
dm.op.should.equal('deploy+mint')
|
|
46
|
-
dm.amt.should.equal('1000000')
|
|
47
|
-
dm.dec.should.equal('8')
|
|
48
|
-
dm.sym.should.equal('XYZ')
|
|
49
|
-
|
|
50
|
-
var id = 'a'.repeat(64) + '_0'
|
|
51
|
-
var xfer = B.parseBsv20(B.buildTransfer({ address: address, id: id, amt: '5' }))
|
|
52
|
-
xfer.op.should.equal('transfer')
|
|
53
|
-
xfer.id.should.equal(id)
|
|
54
|
-
xfer.amt.should.equal('5')
|
|
55
|
-
;(xfer.tick === undefined).should.equal(true)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('preserves integer amounts larger than 2^53 exactly (string, never a JS number)', function () {
|
|
59
|
-
// uint64 max: far beyond 2^53, and the largest amount the spec allows. The value this
|
|
60
|
-
// used to test (26 digits) exceeded uint64 and would have been burned by an indexer.
|
|
61
|
-
var huge = '18446744073709551615'
|
|
62
|
-
var p = B.parseBsv20(B.buildMint({ address: address, tick: 'BIG', amt: huge }))
|
|
63
|
-
p.amt.should.equal(huge)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('accepts numeric amounts and normalizes them to strings', function () {
|
|
67
|
-
var p = B.parseBsv20(B.buildMint({ address: address, tick: 'NUM', amt: 42 }))
|
|
68
|
-
p.amt.should.equal('42')
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
// Code-review finding #4: leading-zero integer strings are canonicalized (indexers expect
|
|
72
|
-
// canonical decimals), while preserving arbitrary precision.
|
|
73
|
-
it('canonicalizes leading-zero amount strings', function () {
|
|
74
|
-
B.parseBsv20(B.buildMint({ address: address, tick: 'ORDI', amt: '007' })).amt.should.equal('7')
|
|
75
|
-
B.parseBsv20(B.buildDeploy({ address: address, tick: 'ORDI', max: '000100' })).max.should.equal('100')
|
|
76
|
-
// canonicalization does not corrupt a huge value
|
|
77
|
-
var huge = '18446744073709551615'
|
|
78
|
-
B.parseBsv20(B.buildMint({ address: address, tick: 'ORDI', amt: '0' + huge })).amt.should.equal(huge)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('builds a 1-sat token output', function () {
|
|
82
|
-
var out = B.createMintOutput({ address: address, tick: 'ORDI', amt: '1' })
|
|
83
|
-
out.satoshis.should.equal(1)
|
|
84
|
-
B.isBsv20(out.script).should.equal(true)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('round-trips through hex serialization', function () {
|
|
88
|
-
var s = B.buildDeploy({ address: address, tick: 'HEX', max: '1000' })
|
|
89
|
-
var p = B.parseBsv20(s.toHex())
|
|
90
|
-
p.tick.should.equal('HEX')
|
|
91
|
-
p.max.should.equal('1000')
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('parses a raw JSON string or an already-parsed object', function () {
|
|
95
|
-
var json = '{"p":"bsv-20","op":"mint","tick":"ORDI","amt":"1"}'
|
|
96
|
-
B.parseBsv20(json).amt.should.equal('1')
|
|
97
|
-
B.parseBsv20({ p: 'bsv-20', op: 'mint', tick: 'ORDI', amt: '1' }).op.should.equal('mint')
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('isBsv20 is false for a plain inscription and a P2PKH script', function () {
|
|
101
|
-
var plain = Ord.buildInscription({ address: address, contentType: 'text/plain', content: 'hi' })
|
|
102
|
-
B.isBsv20(plain).should.equal(false)
|
|
103
|
-
B.isBsv20(bsv.Script.buildPublicKeyHashOut(address)).should.equal(false)
|
|
104
|
-
;(B.parseBsv20(plain) === null).should.equal(true)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
describe('validation', function () {
|
|
108
|
-
it('rejects a tick longer than 4 UTF-8 bytes', function () {
|
|
109
|
-
;(function () { B.buildDeploy({ address: address, tick: 'TOOLONG', max: '1' }) }).should.throw(/1–4 UTF-8 bytes/)
|
|
110
|
-
})
|
|
111
|
-
it('rejects a non-integer / negative amount', function () {
|
|
112
|
-
;(function () { B.buildMint({ address: address, tick: 'ORDI', amt: '1.5' }) }).should.throw(/non-negative integer/)
|
|
113
|
-
;(function () { B.buildMint({ address: address, tick: 'ORDI', amt: -5 }) }).should.throw(/non-negative integer/)
|
|
114
|
-
})
|
|
115
|
-
it('rejects a zero amount for mint/transfer', function () {
|
|
116
|
-
;(function () { B.buildMint({ address: address, tick: 'ORDI', amt: '0' }) }).should.throw(/greater than zero/)
|
|
117
|
-
})
|
|
118
|
-
it('rejects dec out of the 0…18 range', function () {
|
|
119
|
-
;(function () { B.buildDeploy({ address: address, tick: 'ORDI', max: '1', dec: 19 }) }).should.throw(/dec must be/)
|
|
120
|
-
})
|
|
121
|
-
it('rejects a malformed BSV-21 id', function () {
|
|
122
|
-
;(function () { B.buildTransfer({ address: address, id: 'notanid', amt: '1' }) }).should.throw(/txid.*vout|<txid>/)
|
|
123
|
-
})
|
|
124
|
-
it('requires an owner (address or lock)', function () {
|
|
125
|
-
;(function () { B.buildMint({ tick: 'ORDI', amt: '1' }) }).should.throw(/address or a lock/)
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
// Checked against the BSV-20 / BSV-21 specification at docs.1satordinals.com.
|
|
130
|
-
describe('specification conformance', function () {
|
|
131
|
-
var OUTPOINT = '3b31'.repeat(16) + '_0'
|
|
132
|
-
var UINT64_MAX = '18446744073709551615'
|
|
133
|
-
|
|
134
|
-
it('accepts lim: 0, which the spec defines as unlimited', function () {
|
|
135
|
-
// "lim | No | String | Per-mint limit; 0 or omitted = unlimited"
|
|
136
|
-
B.parseBsv20(B.buildDeploy({ address: address, tick: 'ORDI', max: '21000000', lim: 0 }))
|
|
137
|
-
.lim.should.equal('0')
|
|
138
|
-
B.parseBsv20(B.buildDeploy({ address: address, tick: 'ORDI', max: '21000000', lim: '0' }))
|
|
139
|
-
.lim.should.equal('0')
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('accepts an amount of exactly uint64 max but rejects one above it', function () {
|
|
143
|
-
// "Amounts in BSV-21: strings representing uint64" — larger values are emitted
|
|
144
|
-
// happily as JSON and then discarded by indexers, burning the tokens.
|
|
145
|
-
B.parseBsv20(B.buildDeploy({ address: address, tick: 'ORDI', max: UINT64_MAX }))
|
|
146
|
-
.max.should.equal(UINT64_MAX)
|
|
147
|
-
;(function () {
|
|
148
|
-
B.buildDeploy({ address: address, tick: 'ORDI', max: '18446744073709551616' })
|
|
149
|
-
}).should.throw(/exceeds the uint64 maximum/)
|
|
150
|
-
;(function () {
|
|
151
|
-
B.buildMint({ address: address, tick: 'ORDI', amt: '9'.repeat(26) })
|
|
152
|
-
}).should.throw(/exceeds the uint64 maximum/)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('rejects a numeric amount past 2^53, which is already imprecise', function () {
|
|
156
|
-
(function () {
|
|
157
|
-
B.buildMint({ address: address, tick: 'ORDI', amt: 1e17 })
|
|
158
|
-
}).should.throw(/MAX_SAFE_INTEGER/)
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('rejects non-string sym / icon instead of stringifying them', function () {
|
|
162
|
-
(function () {
|
|
163
|
-
B.buildDeployMint({ address: address, amt: '100', sym: {} })
|
|
164
|
-
}).should.throw(/sym must be a string, got an object/)
|
|
165
|
-
;(function () {
|
|
166
|
-
B.buildDeployMint({ address: address, amt: '100', icon: 42 })
|
|
167
|
-
}).should.throw(/icon must be a string/)
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it('requires icon to be an outpoint reference, as the spec defines it', function () {
|
|
171
|
-
(function () {
|
|
172
|
-
B.buildDeployMint({ address: address, amt: '100', icon: 'https://example.com/i.png' })
|
|
173
|
-
}).should.throw(/outpoint reference/)
|
|
174
|
-
B.parseBsv20(B.buildDeployMint({ address: address, amt: '100', icon: OUTPOINT }))
|
|
175
|
-
.icon.should.equal(OUTPOINT)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
// parseBsv20/isBsv20 are documented to report VALIDITY. They previously returned any
|
|
179
|
-
// JSON object carrying p:'bsv-20' and a string op, including payloads no indexer acts on.
|
|
180
|
-
describe('parseBsv20 enforces the validity it reports', function () {
|
|
181
|
-
function rejects (label, payload) {
|
|
182
|
-
it('rejects ' + label, function () {
|
|
183
|
-
var json = JSON.stringify(payload)
|
|
184
|
-
B.isBsv20(json).should.equal(false)
|
|
185
|
-
;(B.parseBsv20(json) === null).should.equal(true)
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
rejects('a transfer with no amount and no token', { p: 'bsv-20', op: 'transfer' })
|
|
189
|
-
rejects('a mint with no amount', { p: 'bsv-20', op: 'mint', tick: 'ORDI' })
|
|
190
|
-
rejects('a deploy with no max', { p: 'bsv-20', op: 'deploy', tick: 'ORDI' })
|
|
191
|
-
rejects('an operation the spec does not define', { p: 'bsv-20', op: 'not-an-op' })
|
|
192
|
-
rejects('a transfer naming both tick and id', { p: 'bsv-20', op: 'transfer', tick: 'A', id: OUTPOINT, amt: '1' })
|
|
193
|
-
rejects('an auth carrying amt, which the spec forbids', { p: 'bsv-20', op: 'auth', id: OUTPOINT, amt: '1' })
|
|
194
|
-
rejects('a deploy+auth carrying amt, which the spec forbids', { p: 'bsv-20', op: 'deploy+auth', amt: '1' })
|
|
195
|
-
rejects('a ticker longer than 4 bytes', { p: 'bsv-20', op: 'mint', tick: 'TOOLONG', amt: '1' })
|
|
196
|
-
rejects('a malformed token id', { p: 'bsv-20', op: 'transfer', id: 'nope', amt: '1' })
|
|
197
|
-
rejects('an amount above uint64', { p: 'bsv-20', op: 'mint', tick: 'A', amt: '9'.repeat(26) })
|
|
198
|
-
rejects('dec out of range', { p: 'bsv-20', op: 'deploy', tick: 'A', max: '1', dec: '19' })
|
|
199
|
-
|
|
200
|
-
it('accepts the spec operations this library does not yet emit', function () {
|
|
201
|
-
// Reading the chain is not the same as writing it: burn/auth/deploy+auth are valid
|
|
202
|
-
// BSV-21 and must be recognised even though there is no builder for them yet.
|
|
203
|
-
B.isBsv20(JSON.stringify({ p: 'bsv-20', op: 'burn', id: OUTPOINT, amt: '5' })).should.equal(true)
|
|
204
|
-
B.isBsv20(JSON.stringify({ p: 'bsv-20', op: 'auth', id: OUTPOINT })).should.equal(true)
|
|
205
|
-
B.isBsv20(JSON.stringify({ p: 'bsv-20', op: 'deploy+auth', sym: 'STABLE' })).should.equal(true)
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('tolerates a non-canonical on-chain amount when reading', function () {
|
|
209
|
-
// Our builder emits canonical amounts, but other people's payloads are not ours
|
|
210
|
-
// to reject over leading zeros.
|
|
211
|
-
B.isBsv20(JSON.stringify({ p: 'bsv-20', op: 'mint', tick: 'A', amt: '007' })).should.equal(true)
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
it('still round-trips everything this library builds', function () {
|
|
215
|
-
B.isBsv20(B.buildDeploy({ address: address, tick: 'ORDI', max: '21000000' })).should.equal(true)
|
|
216
|
-
B.isBsv20(B.buildMint({ address: address, tick: 'ORDI', amt: '1' })).should.equal(true)
|
|
217
|
-
B.isBsv20(B.buildTransfer({ address: address, id: OUTPOINT, amt: '1' })).should.equal(true)
|
|
218
|
-
B.isBsv20(B.buildDeployMint({ address: address, amt: '1000' })).should.equal(true)
|
|
219
|
-
B.isBsv20(B.buildBurn({ address: address, id: OUTPOINT, amt: '1' })).should.equal(true)
|
|
220
|
-
B.isBsv20(B.buildAuth({ address: address, id: OUTPOINT })).should.equal(true)
|
|
221
|
-
B.isBsv20(B.buildDeployAuth({ address: address, sym: 'STABLE' })).should.equal(true)
|
|
222
|
-
})
|
|
223
|
-
})
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
// BSV-21 authority model: deploy+auth creates no supply, an auth output carries the right
|
|
227
|
-
// to mint, and mint names its token by `id`. Previously only the fixed-supply deploy+mint
|
|
228
|
-
// path could be built, so these operations could be read but never written.
|
|
229
|
-
describe('BSV-21 authority operations', function () {
|
|
230
|
-
var OUTPOINT = '3b31'.repeat(16) + '_0'
|
|
231
|
-
|
|
232
|
-
it('mints by id, which requires an auth input on chain', function () {
|
|
233
|
-
var p = B.parseBsv20(B.buildMint({ address: address, id: OUTPOINT, amt: '1000000' }))
|
|
234
|
-
p.op.should.equal('mint')
|
|
235
|
-
p.id.should.equal(OUTPOINT)
|
|
236
|
-
p.amt.should.equal('1000000')
|
|
237
|
-
;(p.tick === undefined).should.equal(true)
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('leaves the v1 ticker mint byte-for-byte unchanged', function () {
|
|
241
|
-
var p = B.parseBsv20(B.buildMint({ address: address, tick: 'ORDI', amt: '1000' }))
|
|
242
|
-
JSON.stringify(p).should.equal('{"p":"bsv-20","op":"mint","tick":"ORDI","amt":"1000"}')
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
it('refuses to guess which token an operation means', function () {
|
|
246
|
-
// Naming both used to silently drop the tick — a different token, not a preference.
|
|
247
|
-
(function () {
|
|
248
|
-
B.buildMint({ address: address, tick: 'ORDI', id: OUTPOINT, amt: '1' })
|
|
249
|
-
}).should.throw(/not both/)
|
|
250
|
-
;(function () {
|
|
251
|
-
B.buildTransfer({ address: address, tick: 'ORDI', id: OUTPOINT, amt: '1' })
|
|
252
|
-
}).should.throw(/not both/)
|
|
253
|
-
;(function () {
|
|
254
|
-
B.buildMint({ address: address, amt: '1' })
|
|
255
|
-
}).should.throw(/requires `tick` \(v1\) or `id`/)
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
it('builds a burn', function () {
|
|
259
|
-
var p = B.parseBsv20(B.buildBurn({ address: address, id: OUTPOINT, amt: '5' }))
|
|
260
|
-
p.op.should.equal('burn')
|
|
261
|
-
p.id.should.equal(OUTPOINT)
|
|
262
|
-
p.amt.should.equal('5')
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
it('rejects a ticker burn, which the spec does not define', function () {
|
|
266
|
-
(function () {
|
|
267
|
-
B.buildBurn({ address: address, tick: 'ORDI', amt: '5' })
|
|
268
|
-
}).should.throw(/BSV-21 operation/)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
it('builds deploy+auth with no supply', function () {
|
|
272
|
-
var p = B.parseBsv20(B.buildDeployAuth({ address: address, sym: 'STABLE', dec: 6 }))
|
|
273
|
-
p.op.should.equal('deploy+auth')
|
|
274
|
-
p.sym.should.equal('STABLE')
|
|
275
|
-
p.dec.should.equal('6')
|
|
276
|
-
;(p.amt === undefined).should.equal(true)
|
|
277
|
-
// Every field is optional.
|
|
278
|
-
B.parseBsv20(B.buildDeployAuth({ address: address })).op.should.equal('deploy+auth')
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
it('builds an auth output carrying no amount', function () {
|
|
282
|
-
var p = B.parseBsv20(B.buildAuth({ address: address, id: OUTPOINT }))
|
|
283
|
-
p.op.should.equal('auth')
|
|
284
|
-
p.id.should.equal(OUTPOINT)
|
|
285
|
-
;(p.amt === undefined).should.equal(true)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it('refuses to attach an amt where the spec forbids one', function () {
|
|
289
|
-
(function () {
|
|
290
|
-
B.buildAuth({ address: address, id: OUTPOINT, amt: '1' })
|
|
291
|
-
}).should.throw(/must not carry an amt/)
|
|
292
|
-
;(function () {
|
|
293
|
-
B.buildDeployAuth({ address: address, sym: 'X', amt: '1' })
|
|
294
|
-
}).should.throw(/must not carry an amt/)
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
it('applies the same amount rules as the other operations', function () {
|
|
298
|
-
(function () {
|
|
299
|
-
B.buildBurn({ address: address, id: OUTPOINT, amt: '0' })
|
|
300
|
-
}).should.throw(/greater than zero/)
|
|
301
|
-
;(function () {
|
|
302
|
-
B.buildBurn({ address: address, id: OUTPOINT, amt: '9'.repeat(26) })
|
|
303
|
-
}).should.throw(/exceeds the uint64 maximum/)
|
|
304
|
-
;(function () {
|
|
305
|
-
B.buildMint({ address: address, id: 'notanid', amt: '1' })
|
|
306
|
-
}).should.throw(/<txid>/)
|
|
307
|
-
;(function () {
|
|
308
|
-
B.buildDeployAuth({ address: address, sym: {} })
|
|
309
|
-
}).should.throw(/sym must be a string/)
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
it('builds 1-sat outputs for each new operation', function () {
|
|
313
|
-
var outs = [
|
|
314
|
-
B.createBurnOutput({ address: address, id: OUTPOINT, amt: '5' }),
|
|
315
|
-
B.createAuthOutput({ address: address, id: OUTPOINT }),
|
|
316
|
-
B.createDeployAuthOutput({ address: address, sym: 'STABLE' })
|
|
317
|
-
]
|
|
318
|
-
outs.forEach(function (o) {
|
|
319
|
-
o.satoshis.should.equal(1)
|
|
320
|
-
B.isBsv20(o.script).should.equal(true)
|
|
321
|
-
})
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
it('round-trips a full authority lifecycle', function () {
|
|
325
|
-
// deploy+auth -> auth output delegating mint rights -> mint by id -> burn.
|
|
326
|
-
var deploy = B.parseBsv20(B.buildDeployAuth({ address: address, sym: 'GOLD', dec: 8 }))
|
|
327
|
-
var auth = B.parseBsv20(B.buildAuth({ address: address, id: OUTPOINT }))
|
|
328
|
-
var mint = B.parseBsv20(B.buildMint({ address: address, id: OUTPOINT, amt: '1000' }))
|
|
329
|
-
var burn = B.parseBsv20(B.buildBurn({ address: address, id: OUTPOINT, amt: '400' }))
|
|
330
|
-
deploy.op.should.equal('deploy+auth')
|
|
331
|
-
auth.op.should.equal('auth')
|
|
332
|
-
mint.amt.should.equal('1000')
|
|
333
|
-
burn.amt.should.equal('400')
|
|
334
|
-
;[deploy, auth, mint, burn].forEach(function (p) { p.p.should.equal('bsv-20') })
|
|
335
|
-
})
|
|
336
|
-
})
|
|
337
|
-
})
|