@smartledger/bsv 9.0.0 → 9.1.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.
Files changed (155) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/README.md +152 -1041
  3. package/STABILITY.md +134 -0
  4. package/bsv-gdaf.min.js +53 -48
  5. package/bsv-ltp.min.js +23 -18
  6. package/bsv-smartcontract.min.js +18 -18
  7. package/bsv.bundle.js +53 -48
  8. package/bsv.min.js +53 -48
  9. package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
  10. package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
  11. package/docs/getting-started/INSTALLATION.md +23 -23
  12. package/docs/getting-started/QUICK_START.md +7 -7
  13. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  14. package/docs/proposals/10.0.0-package-split.md +131 -0
  15. package/index.js +5 -0
  16. package/lib/block/merkleblock.js +53 -9
  17. package/lib/hdprivatekey.js +15 -0
  18. package/lib/script/interpreter.js +109 -1
  19. package/lib/smart_contract/builder.js +14 -0
  20. package/lib/util/deprecate.js +159 -0
  21. package/package.json +19 -82
  22. package/version.js +1 -1
  23. package/.mocharc.json +0 -5
  24. package/test/address.js +0 -629
  25. package/test/block/block.js +0 -239
  26. package/test/block/blockheader.js +0 -270
  27. package/test/block/merkleblock.js +0 -207
  28. package/test/build/bundle_crypto_shim.js +0 -104
  29. package/test/build/bundle_externals.js +0 -125
  30. package/test/build/bundle_smoke.js +0 -55
  31. package/test/build/esbuild_main.js +0 -65
  32. package/test/build/esm_wrapper.js +0 -97
  33. package/test/build/exports_resolution.js +0 -84
  34. package/test/build/version_sync.js +0 -14
  35. package/test/cli/smoke.js +0 -261
  36. package/test/consensus/base58-vectors.js +0 -120
  37. package/test/consensus/sv-script-vectors.js +0 -81
  38. package/test/consensus/sv-sighash-vectors.js +0 -51
  39. package/test/consensus/sv-tx-vectors.js +0 -48
  40. package/test/credentials-test.js +0 -332
  41. package/test/crypto/backend_selection.js +0 -126
  42. package/test/crypto/bn.js +0 -168
  43. package/test/crypto/ecdsa.js +0 -426
  44. package/test/crypto/elliptic-fixed.js +0 -61
  45. package/test/crypto/hash.browser.js +0 -121
  46. package/test/crypto/hash.js +0 -122
  47. package/test/crypto/point.js +0 -193
  48. package/test/crypto/random.js +0 -105
  49. package/test/crypto/security.js +0 -176
  50. package/test/crypto/shamir.js +0 -177
  51. package/test/crypto/shamir_rngcap.js +0 -29
  52. package/test/crypto/signature.js +0 -399
  53. package/test/data/bip69.json +0 -215
  54. package/test/data/bitcoin-sv/README.md +0 -170
  55. package/test/data/bitcoin-sv/base58_encode_decode.json +0 -14
  56. package/test/data/bitcoin-sv/base58_keys_invalid.json +0 -152
  57. package/test/data/bitcoin-sv/base58_keys_valid.json +0 -452
  58. package/test/data/bitcoin-sv/script_tests.json +0 -2591
  59. package/test/data/bitcoin-sv/sighash.json +0 -1003
  60. package/test/data/bitcoin-sv/tx_invalid.json +0 -285
  61. package/test/data/bitcoin-sv/tx_valid.json +0 -367
  62. package/test/data/bitcoind/base58_keys_invalid.json +0 -152
  63. package/test/data/bitcoind/base58_keys_valid.json +0 -452
  64. package/test/data/bitcoind/blocks.json +0 -27
  65. package/test/data/bitcoind/script_tests.json +0 -2244
  66. package/test/data/bitcoind/sig_canonical.json +0 -7
  67. package/test/data/bitcoind/sig_noncanonical.json +0 -22
  68. package/test/data/bitcoind/tx_invalid.json +0 -177
  69. package/test/data/bitcoind/tx_valid.json +0 -224
  70. package/test/data/blk86756-testnet.dat +0 -0
  71. package/test/data/blk86756-testnet.js +0 -12
  72. package/test/data/blk86756-testnet.json +0 -684
  73. package/test/data/brc220-batch-vector.json +0 -129
  74. package/test/data/ecdsa.json +0 -230
  75. package/test/data/merkleblocks.js +0 -486
  76. package/test/data/messages.json +0 -22
  77. package/test/data/sighash.json +0 -1004
  78. package/test/data/tx_creation.json +0 -85
  79. package/test/didweb/relationships.js +0 -144
  80. package/test/ecies/bitcore-ecies.js +0 -178
  81. package/test/ecies/electrum-ecies.js +0 -206
  82. package/test/encoding/base58.js +0 -131
  83. package/test/encoding/base58check.js +0 -145
  84. package/test/encoding/bufferreader.js +0 -328
  85. package/test/encoding/bufferwriter.js +0 -160
  86. package/test/encoding/varint.js +0 -104
  87. package/test/gdaf/anchor_no_key_leak.js +0 -146
  88. package/test/gdaf/anchor_spv.js +0 -163
  89. package/test/gdaf/canonicalization.js +0 -106
  90. package/test/gdaf/canonicalize.js +0 -140
  91. package/test/gdaf/zk_prover.js +0 -204
  92. package/test/hdkeys.js +0 -365
  93. package/test/hdprivatekey.js +0 -339
  94. package/test/hdpublickey.js +0 -293
  95. package/test/index.js +0 -16
  96. package/test/ltp/ids.js +0 -74
  97. package/test/ltp/right.js +0 -198
  98. package/test/ltp/verify_failclosed.js +0 -134
  99. package/test/message/message.js +0 -190
  100. package/test/mnemonic/data/fixtures.json +0 -300
  101. package/test/mnemonic/mnemonic.js +0 -277
  102. package/test/mnemonic/mocha.opts +0 -1
  103. package/test/mnemonic/pbkdf2.test.js +0 -43
  104. package/test/networks.js +0 -207
  105. package/test/notaryhash/batch_leaf.js +0 -140
  106. package/test/notaryhash/batch_vector.js +0 -282
  107. package/test/notaryhash/certificate.js +0 -249
  108. package/test/notaryhash/encoding.js +0 -186
  109. package/test/notaryhash/interop.js +0 -112
  110. package/test/notaryhash/merkle.js +0 -181
  111. package/test/notaryhash/script.js +0 -270
  112. package/test/notaryhash/verify.js +0 -342
  113. package/test/opcode.js +0 -186
  114. package/test/ordinals/bsv20.js +0 -337
  115. package/test/ordinals/inscription.js +0 -329
  116. package/test/ordinals/ordlock.js +0 -567
  117. package/test/privatekey.js +0 -540
  118. package/test/publickey.js +0 -411
  119. package/test/regressions.js +0 -215
  120. package/test/script/chronicle.js +0 -543
  121. package/test/script/defaults.js +0 -160
  122. package/test/script/genesis_limits.js +0 -203
  123. package/test/script/interpreter.js +0 -776
  124. package/test/script/script.js +0 -1259
  125. package/test/script/string_ops.js +0 -88
  126. package/test/security/fail_closed_contracts.js +0 -104
  127. package/test/security/threat_model_coverage.js +0 -31
  128. package/test/smart_contract/covenants.js +0 -207
  129. package/test/smart_contract/dsl_debugger.js +0 -92
  130. package/test/smart_contract/extract_field.js +0 -61
  131. package/test/smart_contract/nonenforcing_guard.js +0 -44
  132. package/test/smart_contract/ordinal_transfer.js +0 -71
  133. package/test/smart_contract/preimage.js +0 -98
  134. package/test/smart_contract/sighash_marketplace.js +0 -98
  135. package/test/smart_contract/token_generalized.js +0 -175
  136. package/test/spv/headerchain.js +0 -86
  137. package/test/spv/merkleproof.js +0 -133
  138. package/test/statuslist/failclosed.js +0 -78
  139. package/test/transaction/deserialize.js +0 -33
  140. package/test/transaction/input/input.js +0 -92
  141. package/test/transaction/input/multisig.js +0 -174
  142. package/test/transaction/input/multisigscripthash.js +0 -111
  143. package/test/transaction/input/publickey.js +0 -68
  144. package/test/transaction/input/publickeyhash.js +0 -59
  145. package/test/transaction/output.js +0 -185
  146. package/test/transaction/sighash.js +0 -91
  147. package/test/transaction/signature.js +0 -127
  148. package/test/transaction/transaction.js +0 -1299
  149. package/test/transaction/unspentoutput.js +0 -97
  150. package/test/types/dts_drift.js +0 -124
  151. package/test/types/surface_honesty.js +0 -102
  152. package/test/util/id.js +0 -72
  153. package/test/util/js.js +0 -76
  154. package/test/util/preconditions.js +0 -79
  155. package/test/vcjwt/interop.js +0 -126
@@ -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
- })
@@ -1,329 +0,0 @@
1
- 'use strict'
2
-
3
- /* global describe, it */
4
-
5
- // 1Sat Ordinals inscription build/parse round-trip + output construction.
6
-
7
- require('chai').should()
8
- var bsv = require('../..')
9
- var Ord = bsv.Ordinals
10
-
11
- describe('Ordinals inscriptions', function () {
12
- var key = bsv.PrivateKey.fromRandom()
13
- var address = key.toAddress()
14
-
15
- it('builds a P2PKH inscription and round-trips through parse', function () {
16
- var s = Ord.buildInscription({
17
- address: address, contentType: 'text/plain', content: 'hello ordinals'
18
- })
19
- var parsed = Ord.parseInscription(s)
20
- parsed.should.be.an('object')
21
- parsed.contentType.should.equal('text/plain')
22
- parsed.contentText.should.equal('hello ordinals')
23
- // The base lock is exactly the P2PKH for the address.
24
- parsed.lock.toHex().should.equal(bsv.Script.buildPublicKeyHashOut(address).toHex())
25
- })
26
-
27
- it('preserves binary content and content-type exactly', function () {
28
- var content = bsv.crypto.Random.getRandomBuffer(64)
29
- var s = Ord.buildInscription({ address: address, contentType: 'image/png', content: content })
30
- var parsed = Ord.parseInscription(s)
31
- parsed.contentType.should.equal('image/png')
32
- parsed.content.equals(content).should.equal(true)
33
- })
34
-
35
- it('round-trips through hex serialization', function () {
36
- var s = Ord.buildInscription({ address: address, contentType: 'application/json', content: '{"a":1}' })
37
- var parsed = Ord.parseInscription(s.toHex())
38
- parsed.contentText.should.equal('{"a":1}')
39
- parsed.contentType.should.equal('application/json')
40
- })
41
-
42
- it('accepts a custom base lock script', function () {
43
- var lock = bsv.Script.buildPublicKeyHashOut(address)
44
- var s = Ord.buildInscription({ lock: lock, contentType: 'text/plain', content: 'x' })
45
- Ord.isInscription(s).should.equal(true)
46
- Ord.parseInscription(s).lock.toHex().should.equal(lock.toHex())
47
- })
48
-
49
- it('isInscription is false for a plain P2PKH script', function () {
50
- var p2pkh = bsv.Script.buildPublicKeyHashOut(address)
51
- Ord.isInscription(p2pkh).should.equal(false)
52
- var isNull = Ord.parseInscription(p2pkh) === null
53
- isNull.should.equal(true)
54
- })
55
-
56
- it('createInscriptionOutput is a 1-sat output carrying the inscription', function () {
57
- var out = Ord.createInscriptionOutput({ address: address, contentType: 'text/plain', content: 'hi' })
58
- out.satoshis.should.equal(1)
59
- Ord.isInscription(out.script).should.equal(true)
60
- })
61
-
62
- it('batchInscriptionOutputs builds one output per item', function () {
63
- var outs = Ord.batchInscriptionOutputs([
64
- { address: address, contentType: 'text/plain', content: 'a' },
65
- { address: address, contentType: 'text/plain', content: 'b' }
66
- ])
67
- outs.length.should.equal(2)
68
- Ord.parseInscription(outs[0].script).contentText.should.equal('a')
69
- Ord.parseInscription(outs[1].script).contentText.should.equal('b')
70
- })
71
-
72
- // The 1Sat spec allows the locking script to be prepended OR appended to the
73
- // envelope. The parser only ever looked before it, so an appended lock was dropped
74
- // and an owned ordinal was reported as carrying no locking script at all.
75
- describe('recovers the lock wherever the spec allows it to sit', function () {
76
- var p2pkh = bsv.Script.buildPublicKeyHashOut(address)
77
-
78
- function concat () {
79
- var out = new bsv.Script()
80
- Array.prototype.slice.call(arguments).forEach(function (part) {
81
- part.chunks.forEach(function (c) { out.chunks.push(c) })
82
- })
83
- return out
84
- }
85
-
86
- function envelope () {
87
- return Ord.buildInscription({
88
- lock: new bsv.Script(), allowEmptyLock: true, contentType: 'text/plain', content: 'hello'
89
- })
90
- }
91
-
92
- it('recovers a lock that precedes the envelope', function () {
93
- var parsed = Ord.parseInscription(concat(p2pkh, envelope()))
94
- parsed.lock.toHex().should.equal(p2pkh.toHex())
95
- parsed.contentText.should.equal('hello')
96
- })
97
-
98
- it('recovers a lock that follows the envelope', function () {
99
- var parsed = Ord.parseInscription(concat(envelope(), p2pkh))
100
- parsed.lock.toHex().should.equal(p2pkh.toHex())
101
- parsed.contentText.should.equal('hello')
102
- })
103
-
104
- it('keeps a separating OP_CODESEPARATOR, which really does run', function () {
105
- var sep = concat(p2pkh, new bsv.Script().add(bsv.Opcode.OP_CODESEPARATOR), envelope())
106
- var parsed = Ord.parseInscription(sep)
107
- parsed.lock.chunks.length.should.equal(p2pkh.chunks.length + 1)
108
- parsed.contentText.should.equal('hello')
109
- })
110
-
111
- it('still reports no lock when there genuinely is none', function () {
112
- Ord.parseInscription(envelope()).lock.chunks.length.should.equal(0)
113
- })
114
- })
115
-
116
- // Every case below used to succeed and emit a well-formed script that inscribed
117
- // something other than what the caller asked for. An inscription is permanent, so
118
- // each one now fails loudly at build time instead of on-chain.
119
- describe('rejects arguments that would silently inscribe the wrong thing', function () {
120
- it('throws when content is omitted rather than inscribing an empty payload', function () {
121
- (function () {
122
- Ord.buildInscription({ address: address, contentType: 'text/plain' })
123
- }).should.throw(/requires `content`/)
124
- })
125
-
126
- it('names the field the caller actually passed', function () {
127
- // The real-world report: a wallet whose own builder calls the field `data`.
128
- (function () {
129
- Ord.buildInscription({ address: address, contentType: 'text/plain', data: 'hello world' })
130
- }).should.throw(/received `data`, which is not read/)
131
- })
132
-
133
- it('still allows an explicitly empty payload', function () {
134
- var s = Ord.buildInscription({ address: address, contentType: 'text/plain', content: '' })
135
- Ord.parseInscription(s).content.length.should.equal(0)
136
- })
137
-
138
- it('throws rather than stringifying an object into the payload', function () {
139
- // String({}) is '[object Object]' — permanent, and never intended.
140
- (function () {
141
- Ord.buildInscription({ address: address, contentType: 'text/plain', content: { a: 1 } })
142
- }).should.throw(/content must be a string or Buffer, got an object/)
143
- })
144
-
145
- it('throws on any non-string, non-Buffer content', function () {
146
- [42, true, null, ['a'], undefined].forEach(function (v) {
147
- (function () {
148
- Ord.buildInscription({ address: address, contentType: 'text/plain', content: v })
149
- }).should.throw(Error)
150
- })
151
- })
152
-
153
- it('requires a contentType for Buffer content instead of labelling it text/plain', function () {
154
- var png = Buffer.from([0x89, 0x50, 0x4e, 0x47])
155
- ;(function () {
156
- Ord.buildInscription({ address: address, content: png })
157
- }).should.throw(/contentType is required when content is a Buffer/)
158
- // Declared explicitly, it builds.
159
- var s = Ord.buildInscription({ address: address, contentType: 'image/png', content: png })
160
- Ord.parseInscription(s).contentType.should.equal('image/png')
161
- })
162
-
163
- it('keeps the text/plain default for string content', function () {
164
- var s = Ord.buildInscription({ address: address, content: 'hi' })
165
- Ord.parseInscription(s).contentType.should.equal('text/plain')
166
- })
167
-
168
- it('throws on an empty or non-string contentType', function () {
169
- (function () {
170
- Ord.buildInscription({ address: address, contentType: '', content: 'x' })
171
- }).should.throw(/contentType must not be empty/)
172
- ;(function () {
173
- Ord.buildInscription({ address: address, contentType: {}, content: 'x' })
174
- }).should.throw(/contentType must be a string or Buffer/)
175
- })
176
-
177
- it('throws on an empty base lock, which would be anyone-can-spend', function () {
178
- // With no base lock the script is just the inert envelope: OP_FALSE OP_IF skips
179
- // to OP_ENDIF, so whatever the spender pushed is the final stack and any spender
180
- // succeeds. Proven below rather than asserted.
181
- (function () {
182
- Ord.buildInscription({ lock: Buffer.alloc(0), contentType: 'text/plain', content: 'x' })
183
- }).should.throw(/spendable by anyone/)
184
- })
185
-
186
- it('proves the empty-lock script really is anyone-can-spend', function () {
187
- var envelope = Ord.buildInscription({
188
- lock: new bsv.Script(), allowEmptyLock: true, contentType: 'text/plain', content: 'x'
189
- })
190
- // An unlocking script from a key unrelated to `address` satisfies it.
191
- var unlock = new bsv.Script().add(bsv.Opcode.OP_1)
192
- var interp = new bsv.Script.Interpreter()
193
- var ok = interp.verify(unlock, envelope, new bsv.Transaction(), 0, 0)
194
- ok.should.equal(true)
195
- })
196
-
197
- it('permits an empty lock only when the caller opts in', function () {
198
- var s = Ord.buildInscription({
199
- lock: new bsv.Script(), allowEmptyLock: true, contentType: 'text/plain', content: 'x'
200
- })
201
- Ord.isInscription(s).should.equal(true)
202
- })
203
-
204
- it('throws when both lock and address are given instead of ignoring one', function () {
205
- var lock = bsv.Script.buildPublicKeyHashOut(address)
206
- var other = bsv.PrivateKey.fromRandom().toAddress()
207
- ;(function () {
208
- Ord.buildInscription({ lock: lock, address: other, contentType: 'text/plain', content: 'x' })
209
- }).should.throw(/not both/)
210
- })
211
-
212
- it('rejects a satoshi amount that carries no ordinal', function () {
213
- [0, '1', 1.5, -1].forEach(function (v) {
214
- (function () {
215
- Ord.createInscriptionOutput({
216
- address: address, contentType: 'text/plain', content: 'x', satoshis: v
217
- })
218
- }).should.throw(Error)
219
- })
220
- Ord.createInscriptionOutput({
221
- address: address, contentType: 'text/plain', content: 'x'
222
- }).satoshis.should.equal(1)
223
- })
224
- })
225
-
226
- // Transferring a large inscription. The envelope is inert, so the spend is an
227
- // ordinary P2PKH — but under pre-Genesis rules the interpreter could not even load
228
- // the script, because total script size was capped at 10,000 bytes.
229
- describe('transferring a large inscription', function () {
230
- var I = bsv.Script.Interpreter
231
- var Sighash = bsv.Transaction.Sighash
232
- var BN = bsv.crypto.BN
233
- var SIGHASH = bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID
234
- // Pre-Genesis: no era bit, so the 520-byte element cap and the 10,000-byte script
235
- // cap both still apply. Kept deliberately, to show what they reject.
236
- var FLAGS = I.SCRIPT_VERIFY_STRICTENC | I.SCRIPT_ENABLE_SIGHASH_FORKID
237
- // Current mainnet, where Genesis lifted both.
238
- var MAINNET = I.mainnetFlags()
239
- var saved
240
-
241
- beforeEach(function () { saved = I.getLimits() })
242
- afterEach(function () { I.setLimits(saved) })
243
-
244
- // A signed transfer of a `kb`-sized inscription to a new owner.
245
- function transfer (kb) {
246
- var owner = bsv.PrivateKey.fromBuffer(Buffer.alloc(32, 11))
247
- var recipient = bsv.PrivateKey.fromBuffer(Buffer.alloc(32, 22))
248
- var lock = Ord.buildInscription({
249
- address: owner.toAddress(),
250
- contentType: 'image/png',
251
- content: bsv.crypto.Random.getRandomBuffer(kb * 1024)
252
- })
253
- var spend = new bsv.Transaction()
254
- spend.addInput(new bsv.Transaction.Input({
255
- prevTxId: 'aa'.repeat(32), outputIndex: 0, script: bsv.Script.empty()
256
- }), lock, 1)
257
- spend.addOutput(new bsv.Transaction.Output({
258
- script: bsv.Script.buildPublicKeyHashOut(recipient.toAddress()), satoshis: 1
259
- }))
260
- // Sign over the FULL previous locking script — envelope included. That is the
261
- // script code the network uses; signing the base lock alone yields a signature
262
- // that verifies against its own preimage and nothing else.
263
- var sig = Sighash.sign(spend, owner, SIGHASH, 0, lock, new BN(1), FLAGS)
264
- var unlock = new bsv.Script()
265
- .add(Buffer.concat([sig.toDER(), Buffer.from([SIGHASH])]))
266
- .add(owner.toPublicKey().toBuffer())
267
- spend.inputs[0].setScript(unlock)
268
- return { lock: lock, unlock: unlock, spend: spend, sig: sig, owner: owner }
269
- }
270
-
271
- // Both caps are derived from the ERA flags, not from the limit statics: see
272
- // Interpreter#maxScriptElementSize and #maxScriptSize, each of which branches on
273
- // isAfterGenesis(). Passing the era is therefore the whole fix — these tests used
274
- // to call SmartContract.enableGenesis(), which raised process-wide statics instead
275
- // and had to be undone in an afterEach or it changed the rules for every test that
276
- // ran later.
277
- function evaluate (t, flags) {
278
- var interp = new I()
279
- var ok = interp.verify(t.unlock, t.lock, t.spend, 0,
280
- flags === undefined ? MAINNET : flags, new BN(1))
281
- return { ok: ok, err: interp.errstr }
282
- }
283
-
284
- it('cannot be evaluated under pre-Genesis limits — two separate caps bite', function () {
285
- // Explicitly pre-Genesis: FLAGS carries no era bit, so both caps still apply.
286
- // Under 10 KB the script loads and the 520-byte push cap rejects the content...
287
- var small = evaluate(transfer(3), FLAGS)
288
- small.ok.should.equal(false)
289
- small.err.should.equal('SCRIPT_ERR_PUSH_SIZE')
290
- // ...over 10 KB it never gets that far: evaluate() refuses the script outright.
291
- var big = evaluate(transfer(50), FLAGS)
292
- big.ok.should.equal(false)
293
- big.err.should.equal('SCRIPT_ERR_SCRIPT_SIZE')
294
- })
295
-
296
- it('evaluates under mainnet consensus, with the limit statics untouched', function () {
297
- var before = JSON.stringify(I.getLimits())
298
- var t = transfer(50)
299
- t.lock.toBuffer().length.should.be.above(10000) // past the pre-Genesis cap
300
- evaluate(t).ok.should.equal(true)
301
- // The point of the change: no process-wide mutation was needed to get here.
302
- JSON.stringify(I.getLimits()).should.equal(before)
303
- })
304
-
305
- it('agrees with a direct signature check against the sighash', function () {
306
- // The fallback used when the interpreter cannot run: verify the signature over
307
- // the sighash directly. It must agree with the interpreter where both work.
308
- var t = transfer(50)
309
- var hash = Sighash.sighash(t.spend, SIGHASH, 0, t.lock, new BN(1), FLAGS)
310
- var sigOk = bsv.crypto.ECDSA.verify(hash, t.sig, t.owner.toPublicKey(), 'little')
311
- sigOk.should.equal(true)
312
- evaluate(t).ok.should.equal(true)
313
- })
314
-
315
- it('rejects a signature made over the base lock instead of the full script', function () {
316
- var t = transfer(50)
317
- var baseLock = bsv.Script.buildPublicKeyHashOut(t.owner.toAddress())
318
- var wrong = Sighash.sign(t.spend, t.owner, SIGHASH, 0, baseLock, new BN(1), FLAGS)
319
- var hash = Sighash.sighash(t.spend, SIGHASH, 0, t.lock, new BN(1), FLAGS)
320
- // Verifies against its own (wrong) preimage, but not the one the network checks.
321
- bsv.crypto.ECDSA.verify(hash, wrong, t.owner.toPublicKey(), 'little').should.equal(false)
322
- t.spend.inputs[0].setScript(new bsv.Script()
323
- .add(Buffer.concat([wrong.toDER(), Buffer.from([SIGHASH])]))
324
- .add(t.owner.toPublicKey().toBuffer()))
325
- var interp = new I()
326
- interp.verify(t.spend.inputs[0].script, t.lock, t.spend, 0, FLAGS, new BN(1)).should.equal(false)
327
- })
328
- })
329
- })