UNPKG

21.7 kBJavaScriptView Raw
1/*
2 * Script
3 * ======
4 *
5 * Script is the scripting language built into bitcoin. The Script class lets
6 * you create an instance of a script, e.g. for a scriptSig or a scriptPubKey.
7 * It understands both the binary format, as well as two different string
8 * formats. The default string format, to/fromString, is a custom format only
9 * used by Yours Bitcoin because it is isomorphic to the binary format (or as
10 * isomorphic as it can be ... since OP_0 and OP_FALSE have the same byte
11 * value, and so do OP_1 and OP_TRUE). The bitcoind string format is also
12 * support, but that format is not isomorphic (i.e., if you pull in a string
13 * and then write it again, you are likely to get back a different string, even
14 * if you don't use OP_0, OP_FALSE, OP_1, or OP_TRUE).
15 */
16'use strict'
17
18import { Bn } from './bn'
19import { Br } from './br'
20import { Bw } from './bw'
21import { cmp } from './cmp'
22import { OpCode } from './op-code'
23import { PubKey } from './pub-key'
24import { Sig } from './sig'
25import { Struct } from './struct'
26
27class Script extends Struct {
28 constructor (chunks = []) {
29 super({ chunks })
30 }
31
32 fromJSON (json) {
33 return this.fromString(json)
34 }
35
36 toJSON () {
37 return this.toString()
38 }
39
40 fromBuffer (buf) {
41 this.chunks = []
42
43 const br = new Br(buf)
44 while (!br.eof()) {
45 const opCodeNum = br.readUInt8()
46
47 let len = 0
48 let buf = Buffer.from([])
49 if (opCodeNum > 0 && opCodeNum < OpCode.OP_PUSHDATA1) {
50 len = opCodeNum
51 this.chunks.push({
52 buf: br.read(len),
53 len: len,
54 opCodeNum: opCodeNum
55 })
56 } else if (opCodeNum === OpCode.OP_PUSHDATA1) {
57 try {
58 len = br.readUInt8()
59 buf = br.read(len)
60 } catch (err) {
61 br.read()
62 }
63 this.chunks.push({
64 buf: buf,
65 len: len,
66 opCodeNum: opCodeNum
67 })
68 } else if (opCodeNum === OpCode.OP_PUSHDATA2) {
69 try {
70 len = br.readUInt16LE()
71 buf = br.read(len)
72 } catch (err) {
73 br.read()
74 }
75 this.chunks.push({
76 buf: buf,
77 len: len,
78 opCodeNum: opCodeNum
79 })
80 } else if (opCodeNum === OpCode.OP_PUSHDATA4) {
81 try {
82 len = br.readUInt32LE()
83 buf = br.read(len)
84 } catch (err) {
85 br.read()
86 }
87 this.chunks.push({
88 buf: buf,
89 len: len,
90 opCodeNum: opCodeNum
91 })
92 } else {
93 this.chunks.push({
94 opCodeNum: opCodeNum
95 })
96 }
97 }
98
99 return this
100 }
101
102 toBuffer () {
103 const bw = new Bw()
104
105 for (let i = 0; i < this.chunks.length; i++) {
106 const chunk = this.chunks[i]
107 const opCodeNum = chunk.opCodeNum
108 bw.writeUInt8(opCodeNum)
109 if (chunk.buf) {
110 if (opCodeNum < OpCode.OP_PUSHDATA1) {
111 bw.write(chunk.buf)
112 } else if (opCodeNum === OpCode.OP_PUSHDATA1) {
113 bw.writeUInt8(chunk.len)
114 bw.write(chunk.buf)
115 } else if (opCodeNum === OpCode.OP_PUSHDATA2) {
116 bw.writeUInt16LE(chunk.len)
117 bw.write(chunk.buf)
118 } else if (opCodeNum === OpCode.OP_PUSHDATA4) {
119 bw.writeUInt32LE(chunk.len)
120 bw.write(chunk.buf)
121 }
122 }
123 }
124
125 return bw.toBuffer()
126 }
127
128 fromString (str) {
129 this.chunks = []
130 if (str === '' || str === undefined) {
131 return this
132 }
133
134 const tokens = str.split(' ')
135 let i = 0
136 while (i < tokens.length) {
137 const token = tokens[i]
138 let opCodeNum
139 try {
140 const opCode = new OpCode().fromString(token)
141 opCodeNum = opCode.toNumber()
142 } catch (err) {}
143
144 if (opCodeNum === undefined) {
145 opCodeNum = parseInt(token, 10)
146 if (opCodeNum > 0 && opCodeNum < OpCode.OP_PUSHDATA1) {
147 this.chunks.push({
148 buf: Buffer.from(tokens[i + 1].slice(2), 'hex'),
149 len: opCodeNum,
150 opCodeNum: opCodeNum
151 })
152 i = i + 2
153 } else if (opCodeNum === 0) {
154 this.chunks.push({
155 opCodeNum: 0
156 })
157 i = i + 1
158 } else {
159 throw new Error('Invalid script')
160 }
161 } else if (
162 opCodeNum === OpCode.OP_PUSHDATA1 ||
163 opCodeNum === OpCode.OP_PUSHDATA2 ||
164 opCodeNum === OpCode.OP_PUSHDATA4
165 ) {
166 if (tokens[i + 2].slice(0, 2) !== '0x') {
167 throw new Error('Pushdata data must start with 0x')
168 }
169 this.chunks.push({
170 buf: Buffer.from(tokens[i + 2].slice(2), 'hex'),
171 len: parseInt(tokens[i + 1], 10),
172 opCodeNum: opCodeNum
173 })
174 i = i + 3
175 } else {
176 this.chunks.push({
177 opCodeNum: opCodeNum
178 })
179 i = i + 1
180 }
181 }
182 return this
183 }
184
185 toString () {
186 let str = ''
187
188 for (let i = 0; i < this.chunks.length; i++) {
189 const chunk = this.chunks[i]
190 const opCodeNum = chunk.opCodeNum
191 if (!chunk.buf) {
192 if (OpCode.str[opCodeNum] !== undefined) {
193 str = str + ' ' + new OpCode(opCodeNum).toString()
194 } else {
195 str = str + ' ' + '0x' + opCodeNum.toString(16)
196 }
197 } else {
198 if (
199 opCodeNum === OpCode.OP_PUSHDATA1 ||
200 opCodeNum === OpCode.OP_PUSHDATA2 ||
201 opCodeNum === OpCode.OP_PUSHDATA4
202 ) {
203 str = str + ' ' + new OpCode(opCodeNum).toString()
204 }
205 str = str + ' ' + chunk.len
206 str = str + ' ' + '0x' + chunk.buf.toString('hex')
207 }
208 }
209
210 return str.substr(1)
211 }
212
213 /**
214 * Input the script from the script string format used in bitcoind data tests
215 */
216 fromBitcoindString (str) {
217 const bw = new Bw()
218 const tokens = str.split(' ')
219 let i
220 for (i = 0; i < tokens.length; i++) {
221 const token = tokens[i]
222 if (token === '') {
223 continue
224 }
225 if (token[0] === '0' && token[1] === 'x') {
226 const hex = token.slice(2)
227 bw.write(Buffer.from(hex, 'hex'))
228 } else if (token[0] === "'") {
229 const tstr = token.slice(1, token.length - 1)
230 const cbuf = Buffer.from(tstr)
231 const tbuf = new Script().writeBuffer(cbuf).toBuffer()
232 bw.write(tbuf)
233 } else if (OpCode['OP_' + token] !== undefined) {
234 const opstr = 'OP_' + token
235 const opCodeNum = OpCode[opstr]
236 bw.writeUInt8(opCodeNum)
237 } else if (typeof OpCode[token] === 'number') {
238 const opstr = token
239 const opCodeNum = OpCode[opstr]
240 bw.writeUInt8(opCodeNum)
241 } else if (!isNaN(parseInt(token, 10))) {
242 const bn = new Bn(token)
243 const script = new Script().writeBn(bn)
244 const tbuf = script.toBuffer()
245 bw.write(tbuf)
246 } else {
247 throw new Error('Could not determine type of script value')
248 }
249 }
250 const buf = bw.toBuffer()
251 return this.fromBuffer(buf)
252 }
253
254 static fromBitcoindString (str) {
255 return new this().fromBitcoindString(str)
256 }
257
258 /**
259 * Output the script to the script string format used in bitcoind data tests.
260 */
261 toBitcoindString () {
262 let str = ''
263 for (let i = 0; i < this.chunks.length; i++) {
264 const chunk = this.chunks[i]
265 if (chunk.buf) {
266 const buf = new Script([chunk]).toBuffer()
267 const hex = buf.toString('hex')
268 str = str + ' ' + '0x' + hex
269 } else if (OpCode.str[chunk.opCodeNum] !== undefined) {
270 const ostr = new OpCode(chunk.opCodeNum).toString()
271 str = str + ' ' + ostr.slice(3) // remove OP_
272 } else {
273 str = str + ' ' + '0x' + chunk.opCodeNum.toString(16)
274 }
275 }
276 return str.substr(1)
277 }
278
279 /**
280 * Input the script from the script string format used in bitcoind data tests
281 */
282 fromAsmString (str) {
283 this.chunks = []
284
285 const tokens = str.split(' ')
286 let i = 0
287 while (i < tokens.length) {
288 const token = tokens[i]
289 let opCode, opCodeNum
290 try {
291 opCode = OpCode.fromString(token)
292 opCodeNum = opCode.toNumber()
293 } catch (err) {
294 opCode = undefined
295 opCodeNum = undefined
296 }
297
298 // we start with two special cases, 0 and -1, which are handled specially in
299 // toASM. see _chunkToString.
300 if (token === '0') {
301 opCodeNum = 0
302 this.chunks.push({
303 opCodeNum: opCodeNum
304 })
305 i = i + 1
306 } else if (token === '-1') {
307 opCodeNum = OpCode.OP_1NEGATE
308 this.chunks.push({
309 opCodeNum: opCodeNum
310 })
311 i = i + 1
312 } else if (opCode === undefined) {
313 const hex = tokens[i]
314 const buf = Buffer.from(hex, 'hex')
315 if (buf.toString('hex') !== hex) {
316 throw new Error('invalid hex string in script')
317 }
318 const len = buf.length
319 if (len >= 0 && len < OpCode.OP_PUSHDATA1) {
320 opCodeNum = len
321 } else if (len < Math.pow(2, 8)) {
322 opCodeNum = OpCode.OP_PUSHDATA1
323 } else if (len < Math.pow(2, 16)) {
324 opCodeNum = OpCode.OP_PUSHDATA2
325 } else if (len < Math.pow(2, 32)) {
326 opCodeNum = OpCode.OP_PUSHDATA4
327 }
328 this.chunks.push({
329 buf: buf,
330 len: buf.length,
331 opCodeNum: opCodeNum
332 })
333 i = i + 1
334 } else {
335 this.chunks.push({
336 opCodeNum: opCodeNum
337 })
338 i = i + 1
339 }
340 }
341 return this
342 }
343
344 static fromAsmString (str) {
345 return new this().fromAsmString(str)
346 }
347
348 /**
349 * Output the script to the script string format used in bitcoind data tests.
350 */
351 toAsmString () {
352 var str = ''
353 for (var i = 0; i < this.chunks.length; i++) {
354 var chunk = this.chunks[i]
355 str += this._chunkToString(chunk)
356 }
357
358 return str.substr(1)
359 }
360
361 _chunkToString (chunk, type) {
362 var opCodeNum = chunk.opCodeNum
363 var str = ''
364 if (!chunk.buf) {
365 // no data chunk
366 if (typeof OpCode.str[opCodeNum] !== 'undefined') {
367 // A few cases where the opcode name differs from reverseMap
368 // aside from 1 to 16 data pushes.
369 if (opCodeNum === 0) {
370 // OP_0 -> 0
371 str = str + ' 0'
372 } else if (opCodeNum === 79) {
373 // OP_1NEGATE -> 1
374 str = str + ' -1'
375 } else {
376 str = str + ' ' + new OpCode(opCodeNum).toString()
377 }
378 } else {
379 var numstr = opCodeNum.toString(16)
380 if (numstr.length % 2 !== 0) {
381 numstr = '0' + numstr
382 }
383 str = str + ' ' + numstr
384 }
385 } else {
386 // data chunk
387 if (chunk.len > 0) {
388 str = str + ' ' + chunk.buf.toString('hex')
389 }
390 }
391 return str
392 }
393
394 fromOpReturnData (dataBuf) {
395 this.writeOpCode(OpCode.OP_RETURN)
396 this.writeBuffer(dataBuf)
397 return this
398 }
399
400 static fromOpReturnData (dataBuf) {
401 return new this().fromOpReturnData(dataBuf)
402 }
403
404 fromSafeData (dataBuf) {
405 this.writeOpCode(OpCode.OP_FALSE)
406 this.writeOpCode(OpCode.OP_RETURN)
407 this.writeBuffer(dataBuf)
408 return this
409 }
410
411 static fromSafeData (dataBuf) {
412 return new this().fromSafeData(dataBuf)
413 }
414
415 fromSafeDataArray (dataBufs) {
416 this.writeOpCode(OpCode.OP_FALSE)
417 this.writeOpCode(OpCode.OP_RETURN)
418 for (const i in dataBufs) {
419 const dataBuf = dataBufs[i]
420 this.writeBuffer(dataBuf)
421 }
422 return this
423 }
424
425 static fromSafeDataArray (dataBufs) {
426 return new this().fromSafeDataArray(dataBufs)
427 }
428
429 getData () {
430 if (this.isSafeDataOut()) {
431 const chunks = this.chunks.slice(2)
432 const buffers = chunks.map(chunk => chunk.buf)
433 return buffers
434 }
435 if (this.isOpReturn()) {
436 const chunks = this.chunks.slice(1)
437 const buffers = chunks.map(chunk => chunk.buf)
438 return buffers
439 }
440 throw new Error('Unrecognized script type to get data from')
441 }
442
443 /**
444 * Turn script into a standard pubKeyHash output script
445 */
446 fromPubKeyHash (hashBuf) {
447 if (hashBuf.length !== 20) {
448 throw new Error('hashBuf must be a 20 byte buffer')
449 }
450 this.writeOpCode(OpCode.OP_DUP)
451 this.writeOpCode(OpCode.OP_HASH160)
452 this.writeBuffer(hashBuf)
453 this.writeOpCode(OpCode.OP_EQUALVERIFY)
454 this.writeOpCode(OpCode.OP_CHECKSIG)
455 return this
456 }
457
458 static fromPubKeyHash (hashBuf) {
459 return new this().fromPubKeyHash(hashBuf)
460 }
461
462 static sortPubKeys (pubKeys) {
463 return pubKeys.slice().sort((pubKey1, pubKey2) => {
464 const buf1 = pubKey1.toBuffer()
465 const buf2 = pubKey2.toBuffer()
466 const len = Math.max(buf1.length, buf2.length)
467 for (let i = 0; i <= len; i++) {
468 if (buf1[i] === undefined) {
469 return -1 // shorter strings come first
470 }
471 if (buf2[i] === undefined) {
472 return 1
473 }
474 if (buf1[i] < buf2[i]) {
475 return -1
476 }
477 if (buf1[i] > buf2[i]) {
478 return 1
479 } else {
480 continue
481 }
482 }
483 })
484 }
485
486 /**
487 * Generate a multisig output script from a list of public keys. sort
488 * defaults to true. If sort is true, the pubKeys are sorted
489 * lexicographically.
490 */
491 fromPubKeys (m, pubKeys, sort = true) {
492 if (typeof m !== 'number') {
493 throw new Error('m must be a number')
494 }
495 if (sort === true) {
496 pubKeys = Script.sortPubKeys(pubKeys)
497 }
498 this.writeOpCode(m + OpCode.OP_1 - 1)
499 for (const i in pubKeys) {
500 this.writeBuffer(pubKeys[i].toBuffer())
501 }
502 this.writeOpCode(pubKeys.length + OpCode.OP_1 - 1)
503 this.writeOpCode(OpCode.OP_CHECKMULTISIG)
504 return this
505 }
506
507 static fromPubKeys (m, pubKeys, sort) {
508 return new this().fromPubKeys(m, pubKeys, sort)
509 }
510
511 removeCodeseparators () {
512 const chunks = []
513 for (let i = 0; i < this.chunks.length; i++) {
514 if (this.chunks[i].opCodeNum !== OpCode.OP_CODESEPARATOR) {
515 chunks.push(this.chunks[i])
516 }
517 }
518 this.chunks = chunks
519 return this
520 }
521
522 isPushOnly () {
523 for (let i = 0; i < this.chunks.length; i++) {
524 const chunk = this.chunks[i]
525 const opCodeNum = chunk.opCodeNum
526 if (opCodeNum > OpCode.OP_16) {
527 return false
528 }
529 }
530 return true
531 }
532
533 isOpReturn () {
534 if (
535 this.chunks[0].opCodeNum === OpCode.OP_RETURN &&
536 this.chunks.filter(chunk => Buffer.isBuffer(chunk.buf)).length === this.chunks.slice(1).length
537 ) {
538 return true
539 } else {
540 return false
541 }
542 }
543
544 isSafeDataOut () {
545 if (this.chunks.length < 2) {
546 return false
547 }
548 if (this.chunks[0].opCodeNum !== OpCode.OP_FALSE) {
549 return false
550 }
551 var chunks = this.chunks.slice(1)
552 var script2 = new Script(chunks)
553 return script2.isOpReturn()
554 }
555
556 isPubKeyHashOut () {
557 if (
558 this.chunks[0] &&
559 this.chunks[0].opCodeNum === OpCode.OP_DUP &&
560 this.chunks[1] &&
561 this.chunks[1].opCodeNum === OpCode.OP_HASH160 &&
562 this.chunks[2].buf &&
563 this.chunks[3] &&
564 this.chunks[3].opCodeNum === OpCode.OP_EQUALVERIFY &&
565 this.chunks[4] &&
566 this.chunks[4].opCodeNum === OpCode.OP_CHECKSIG
567 ) {
568 return true
569 } else {
570 return false
571 }
572 }
573
574 /**
575 * A pubKeyHash input should consist of two push operations. The first push
576 * operation may be OP_0, which means the signature is missing, which is true
577 * for some partially signed (and invalid) transactions.
578 */
579 isPubKeyHashIn () {
580 if (
581 this.chunks.length === 2 &&
582 (this.chunks[0].buf || this.chunks[0].opCodeNum === OpCode.OP_0) &&
583 (this.chunks[1].buf || this.chunks[0].opCodeNum === OpCode.OP_0)
584 ) {
585 return true
586 } else {
587 return false
588 }
589 }
590
591 isScriptHashOut () {
592 const buf = this.toBuffer()
593 return (
594 buf.length === 23 &&
595 buf[0] === OpCode.OP_HASH160 &&
596 buf[1] === 0x14 &&
597 buf[22] === OpCode.OP_EQUAL
598 )
599 }
600
601 /**
602 * Note that these are frequently indistinguishable from pubKeyHashin
603 */
604 isScriptHashIn () {
605 if (!this.isPushOnly()) {
606 return false
607 }
608 try {
609 new Script().fromBuffer(this.chunks[this.chunks.length - 1].buf)
610 } catch (err) {
611 return false
612 }
613 return true
614 }
615
616 isMultiSigOut () {
617 const m = this.chunks[0].opCodeNum - OpCode.OP_1 + 1
618 if (!(m >= 1 && m <= 16)) {
619 return false
620 }
621 const pubKeychunks = this.chunks.slice(1, this.chunks.length - 2)
622 if (
623 !pubKeychunks.every(chunk => {
624 try {
625 const buf = chunk.buf
626 const pubKey = new PubKey().fromDer(buf)
627 pubKey.validate()
628 return true
629 } catch (err) {
630 return false
631 }
632 })
633 ) {
634 return false
635 }
636 const n = this.chunks[this.chunks.length - 2].opCodeNum - OpCode.OP_1 + 1
637 if (!(n >= m && n <= 16)) {
638 return false
639 }
640 if (this.chunks[1 + n + 1].opCodeNum !== OpCode.OP_CHECKMULTISIG) {
641 return false
642 }
643 return true
644 }
645
646 isMultiSigIn () {
647 if (this.chunks[0].opCodeNum !== OpCode.OP_0) {
648 return false
649 }
650 const remaining = this.chunks.slice(1)
651 if (remaining.length < 1) {
652 return false
653 }
654 return remaining.every(
655 chunk => Buffer.isBuffer(chunk.buf) && Sig.IsTxDer(chunk.buf)
656 )
657 }
658
659 /**
660 * Analagous to bitcoind's FindAndDelete Find and deconste equivalent chunks,
661 * typically used with push data chunks. Note that this will find and deconste
662 * not just the same data, but the same data with the same push data op as
663 * produced by default. i.e., if a pushdata in a tx does not use the minimal
664 * pushdata op, then when you try to remove the data it is pushing, it will not
665 * be removed, because they do not use the same pushdata op.
666 */
667 findAndDelete (script) {
668 const buf = script.toBuffer()
669 for (let i = 0; i < this.chunks.length; i++) {
670 const script2 = new Script([this.chunks[i]])
671 const buf2 = script2.toBuffer()
672 if (cmp(buf, buf2)) {
673 this.chunks.splice(i, 1)
674 }
675 }
676 return this
677 }
678
679 writeScript (script) {
680 this.chunks = this.chunks.concat(script.chunks)
681 return this
682 }
683
684 static writeScript (script) {
685 return new this().writeScript(script)
686 }
687
688 writeString (str) {
689 const script = new Script().fromString(str)
690 this.chunks = this.chunks.concat(script.chunks)
691 return this
692 }
693
694 static writeString (str) {
695 return new this().writeString(str)
696 }
697
698 writeOpCode (opCodeNum) {
699 this.chunks.push({ opCodeNum })
700 return this
701 }
702
703 static writeOpCode (opCodeNum) {
704 return new this().writeOpCode(opCodeNum)
705 }
706
707 setChunkOpCode (i, opCodeNum) {
708 this.chunks[i] = { opCodeNum }
709 return this
710 }
711
712 // write a big number in the minimal way
713 writeBn (bn) {
714 if (bn.cmp(0) === OpCode.OP_0) {
715 this.chunks.push({
716 opCodeNum: OpCode.OP_0
717 })
718 } else if (bn.cmp(-1) === 0) {
719 this.chunks.push({
720 opCodeNum: OpCode.OP_1NEGATE
721 })
722 } else if (bn.cmp(1) >= 0 && bn.cmp(16) <= 0) {
723 // see OP_1 - OP_16
724 this.chunks.push({
725 opCodeNum: bn.toNumber() + OpCode.OP_1 - 1
726 })
727 } else {
728 const buf = bn.toSm({ endian: 'little' })
729 this.writeBuffer(buf)
730 }
731 return this
732 }
733
734 static writeBn (bn) {
735 return new this().writeBn(bn)
736 }
737
738 writeNumber (number) {
739 this.writeBn(new Bn().fromNumber(number))
740 return this
741 }
742
743 static writeNumber (number) {
744 return new this().writeNumber(number)
745 }
746
747 setChunkBn (i, bn) {
748 this.chunks[i] = new Script().writeBn(bn).chunks[0]
749 return this
750 }
751
752 // note: this does not necessarily write buffers in the minimal way
753 // to write numbers in the minimal way, see writeBn
754 writeBuffer (buf) {
755 let opCodeNum
756 const len = buf.length
757 if (buf.length > 0 && buf.length < OpCode.OP_PUSHDATA1) {
758 opCodeNum = buf.length
759 } else if (buf.length === 0) {
760 opCodeNum = OpCode.OP_0
761 } else if (buf.length < Math.pow(2, 8)) {
762 opCodeNum = OpCode.OP_PUSHDATA1
763 } else if (buf.length < Math.pow(2, 16)) {
764 opCodeNum = OpCode.OP_PUSHDATA2
765 } else if (buf.length < Math.pow(2, 32)) {
766 opCodeNum = OpCode.OP_PUSHDATA4
767 } else {
768 throw new Error("You can't push that much data")
769 }
770 this.chunks.push({
771 buf: buf,
772 len: len,
773 opCodeNum: opCodeNum
774 })
775 return this
776 }
777
778 static writeBuffer (buf) {
779 return new this().writeBuffer(buf)
780 }
781
782 setChunkBuffer (i, buf) {
783 this.chunks[i] = new Script().writeBuffer(buf).chunks[0]
784 return this
785 }
786
787 // make sure a push is the smallest way to push that particular data
788 // comes from bitcoind's script interpreter CheckMinimalPush function
789 checkMinimalPush (i) {
790 const chunk = this.chunks[i]
791 const buf = chunk.buf
792 const opCodeNum = chunk.opCodeNum
793 if (!buf) {
794 return true
795 }
796 if (buf.length === 0) {
797 // Could have used OP_0.
798 return opCodeNum === OpCode.OP_0
799 } else if (buf.length === 1 && buf[0] >= 1 && buf[0] <= 16) {
800 // Could have used OP_1 .. OP_16.
801 return opCodeNum === OpCode.OP_1 + (buf[0] - 1)
802 } else if (buf.length === 1 && buf[0] === 0x81) {
803 // Could have used OP_1NEGATE.
804 return opCodeNum === OpCode.OP_1NEGATE
805 } else if (buf.length <= 75) {
806 // Could have used a direct push (opCode indicating number of bytes pushed + those bytes).
807 return opCodeNum === buf.length
808 } else if (buf.length <= 255) {
809 // Could have used OP_PUSHDATA.
810 return opCodeNum === OpCode.OP_PUSHDATA1
811 } else if (buf.length <= 65535) {
812 // Could have used OP_PUSHDATA2.
813 return opCodeNum === OpCode.OP_PUSHDATA2
814 }
815 return true
816 }
817}
818
819export { Script }