@smartledger/bsv 7.1.0 → 7.2.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 +87 -0
- package/README.md +38 -38
- package/bsv-gdaf.min.js +27 -27
- package/bsv-ltp.min.js +27 -27
- package/bsv-smartcontract.min.js +1 -1
- package/bsv.bundle.js +27 -27
- package/bsv.d.ts +17 -1
- package/bsv.min.js +27 -27
- 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/ordinals/README.md +24 -1
- package/lib/ordinals/inscription.js +113 -23
- package/lib/ordinals/ordlock.js +1 -0
- package/package.json +1 -1
- package/test/ordinals/inscription.js +154 -0
- package/version.js +1 -1
|
@@ -68,4 +68,158 @@ describe('Ordinals inscriptions', function () {
|
|
|
68
68
|
Ord.parseInscription(outs[0].script).contentText.should.equal('a')
|
|
69
69
|
Ord.parseInscription(outs[1].script).contentText.should.equal('b')
|
|
70
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
|
+
})
|
|
71
225
|
})
|
package/version.js
CHANGED