@vbyte/btc-dev 1.0.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/LICENSE +121 -0
- package/README.md +5 -0
- package/dist/class/index.d.ts +5 -0
- package/dist/class/index.js +5 -0
- package/dist/class/signer.d.ts +18 -0
- package/dist/class/signer.js +32 -0
- package/dist/class/tx.d.ts +38 -0
- package/dist/class/tx.js +73 -0
- package/dist/class/txin.d.ts +29 -0
- package/dist/class/txin.js +68 -0
- package/dist/class/txout.d.ts +18 -0
- package/dist/class/txout.js +38 -0
- package/dist/class/witness.d.ts +20 -0
- package/dist/class/witness.js +57 -0
- package/dist/const.d.ts +22 -0
- package/dist/const.js +36 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/lib/address/encode.d.ts +3 -0
- package/dist/lib/address/encode.js +79 -0
- package/dist/lib/address/index.d.ts +20 -0
- package/dist/lib/address/index.js +21 -0
- package/dist/lib/address/p2pkh.d.ts +10 -0
- package/dist/lib/address/p2pkh.js +33 -0
- package/dist/lib/address/p2sh.d.ts +10 -0
- package/dist/lib/address/p2sh.js +33 -0
- package/dist/lib/address/p2tr.d.ts +8 -0
- package/dist/lib/address/p2tr.js +26 -0
- package/dist/lib/address/p2wpkh.d.ts +10 -0
- package/dist/lib/address/p2wpkh.js +34 -0
- package/dist/lib/address/p2wsh.d.ts +10 -0
- package/dist/lib/address/p2wsh.js +33 -0
- package/dist/lib/address/script.d.ts +5 -0
- package/dist/lib/address/script.js +46 -0
- package/dist/lib/address/util.d.ts +4 -0
- package/dist/lib/address/util.js +57 -0
- package/dist/lib/meta/index.d.ts +2 -0
- package/dist/lib/meta/index.js +2 -0
- package/dist/lib/meta/pointer.d.ts +42 -0
- package/dist/lib/meta/pointer.js +69 -0
- package/dist/lib/meta/scribe.d.ts +7 -0
- package/dist/lib/meta/scribe.js +192 -0
- package/dist/lib/psbt/encoder.d.ts +5 -0
- package/dist/lib/psbt/encoder.js +21 -0
- package/dist/lib/psbt/index.d.ts +4 -0
- package/dist/lib/psbt/index.js +4 -0
- package/dist/lib/psbt/meta.d.ts +3 -0
- package/dist/lib/psbt/meta.js +11 -0
- package/dist/lib/psbt/util.d.ts +5 -0
- package/dist/lib/psbt/util.js +44 -0
- package/dist/lib/psbt/validate.d.ts +2 -0
- package/dist/lib/psbt/validate.js +11 -0
- package/dist/lib/script/decode.d.ts +2 -0
- package/dist/lib/script/decode.js +55 -0
- package/dist/lib/script/encode.d.ts +6 -0
- package/dist/lib/script/encode.js +80 -0
- package/dist/lib/script/index.d.ts +126 -0
- package/dist/lib/script/index.js +17 -0
- package/dist/lib/script/util.d.ts +2 -0
- package/dist/lib/script/util.js +10 -0
- package/dist/lib/script/words.d.ts +116 -0
- package/dist/lib/script/words.js +164 -0
- package/dist/lib/sighash/index.d.ts +5 -0
- package/dist/lib/sighash/index.js +5 -0
- package/dist/lib/sighash/segwit.d.ts +3 -0
- package/dist/lib/sighash/segwit.js +89 -0
- package/dist/lib/sighash/sign.d.ts +3 -0
- package/dist/lib/sighash/sign.js +20 -0
- package/dist/lib/sighash/taproot.d.ts +9 -0
- package/dist/lib/sighash/taproot.js +124 -0
- package/dist/lib/sighash/util.d.ts +3 -0
- package/dist/lib/sighash/util.js +16 -0
- package/dist/lib/sighash/verify.d.ts +1 -0
- package/dist/lib/sighash/verify.js +1 -0
- package/dist/lib/taproot/cblock.d.ts +3 -0
- package/dist/lib/taproot/cblock.js +55 -0
- package/dist/lib/taproot/encode.d.ts +6 -0
- package/dist/lib/taproot/encode.js +26 -0
- package/dist/lib/taproot/index.d.ts +4 -0
- package/dist/lib/taproot/index.js +4 -0
- package/dist/lib/taproot/parse.d.ts +11 -0
- package/dist/lib/taproot/parse.js +47 -0
- package/dist/lib/taproot/tree.d.ts +3 -0
- package/dist/lib/taproot/tree.js +49 -0
- package/dist/lib/tx/create.d.ts +6 -0
- package/dist/lib/tx/create.js +54 -0
- package/dist/lib/tx/decode.d.ts +9 -0
- package/dist/lib/tx/decode.js +109 -0
- package/dist/lib/tx/encode.d.ts +15 -0
- package/dist/lib/tx/encode.js +94 -0
- package/dist/lib/tx/index.d.ts +10 -0
- package/dist/lib/tx/index.js +10 -0
- package/dist/lib/tx/locktime.d.ts +7 -0
- package/dist/lib/tx/locktime.js +37 -0
- package/dist/lib/tx/meta.d.ts +8 -0
- package/dist/lib/tx/meta.js +48 -0
- package/dist/lib/tx/parse.d.ts +2 -0
- package/dist/lib/tx/parse.js +12 -0
- package/dist/lib/tx/sequence.d.ts +7 -0
- package/dist/lib/tx/sequence.js +65 -0
- package/dist/lib/tx/size.d.ts +10 -0
- package/dist/lib/tx/size.js +48 -0
- package/dist/lib/tx/validate.d.ts +7 -0
- package/dist/lib/tx/validate.js +21 -0
- package/dist/lib/tx/witness.d.ts +3 -0
- package/dist/lib/tx/witness.js +85 -0
- package/dist/main.cjs +14625 -0
- package/dist/main.cjs.map +1 -0
- package/dist/module.mjs +14610 -0
- package/dist/module.mjs.map +1 -0
- package/dist/package.json +106 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/taproot.d.ts +18 -0
- package/dist/schema/taproot.js +9 -0
- package/dist/schema/tx.d.ts +278 -0
- package/dist/schema/tx.js +35 -0
- package/dist/script.js +15 -0
- package/dist/script.js.map +1 -0
- package/dist/types/address.d.ts +34 -0
- package/dist/types/address.js +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +9 -0
- package/dist/types/meta.d.ts +10 -0
- package/dist/types/meta.js +1 -0
- package/dist/types/psbt.d.ts +9 -0
- package/dist/types/psbt.js +1 -0
- package/dist/types/sighash.d.ts +14 -0
- package/dist/types/sighash.js +1 -0
- package/dist/types/taproot.d.ts +35 -0
- package/dist/types/taproot.js +1 -0
- package/dist/types/transaction.d.ts +81 -0
- package/dist/types/transaction.js +1 -0
- package/dist/types/txdata.d.ts +45 -0
- package/dist/types/txdata.js +1 -0
- package/dist/types/txmeta.d.ts +19 -0
- package/dist/types/txmeta.js +1 -0
- package/dist/types/witness.d.ts +30 -0
- package/dist/types/witness.js +1 -0
- package/package.json +106 -0
- package/src/class/index.ts +5 -0
- package/src/class/signer.ts +47 -0
- package/src/class/tx.ts +118 -0
- package/src/class/txin.ts +95 -0
- package/src/class/txout.ts +57 -0
- package/src/class/witness.ts +85 -0
- package/src/const.ts +43 -0
- package/src/index.ts +14 -0
- package/src/lib/address/encode.ts +183 -0
- package/src/lib/address/index.ts +24 -0
- package/src/lib/address/p2pkh.ts +65 -0
- package/src/lib/address/p2sh.ts +65 -0
- package/src/lib/address/p2tr.ts +51 -0
- package/src/lib/address/p2wpkh.ts +67 -0
- package/src/lib/address/p2wsh.ts +65 -0
- package/src/lib/address/script.ts +63 -0
- package/src/lib/address/util.ts +102 -0
- package/src/lib/meta/index.ts +2 -0
- package/src/lib/meta/pointer.ts +107 -0
- package/src/lib/meta/scribe.ts +251 -0
- package/src/lib/psbt/encoder.ts +24 -0
- package/src/lib/psbt/index.ts +4 -0
- package/src/lib/psbt/meta.ts +15 -0
- package/src/lib/psbt/util.ts +62 -0
- package/src/lib/psbt/validate.ts +18 -0
- package/src/lib/script/decode.ts +75 -0
- package/src/lib/script/encode.ts +130 -0
- package/src/lib/script/index.ts +26 -0
- package/src/lib/script/util.ts +78 -0
- package/src/lib/script/words.ts +182 -0
- package/src/lib/sighash/index.ts +5 -0
- package/src/lib/sighash/segwit.ts +152 -0
- package/src/lib/sighash/sign.ts +35 -0
- package/src/lib/sighash/taproot.ts +236 -0
- package/src/lib/sighash/util.ts +29 -0
- package/src/lib/sighash/verify.ts +83 -0
- package/src/lib/taproot/cblock.ts +95 -0
- package/src/lib/taproot/encode.ts +49 -0
- package/src/lib/taproot/index.ts +4 -0
- package/src/lib/taproot/parse.ts +65 -0
- package/src/lib/taproot/tree.ts +94 -0
- package/src/lib/tx/create.ts +82 -0
- package/src/lib/tx/decode.ts +145 -0
- package/src/lib/tx/encode.ts +154 -0
- package/src/lib/tx/index.ts +10 -0
- package/src/lib/tx/locktime.ts +57 -0
- package/src/lib/tx/meta.ts +73 -0
- package/src/lib/tx/parse.ts +16 -0
- package/src/lib/tx/sequence.ts +146 -0
- package/src/lib/tx/size.ts +77 -0
- package/src/lib/tx/validate.ts +36 -0
- package/src/lib/tx/witness.ts +122 -0
- package/src/schema/index.ts +2 -0
- package/src/schema/taproot.ts +12 -0
- package/src/schema/tx.ts +42 -0
- package/src/types/address.ts +39 -0
- package/src/types/index.ts +9 -0
- package/src/types/meta.ts +10 -0
- package/src/types/psbt.ts +15 -0
- package/src/types/sighash.ts +16 -0
- package/src/types/taproot.ts +40 -0
- package/src/types/transaction.ts +98 -0
- package/src/types/txdata.ts +53 -0
- package/src/types/txmeta.ts +25 -0
- package/src/types/witness.ts +36 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
2
|
+
import { parse_psbt } from './encoder.js'
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
PSBTData,
|
|
6
|
+
PSBTInput,
|
|
7
|
+
PSBTOutput,
|
|
8
|
+
PSBTPrevouts
|
|
9
|
+
} from '@/types/index.js'
|
|
10
|
+
|
|
11
|
+
export function collect_vins (
|
|
12
|
+
psbt : string | PSBTData
|
|
13
|
+
) : PSBTInput[] {
|
|
14
|
+
const pdata = parse_psbt(psbt)
|
|
15
|
+
const count = pdata.inputsLength
|
|
16
|
+
const vins : PSBTInput[] = []
|
|
17
|
+
for (let i = 0; i < count; i++) {
|
|
18
|
+
const vin = pdata.getInput(i)
|
|
19
|
+
vins.push(vin)
|
|
20
|
+
}
|
|
21
|
+
return vins
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function collect_vouts (
|
|
25
|
+
psbt : string | PSBTData
|
|
26
|
+
) : PSBTOutput[] {
|
|
27
|
+
const pdata = parse_psbt(psbt)
|
|
28
|
+
const count = pdata.outputsLength
|
|
29
|
+
const vouts : PSBTOutput[] = []
|
|
30
|
+
for (let i = 0; i < count; i++) {
|
|
31
|
+
const vout = pdata.getOutput(i)
|
|
32
|
+
vouts.push(vout)
|
|
33
|
+
}
|
|
34
|
+
return vouts
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function collect_prevouts (
|
|
38
|
+
psbt : PSBTData
|
|
39
|
+
) : PSBTPrevouts {
|
|
40
|
+
const amounts : bigint[] = [],
|
|
41
|
+
scripts : Uint8Array[] = []
|
|
42
|
+
const pdata = parse_psbt(psbt)
|
|
43
|
+
for (let i = 0; i < pdata.inputsLength; i++) {
|
|
44
|
+
const txin = pdata.getInput(i)
|
|
45
|
+
Assert.exists(txin.witnessUtxo, `witness utxo does not exist for input ${i}`)
|
|
46
|
+
amounts.push(txin.witnessUtxo.amount)
|
|
47
|
+
scripts.push(txin.witnessUtxo.script)
|
|
48
|
+
}
|
|
49
|
+
return { amounts, scripts }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function finalize_legacy_inputs (pdata : PSBTData) {
|
|
53
|
+
for (let i = 0; i < pdata.inputsLength; i++) {
|
|
54
|
+
const pvin = pdata.getInput(i)
|
|
55
|
+
const script = pvin.redeemScript
|
|
56
|
+
const psig = pvin.partialSig?.at(0)
|
|
57
|
+
if (script !== undefined && psig !== undefined) {
|
|
58
|
+
pdata.finalizeIdx(i)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return pdata
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
2
|
+
import { parse_psbt } from './encoder.js'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
collect_vins,
|
|
6
|
+
collect_vouts
|
|
7
|
+
} from './util.js'
|
|
8
|
+
|
|
9
|
+
import type { PSBTData } from '@/types/index.js'
|
|
10
|
+
|
|
11
|
+
export function assert_psbt_is_funded (psbt : string | PSBTData) : void {
|
|
12
|
+
const pdata = parse_psbt(psbt)
|
|
13
|
+
const pvouts = collect_vins(pdata)
|
|
14
|
+
const txouts = collect_vouts(pdata)
|
|
15
|
+
const vin_amt = pvouts.reduce((p, n) => p + Number(n.witnessUtxo?.amount ?? 0), 0)
|
|
16
|
+
const out_amt = txouts.reduce((p, n) => p + Number(n.amount ?? 0), 0)
|
|
17
|
+
Assert.ok(vin_amt >= out_amt, `value in (${vin_amt}) < value out (${out_amt})`)
|
|
18
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Stream } from '@vbyte/buff'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
get_op_code,
|
|
5
|
+
get_op_type,
|
|
6
|
+
is_valid_op
|
|
7
|
+
} from './words.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Decode a bitcoin script into asm instructions.
|
|
11
|
+
*/
|
|
12
|
+
export function decode_script (
|
|
13
|
+
script : string | Uint8Array
|
|
14
|
+
) : string[] {
|
|
15
|
+
const stream = new Stream(script)
|
|
16
|
+
|
|
17
|
+
const stack : string[] = []
|
|
18
|
+
const stack_size = stream.size
|
|
19
|
+
|
|
20
|
+
let word : number
|
|
21
|
+
let word_type : string
|
|
22
|
+
let word_size : number
|
|
23
|
+
|
|
24
|
+
let count = 0
|
|
25
|
+
|
|
26
|
+
while (count < stack_size) {
|
|
27
|
+
word = stream.read(1).num
|
|
28
|
+
word_type = get_op_type(word)
|
|
29
|
+
count++
|
|
30
|
+
switch (word_type) {
|
|
31
|
+
case 'varint':
|
|
32
|
+
stack.push(stream.read(word).hex)
|
|
33
|
+
count += word
|
|
34
|
+
break
|
|
35
|
+
case 'pushdata1':
|
|
36
|
+
word_size = stream.read(1).reverse().num
|
|
37
|
+
stack.push(stream.read(word_size).hex)
|
|
38
|
+
count += word_size + 1
|
|
39
|
+
break
|
|
40
|
+
case 'pushdata2':
|
|
41
|
+
word_size = stream.read(2).reverse().num
|
|
42
|
+
stack.push(stream.read(word_size).hex)
|
|
43
|
+
count += word_size + 2
|
|
44
|
+
break
|
|
45
|
+
case 'pushdata4':
|
|
46
|
+
word_size = stream.read(4).reverse().num
|
|
47
|
+
stack.push(stream.read(word_size).hex)
|
|
48
|
+
count += word_size + 4
|
|
49
|
+
break
|
|
50
|
+
case 'opcode':
|
|
51
|
+
if (!is_valid_op(word)) {
|
|
52
|
+
throw new Error(`Invalid OPCODE: ${word}`)
|
|
53
|
+
}
|
|
54
|
+
stack.push(get_op_code(word))
|
|
55
|
+
break
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(`Word type undefined: ${word}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return stack
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if a script is valid.
|
|
65
|
+
*/
|
|
66
|
+
export function is_valid_script (
|
|
67
|
+
script : string | Uint8Array
|
|
68
|
+
) : boolean {
|
|
69
|
+
try {
|
|
70
|
+
const stack = decode_script(script)
|
|
71
|
+
return stack.length > 0
|
|
72
|
+
} catch {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Buff, Stream } from '@vbyte/buff'
|
|
2
|
+
import { get_asm_code, } from './words.js'
|
|
3
|
+
|
|
4
|
+
// The maximum size of a word in bytes.
|
|
5
|
+
const MAX_WORD_SIZE = 520
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Encode script asm instructions into a hex string.
|
|
9
|
+
*/
|
|
10
|
+
export function encode_script (
|
|
11
|
+
words : (string | number | Uint8Array)[],
|
|
12
|
+
varint = false
|
|
13
|
+
) : string {
|
|
14
|
+
if (words.length === 0) return '00'
|
|
15
|
+
|
|
16
|
+
const bytes = []
|
|
17
|
+
|
|
18
|
+
for (const word of words) {
|
|
19
|
+
|
|
20
|
+
bytes.push(encode_script_word(word))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const buffer = Buff.join(bytes)
|
|
24
|
+
|
|
25
|
+
return (varint)
|
|
26
|
+
? buffer.prepend(Buff.varint(buffer.length, 'le')).hex
|
|
27
|
+
: buffer.hex
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/** Check if the word is a valid opcode,
|
|
32
|
+
* and return its integer value.
|
|
33
|
+
*/
|
|
34
|
+
export function encode_script_word (word : string | number | Uint8Array) : Uint8Array {
|
|
35
|
+
let buff : Buff
|
|
36
|
+
|
|
37
|
+
// If word is a string:
|
|
38
|
+
if (typeof (word) === 'string') {
|
|
39
|
+
// If word is an opcode:
|
|
40
|
+
if (word.startsWith('OP_')) {
|
|
41
|
+
// Get the opcode number value.
|
|
42
|
+
const asm_code = get_asm_code(word)
|
|
43
|
+
// Return the opcode as a single byte.
|
|
44
|
+
return Buff.num(asm_code, 1)
|
|
45
|
+
// If word is valid hex:
|
|
46
|
+
} else if (Buff.is_hex(word)) {
|
|
47
|
+
// Encode as hex.
|
|
48
|
+
buff = Buff.hex(word)
|
|
49
|
+
} else {
|
|
50
|
+
// Encode as UTF8 string.
|
|
51
|
+
buff = Buff.str(word)
|
|
52
|
+
}
|
|
53
|
+
// If word is a number:
|
|
54
|
+
} else if (typeof (word) === 'number') {
|
|
55
|
+
// Encode the number value.
|
|
56
|
+
buff = Buff.num(word)
|
|
57
|
+
// If word is a Uint8Array:
|
|
58
|
+
} else if (word instanceof Uint8Array) {
|
|
59
|
+
// Encode as bytes.
|
|
60
|
+
buff = new Buff(word)
|
|
61
|
+
} else {
|
|
62
|
+
// If word is not a string, number, or Uint8Array, throw an error.
|
|
63
|
+
throw new Error('invalid word type:' + typeof (word))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Format and return the word based on its size.
|
|
67
|
+
if (buff.length === 1 && buff[0] <= 16) {
|
|
68
|
+
// Number values 0-16 must be treated as opcodes.
|
|
69
|
+
if (buff[0] !== 0) buff[0] += 0x50
|
|
70
|
+
} else if (buff.length > MAX_WORD_SIZE) {
|
|
71
|
+
// Number values larger than max size must be split into chunks.
|
|
72
|
+
let words : Buff[]
|
|
73
|
+
// Split bytes into chunks, based on max word size.
|
|
74
|
+
words = split_script_word(buff)
|
|
75
|
+
// Prefix a varint length byte for each chunk.
|
|
76
|
+
words = words.map(e => prefix_word_size(e))
|
|
77
|
+
// Concatenate the chunks
|
|
78
|
+
buff = Buff.join(words)
|
|
79
|
+
} else {
|
|
80
|
+
// Else, return the word with a varint prefix.
|
|
81
|
+
buff = prefix_word_size(buff)
|
|
82
|
+
}
|
|
83
|
+
// Return the final result.
|
|
84
|
+
return buff
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Split a word into smaller chunks.
|
|
89
|
+
*/
|
|
90
|
+
export function split_script_word (
|
|
91
|
+
word : Uint8Array
|
|
92
|
+
) : Buff[] {
|
|
93
|
+
const words = []
|
|
94
|
+
const buff = new Stream(word)
|
|
95
|
+
while (buff.size > MAX_WORD_SIZE) {
|
|
96
|
+
// Push a word chunk to the array.
|
|
97
|
+
words.push(buff.read(MAX_WORD_SIZE))
|
|
98
|
+
}
|
|
99
|
+
// Push the remainder to the array.
|
|
100
|
+
words.push(buff.read(buff.size))
|
|
101
|
+
return words
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Prefix a word with its size, encoded as a varint.
|
|
106
|
+
*/
|
|
107
|
+
export function prefix_word_size (
|
|
108
|
+
word : Uint8Array
|
|
109
|
+
) : Buff {
|
|
110
|
+
const varint = get_size_varint(word.length)
|
|
111
|
+
return Buff.join([ varint, word ])
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Return a varint that encodes a size value.
|
|
116
|
+
*/
|
|
117
|
+
export function get_size_varint (size : number) : Buff {
|
|
118
|
+
const OP_PUSHDATA1 = Buff.num(0x4c, 1)
|
|
119
|
+
const OP_PUSHDATA2 = Buff.num(0x4d, 1)
|
|
120
|
+
switch (true) {
|
|
121
|
+
case (size <= 0x4b):
|
|
122
|
+
return Buff.num(size)
|
|
123
|
+
case (size > 0x4b && size < 0x100):
|
|
124
|
+
return Buff.join([ OP_PUSHDATA1, Buff.num(size, 1, 'le') ])
|
|
125
|
+
case (size >= 0x100 && size <= MAX_WORD_SIZE):
|
|
126
|
+
return Buff.join([ OP_PUSHDATA2, Buff.num(size, 2, 'le') ])
|
|
127
|
+
default:
|
|
128
|
+
throw new Error('Invalid word size:' + size.toString())
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OPCODE_MAP } from './words.js'
|
|
2
|
+
import { encode_script } from './encode.js'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
decode_script,
|
|
6
|
+
is_valid_script
|
|
7
|
+
} from './decode.js'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
parse_script_pubkeys,
|
|
11
|
+
prefix_script_size
|
|
12
|
+
} from './util.js'
|
|
13
|
+
|
|
14
|
+
export * from './decode.js'
|
|
15
|
+
export * from './encode.js'
|
|
16
|
+
export * from './util.js'
|
|
17
|
+
export * from './words.js'
|
|
18
|
+
|
|
19
|
+
export namespace ScriptUtil {
|
|
20
|
+
export const prefix_size = prefix_script_size
|
|
21
|
+
export const decode = decode_script
|
|
22
|
+
export const encode = encode_script
|
|
23
|
+
export const is_valid = is_valid_script
|
|
24
|
+
export const get_pubkeys = parse_script_pubkeys
|
|
25
|
+
export const OPCODES = OPCODE_MAP
|
|
26
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
|
|
3
|
+
export function prefix_script_size (script: string | Uint8Array): string {
|
|
4
|
+
return Buff.bytes(script).prefix_varint('le').hex
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function parse_script_pubkeys (script: string | Uint8Array): string[] {
|
|
8
|
+
// Convert the script to a string if it's a Uint8Array
|
|
9
|
+
const scriptHex = typeof script === 'string' ? script : Buff.bytes(script).hex
|
|
10
|
+
|
|
11
|
+
// Define the regex pattern to match the specified pattern
|
|
12
|
+
// 20 = pushdata byte for 32 bytes (0x20)
|
|
13
|
+
// [0-9a-f]{64} = 32-byte hex string (64 hex characters)
|
|
14
|
+
// (ac|ad|ba) = OP_CHECKSIG (0xac), OP_CHECKSIGVERIFY (0xad), or OP_CHECKSIGADD (0xba)
|
|
15
|
+
const pubkeyPattern = /20([0-9a-f]{64})(ac|ad|ba)/gi
|
|
16
|
+
|
|
17
|
+
// Find all matches in the script
|
|
18
|
+
const matches = [...scriptHex.matchAll(pubkeyPattern)]
|
|
19
|
+
|
|
20
|
+
// Extract the public keys from the matches
|
|
21
|
+
return matches.map(match => match[1])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// export function parse_witness_pubkeys (
|
|
25
|
+
// witness : string[] | TxWitnessData
|
|
26
|
+
// ) : string[] {
|
|
27
|
+
// // Parse the witness data if it is an array.
|
|
28
|
+
// if (witness instanceof Array) {
|
|
29
|
+
// witness = parse_witness_data(witness)
|
|
30
|
+
// }
|
|
31
|
+
// // Define the set of pubkeys.
|
|
32
|
+
// const pubkeys = new Set<string>()
|
|
33
|
+
// // Get the witness type.
|
|
34
|
+
// const type = witness.type
|
|
35
|
+
// // If the witness type is a p2tr-ts:
|
|
36
|
+
// if (type === 'p2tr-ts') {
|
|
37
|
+
// // Parse the internal pubkey from the cblock.
|
|
38
|
+
// const int_pk = witness.cblock?.slice(2, 66)
|
|
39
|
+
// // If the internal pubkey is present, add it to the list.
|
|
40
|
+
// if (int_pk !== undefined) pubkeys.add(int_pk)
|
|
41
|
+
// // Parse any pubkeys witness parameters.
|
|
42
|
+
// witness.params
|
|
43
|
+
// .filter(p => p.length === 64)
|
|
44
|
+
// .forEach(p => pubkeys.add(p))
|
|
45
|
+
// // Parse any pubkeys from the script.
|
|
46
|
+
// parse_taproot_pubkeys(witness.script!)
|
|
47
|
+
// .forEach(p => pubkeys.add(p))
|
|
48
|
+
// // If the witness type is a p2w-pkh:
|
|
49
|
+
// } else if (type === 'p2w-pkh') {
|
|
50
|
+
// // Parse the witness parameter pubkey.
|
|
51
|
+
// const params_pk = witness.params.find(p => p.length === 66)
|
|
52
|
+
// // If the parameter pubkey is present, add it to the list.
|
|
53
|
+
// if (params_pk !== undefined) pubkeys.add(params_pk)
|
|
54
|
+
// // If the witness type is a p2w-sh:
|
|
55
|
+
// } else if (type === 'p2w-sh') {
|
|
56
|
+
// // Parse any witness parameter pubkeys.
|
|
57
|
+
// witness.params
|
|
58
|
+
// .filter(p => p.length === 66)
|
|
59
|
+
// .forEach(p => pubkeys.add(p))
|
|
60
|
+
// // Parse any script pubkeys.
|
|
61
|
+
// parse_segwit_pubkeys(witness.script!)
|
|
62
|
+
// .forEach(p => pubkeys.add(p))
|
|
63
|
+
// }
|
|
64
|
+
// // Return the list of pubkeys.
|
|
65
|
+
// return Array.from(pubkeys)
|
|
66
|
+
// }
|
|
67
|
+
|
|
68
|
+
// function parse_segwit_pubkeys (script : string) : string[] {
|
|
69
|
+
// const regex = /21([0-9a-f]{66})(ac|ad)/gi
|
|
70
|
+
// const matches = [...script.matchAll(regex)]
|
|
71
|
+
// return matches.map(match => match[1])
|
|
72
|
+
// }
|
|
73
|
+
|
|
74
|
+
// function parse_taproot_pubkeys (script : string) : string[] {
|
|
75
|
+
// const regex = /20([0-9a-f]{64})(ac|ad|ba)/gi
|
|
76
|
+
// const matches = [...script.matchAll(regex)]
|
|
77
|
+
// return matches.map(match => match[1])
|
|
78
|
+
// }
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
export const OPCODE_MAP = {
|
|
2
|
+
OP_0 : 0,
|
|
3
|
+
OP_PUSHDATA1 : 76,
|
|
4
|
+
OP_PUSHDATA2 : 77,
|
|
5
|
+
OP_PUSHDATA4 : 78,
|
|
6
|
+
OP_1NEGATE : 79,
|
|
7
|
+
OP_SUCCESS80 : 80,
|
|
8
|
+
OP_1 : 81,
|
|
9
|
+
OP_2 : 82,
|
|
10
|
+
OP_3 : 83,
|
|
11
|
+
OP_4 : 84,
|
|
12
|
+
OP_5 : 85,
|
|
13
|
+
OP_6 : 86,
|
|
14
|
+
OP_7 : 87,
|
|
15
|
+
OP_8 : 88,
|
|
16
|
+
OP_9 : 89,
|
|
17
|
+
OP_10 : 90,
|
|
18
|
+
OP_11 : 91,
|
|
19
|
+
OP_12 : 92,
|
|
20
|
+
OP_13 : 93,
|
|
21
|
+
OP_14 : 94,
|
|
22
|
+
OP_15 : 95,
|
|
23
|
+
OP_16 : 96,
|
|
24
|
+
OP_NOP : 97,
|
|
25
|
+
OP_SUCCESS98 : 98,
|
|
26
|
+
OP_IF : 99,
|
|
27
|
+
OP_NOTIF : 100,
|
|
28
|
+
OP_ELSE : 103,
|
|
29
|
+
OP_ENDIF : 104,
|
|
30
|
+
OP_VERIFY : 105,
|
|
31
|
+
OP_RETURN : 106,
|
|
32
|
+
OP_TOALTSTACK : 107,
|
|
33
|
+
OP_FROMALTSTACK : 108,
|
|
34
|
+
OP_2DROP : 109,
|
|
35
|
+
OP_2DUP : 110,
|
|
36
|
+
OP_3DUP : 111,
|
|
37
|
+
OP_2OVER : 112,
|
|
38
|
+
OP_2ROT : 113,
|
|
39
|
+
OP_2SWAP : 114,
|
|
40
|
+
OP_IFDUP : 115,
|
|
41
|
+
OP_DEPTH : 116,
|
|
42
|
+
OP_DROP : 117,
|
|
43
|
+
OP_DUP : 118,
|
|
44
|
+
OP_NIP : 119,
|
|
45
|
+
OP_OVER : 120,
|
|
46
|
+
OP_PICK : 121,
|
|
47
|
+
OP_ROLL : 122,
|
|
48
|
+
OP_ROT : 123,
|
|
49
|
+
OP_SWAP : 124,
|
|
50
|
+
OP_TUCK : 125,
|
|
51
|
+
OP_SUCCESS126 : 126,
|
|
52
|
+
OP_SUCCESS127 : 127,
|
|
53
|
+
OP_SUCCESS128 : 128,
|
|
54
|
+
OP_SUCCESS129 : 129,
|
|
55
|
+
OP_SIZE : 130,
|
|
56
|
+
OP_SUCCESS131 : 131,
|
|
57
|
+
OP_SUCCESS132 : 132,
|
|
58
|
+
OP_SUCCESS133 : 133,
|
|
59
|
+
OP_SUCCESS134 : 134,
|
|
60
|
+
OP_EQUAL : 135,
|
|
61
|
+
OP_EQUALVERIFY : 136,
|
|
62
|
+
OP_SUCCESS137 : 137,
|
|
63
|
+
OP_SUCCESS138 : 138,
|
|
64
|
+
OP_1ADD : 139,
|
|
65
|
+
OP_1SUB : 140,
|
|
66
|
+
OP_SUCCESS141 : 141,
|
|
67
|
+
OP_SUCCESS142 : 142,
|
|
68
|
+
OP_NEGATE : 143,
|
|
69
|
+
OP_ABS : 144,
|
|
70
|
+
OP_NOT : 145,
|
|
71
|
+
OP_0NOTEQUAL : 146,
|
|
72
|
+
OP_ADD : 147,
|
|
73
|
+
OP_SUB : 148,
|
|
74
|
+
OP_SUCCESS149 : 149,
|
|
75
|
+
OP_SUCCESS150 : 150,
|
|
76
|
+
OP_SUCCESS151 : 151,
|
|
77
|
+
OP_SUCCESS152 : 152,
|
|
78
|
+
OP_SUCCESS153 : 153,
|
|
79
|
+
OP_BOOLAND : 154,
|
|
80
|
+
OP_BOOLOR : 155,
|
|
81
|
+
OP_NUMEQUAL : 156,
|
|
82
|
+
OP_NUMEQUALVERIFY : 157,
|
|
83
|
+
OP_NUMNOTEQUAL : 158,
|
|
84
|
+
OP_LESSTHAN : 159,
|
|
85
|
+
OP_GREATERTHAN : 160,
|
|
86
|
+
OP_LESSTHANOREQUAL : 161,
|
|
87
|
+
OP_GREATERTHANOREQUAL : 162,
|
|
88
|
+
OP_MIN : 163,
|
|
89
|
+
OP_MAX : 164,
|
|
90
|
+
OP_WITHIN : 165,
|
|
91
|
+
OP_RIPEMD160 : 166,
|
|
92
|
+
OP_SHA1 : 167,
|
|
93
|
+
OP_SHA256 : 168,
|
|
94
|
+
OP_HASH160 : 169,
|
|
95
|
+
OP_HASH256 : 170,
|
|
96
|
+
OP_CODESEPARATOR : 171,
|
|
97
|
+
OP_CHECKSIG : 172,
|
|
98
|
+
OP_CHECKSIGVERIFY : 173,
|
|
99
|
+
OP_CHECKMULTISIG : 174,
|
|
100
|
+
OP_CHECKMULTISIGVERIFY : 175,
|
|
101
|
+
OP_NOP1 : 176,
|
|
102
|
+
OP_CHECKLOCKTIMEVERIFY : 177,
|
|
103
|
+
OP_CHECKSEQUENCEVERIFY : 178,
|
|
104
|
+
OP_NOP4 : 179,
|
|
105
|
+
OP_NOP5 : 180,
|
|
106
|
+
OP_NOP6 : 181,
|
|
107
|
+
OP_NOP7 : 182,
|
|
108
|
+
OP_NOP8 : 183,
|
|
109
|
+
OP_NOP9 : 184,
|
|
110
|
+
OP_NOP10 : 185,
|
|
111
|
+
OP_CHECKSIGADD : 186
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the sting-representation of an opcode
|
|
116
|
+
* based on its number value.
|
|
117
|
+
*/
|
|
118
|
+
export function get_op_code (num : number) : string {
|
|
119
|
+
if (num > 186 && num < 255) {
|
|
120
|
+
return 'OP_SUCCESS' + String(num)
|
|
121
|
+
}
|
|
122
|
+
for (const [ k, v ] of Object.entries(OPCODE_MAP)) {
|
|
123
|
+
if (v === num) return k
|
|
124
|
+
}
|
|
125
|
+
throw new Error('OPCODE not found:' + String(num))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the number-representation of an opcode
|
|
130
|
+
* based on its asm string value.
|
|
131
|
+
*/
|
|
132
|
+
export function get_asm_code (string : string) : number {
|
|
133
|
+
for (const [ k, v ] of Object.entries(OPCODE_MAP)) {
|
|
134
|
+
if (k === string) return Number(v)
|
|
135
|
+
}
|
|
136
|
+
throw new Error('OPCODE not found:' + string)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the type of word based on its number value.
|
|
141
|
+
*/
|
|
142
|
+
export function get_op_type (word : number) : string {
|
|
143
|
+
switch (true) {
|
|
144
|
+
case (word === 0):
|
|
145
|
+
return 'opcode'
|
|
146
|
+
case (word >= 1 && word <= 75):
|
|
147
|
+
return 'varint'
|
|
148
|
+
case (word === 76):
|
|
149
|
+
return 'pushdata1'
|
|
150
|
+
case (word === 77):
|
|
151
|
+
return 'pushdata2'
|
|
152
|
+
case (word === 78):
|
|
153
|
+
return 'pushdata4'
|
|
154
|
+
case (word <= 254):
|
|
155
|
+
return 'opcode'
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Invalid word range: ${word}`)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if the provided value is a valid script opcode.
|
|
163
|
+
*/
|
|
164
|
+
export function is_valid_op (word : number) : boolean {
|
|
165
|
+
const MIN_RANGE = 75
|
|
166
|
+
const MAX_RANGE = 254
|
|
167
|
+
|
|
168
|
+
const DISABLED_OPCODES : number[] = []
|
|
169
|
+
|
|
170
|
+
switch (true) {
|
|
171
|
+
case (typeof (word) !== 'number'):
|
|
172
|
+
return false
|
|
173
|
+
case (word === 0):
|
|
174
|
+
return true
|
|
175
|
+
case (DISABLED_OPCODES.includes(word)):
|
|
176
|
+
return false
|
|
177
|
+
case (MIN_RANGE < word && word < MAX_RANGE):
|
|
178
|
+
return true
|
|
179
|
+
default:
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
}
|