@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,152 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { hash160, hash256 } from '@vbyte/micro-lib/hash'
|
|
3
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
4
|
+
import { parse_txinput } from './util.js'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
prefix_script_size,
|
|
8
|
+
decode_script
|
|
9
|
+
} from '@/lib/script/index.js'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
encode_txin_vout,
|
|
13
|
+
encode_tx_locktime,
|
|
14
|
+
encode_txin_sequence,
|
|
15
|
+
encode_txin_txid,
|
|
16
|
+
encode_vout_value,
|
|
17
|
+
encode_tx_version,
|
|
18
|
+
parse_tx_data
|
|
19
|
+
} from '@/lib/tx/index.js'
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
SigHashOptions,
|
|
23
|
+
TxData,
|
|
24
|
+
TxInput,
|
|
25
|
+
TxOutput
|
|
26
|
+
} from '@/types/index.js'
|
|
27
|
+
|
|
28
|
+
import * as CONST from '@/const.js'
|
|
29
|
+
|
|
30
|
+
export function hash_segwit_tx (
|
|
31
|
+
txdata : TxData,
|
|
32
|
+
options : SigHashOptions = {}
|
|
33
|
+
) : Buff {
|
|
34
|
+
// Unpack the sigflag from our config object.
|
|
35
|
+
const { sigflag = 0x01, txindex } = options
|
|
36
|
+
// Normalize the tx into JSON format.
|
|
37
|
+
const tx = parse_tx_data(txdata)
|
|
38
|
+
// Check if the ANYONECANPAY flag is set.
|
|
39
|
+
const is_anypay = (sigflag & 0x80) === 0x80
|
|
40
|
+
// Save a normalized version of the sigflag.
|
|
41
|
+
const flag = sigflag % 0x80
|
|
42
|
+
// Check if the sigflag exists as a valid type.
|
|
43
|
+
if (!CONST.SIGHASH_SEGWIT.includes(flag)) {
|
|
44
|
+
throw new Error('Invalid hash type: ' + String(sigflag))
|
|
45
|
+
}
|
|
46
|
+
// Unpack the tx object.
|
|
47
|
+
const { version, vin, vout, locktime } = tx
|
|
48
|
+
// Parse the input we are signing from the config.
|
|
49
|
+
const txinput = parse_txinput(tx, options)
|
|
50
|
+
// Unpack the chosen input for signing.
|
|
51
|
+
const { txid, vout: prevIdx, prevout, sequence } = txinput
|
|
52
|
+
// Unpack the prevout for the chosen input.
|
|
53
|
+
const { value } = prevout ?? {}
|
|
54
|
+
// Check if a prevout value is provided.
|
|
55
|
+
if (value === undefined) {
|
|
56
|
+
throw new Error('Prevout value is empty!')
|
|
57
|
+
}
|
|
58
|
+
// Initialize our script variable from the config.
|
|
59
|
+
let { pubkey, script } = options
|
|
60
|
+
// Check if a pubkey is provided (instead of a script).
|
|
61
|
+
if (script === undefined && pubkey !== undefined) {
|
|
62
|
+
const pkhash = hash160(pubkey).hex
|
|
63
|
+
script = `76a914${String(pkhash)}88ac`
|
|
64
|
+
}
|
|
65
|
+
// Make sure that some form of script has been provided.
|
|
66
|
+
if (script === undefined) {
|
|
67
|
+
throw new Error('No pubkey / script has been set!')
|
|
68
|
+
}
|
|
69
|
+
// Throw if OP_CODESEPARATOR is used in a script.
|
|
70
|
+
if (decode_script(script).includes('OP_CODESEPARATOR')) {
|
|
71
|
+
throw new Error('This library does not currently support the use of OP_CODESEPARATOR in segwit scripts.')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const sighash = [
|
|
75
|
+
encode_tx_version(version),
|
|
76
|
+
hash_prevouts(vin, is_anypay),
|
|
77
|
+
hash_sequence(vin, flag, is_anypay),
|
|
78
|
+
encode_txin_txid(txid),
|
|
79
|
+
encode_txin_vout(prevIdx),
|
|
80
|
+
prefix_script_size(script),
|
|
81
|
+
encode_vout_value(value),
|
|
82
|
+
encode_txin_sequence(sequence),
|
|
83
|
+
hash_outputs(vout, flag, txindex),
|
|
84
|
+
encode_tx_locktime(locktime),
|
|
85
|
+
Buff.num(sigflag, 4).reverse()
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
return hash256(Buff.join(sighash))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function hash_prevouts (
|
|
92
|
+
vin : TxInput[],
|
|
93
|
+
isAnypay ?: boolean
|
|
94
|
+
) : Uint8Array {
|
|
95
|
+
if (isAnypay === true) {
|
|
96
|
+
return Buff.num(0, 32)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const stack = []
|
|
100
|
+
|
|
101
|
+
for (const { txid, vout } of vin) {
|
|
102
|
+
stack.push(encode_txin_txid(txid))
|
|
103
|
+
stack.push(encode_txin_vout(vout))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return hash256(Buff.join(stack))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function hash_sequence (
|
|
110
|
+
vin : TxInput[],
|
|
111
|
+
sigflag : number,
|
|
112
|
+
isAnyPay : boolean
|
|
113
|
+
) : Uint8Array {
|
|
114
|
+
if (isAnyPay || sigflag !== 0x01) {
|
|
115
|
+
return Buff.num(0, 32)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const stack = []
|
|
119
|
+
|
|
120
|
+
for (const { sequence } of vin) {
|
|
121
|
+
stack.push(encode_txin_sequence(sequence))
|
|
122
|
+
}
|
|
123
|
+
return hash256(Buff.join(stack))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hash_outputs (
|
|
127
|
+
vout : TxOutput[],
|
|
128
|
+
sigflag : number,
|
|
129
|
+
idx ?: number
|
|
130
|
+
) : Uint8Array {
|
|
131
|
+
const stack = []
|
|
132
|
+
|
|
133
|
+
if (sigflag === 0x01) {
|
|
134
|
+
for (const { value, script_pk } of vout) {
|
|
135
|
+
stack.push(encode_vout_value(value))
|
|
136
|
+
stack.push(prefix_script_size(script_pk))
|
|
137
|
+
}
|
|
138
|
+
return hash256(Buff.join(stack))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (sigflag === 0x03) {
|
|
142
|
+
Assert.ok(idx !== undefined)
|
|
143
|
+
if (idx < vout.length) {
|
|
144
|
+
const { value, script_pk } = vout[idx]
|
|
145
|
+
stack.push(encode_vout_value(value))
|
|
146
|
+
stack.push(prefix_script_size(script_pk))
|
|
147
|
+
return hash256(Buff.join(stack))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return Buff.num(0, 32)
|
|
152
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ECC } from '@vbyte/micro-lib'
|
|
2
|
+
import { parse_tx_data } from '@/lib/tx/parse.js'
|
|
3
|
+
import { SIGHASH_DEFAULT } from '@/const.js'
|
|
4
|
+
import { hash_segwit_tx } from './segwit.js'
|
|
5
|
+
import { hash_taproot_tx } from './taproot.js'
|
|
6
|
+
import { format_sigflag } from './util.js'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
SigHashOptions,
|
|
10
|
+
TxData
|
|
11
|
+
} from '@/types/index.js'
|
|
12
|
+
|
|
13
|
+
export function sign_segwit_tx (
|
|
14
|
+
seckey : string,
|
|
15
|
+
txdata : TxData,
|
|
16
|
+
options : SigHashOptions,
|
|
17
|
+
) {
|
|
18
|
+
const tx = parse_tx_data(txdata)
|
|
19
|
+
const msg = hash_segwit_tx(tx, options)
|
|
20
|
+
const sig = ECC.sign_ecdsa(seckey, msg).hex
|
|
21
|
+
const flag = format_sigflag(options.sigflag ?? SIGHASH_DEFAULT)
|
|
22
|
+
return sig + flag
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function sign_taproot_tx (
|
|
26
|
+
seckey : string,
|
|
27
|
+
txdata : TxData,
|
|
28
|
+
options : SigHashOptions,
|
|
29
|
+
) {
|
|
30
|
+
const tx = parse_tx_data(txdata)
|
|
31
|
+
const msg = hash_taproot_tx(tx, options)
|
|
32
|
+
const sig = ECC.sign_bip340(seckey, msg).hex
|
|
33
|
+
const flag = format_sigflag(options.sigflag ?? 0)
|
|
34
|
+
return sig + flag
|
|
35
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
3
|
+
import { hash340, sha256 } from '@vbyte/micro-lib/hash'
|
|
4
|
+
import { prefix_script_size } from '@/lib/script/util.js'
|
|
5
|
+
import { encode_tapscript } from '@/lib/taproot/encode.js'
|
|
6
|
+
import { parse_tx_data } from '@/lib/tx/parse.js'
|
|
7
|
+
import * as CONST from '@/const.js'
|
|
8
|
+
import { parse_txinput } from './util.js'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
encode_txin_vout,
|
|
12
|
+
encode_tx_locktime,
|
|
13
|
+
encode_txin_sequence,
|
|
14
|
+
encode_txin_txid,
|
|
15
|
+
encode_vout_value,
|
|
16
|
+
encode_tx_version
|
|
17
|
+
} from '@/lib/tx/encode.js'
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
SigHashOptions,
|
|
21
|
+
TxData,
|
|
22
|
+
TxInput,
|
|
23
|
+
TxOutput
|
|
24
|
+
} from '@/types/index.js'
|
|
25
|
+
|
|
26
|
+
export function hash_taproot_tx (
|
|
27
|
+
template : TxData | string,
|
|
28
|
+
config : SigHashOptions = {}
|
|
29
|
+
) : Buff {
|
|
30
|
+
// Unpack configuration.
|
|
31
|
+
const {
|
|
32
|
+
script,
|
|
33
|
+
txindex,
|
|
34
|
+
sigflag = 0x00,
|
|
35
|
+
extflag = 0x00,
|
|
36
|
+
key_version = 0x00,
|
|
37
|
+
separator_pos = 0xFFFFFFFF
|
|
38
|
+
} = config
|
|
39
|
+
// Normalize the txdata object.
|
|
40
|
+
const tx = parse_tx_data(template)
|
|
41
|
+
// Unpack the txdata object.
|
|
42
|
+
const { version, vin: input, vout: output, locktime } = tx
|
|
43
|
+
// Parse the input we are signing from the config.
|
|
44
|
+
const txinput = parse_txinput(tx, config)
|
|
45
|
+
// Unpack the txinput object.
|
|
46
|
+
const { txid, vout, sequence, witness = [] } = txinput
|
|
47
|
+
// Check if we are using a valid hash type.
|
|
48
|
+
if (!CONST.SIGHASH_TAPROOT.includes(sigflag)) {
|
|
49
|
+
// If the sigflag is an invalid type, throw error.
|
|
50
|
+
throw new Error('Invalid hash type: ' + String(sigflag))
|
|
51
|
+
}
|
|
52
|
+
if (extflag < 0 || extflag > 127) {
|
|
53
|
+
// If the extflag is out of range, throw error.
|
|
54
|
+
throw new Error('Extention flag out of range: ' + String(extflag))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let { extension } = config
|
|
58
|
+
|
|
59
|
+
if (script !== undefined) {
|
|
60
|
+
extension = encode_tapscript(script).hex
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Define the parameters of the transaction.
|
|
64
|
+
const is_anypay = (sigflag & 0x80) === 0x80
|
|
65
|
+
const annex = get_annex_data(witness)
|
|
66
|
+
const annexBit = (annex !== undefined) ? 1 : 0
|
|
67
|
+
const extendBit = (extension !== undefined) ? 1 : 0
|
|
68
|
+
const spendType = ((extflag + extendBit) * 2) + annexBit
|
|
69
|
+
const hashtag = hash340('TapSighash')
|
|
70
|
+
|
|
71
|
+
// Begin building our preimage.
|
|
72
|
+
const preimage : (string | Uint8Array)[] = [
|
|
73
|
+
hashtag, // Buffer input with TapSighash.
|
|
74
|
+
Buff.num(0x00, 1), // Add zero-byte.
|
|
75
|
+
Buff.num(sigflag, 1), // Commit to signature flag.
|
|
76
|
+
encode_tx_version(version), // Commit to tx version.
|
|
77
|
+
encode_tx_locktime(locktime) // Commit to tx locktime.
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
if (!is_anypay) {
|
|
81
|
+
// If flag ANYONE_CAN_PAY is not set,
|
|
82
|
+
// then commit to all inputs.
|
|
83
|
+
const prevouts = input.map(e => get_prevout(e))
|
|
84
|
+
preimage.push(
|
|
85
|
+
hash_outpoints(input), // Commit to txid/vout for each input.
|
|
86
|
+
hash_amounts(prevouts), // Commit to prevout amount for each input.
|
|
87
|
+
hash_scripts(prevouts), // Commit to prevout script for each input.
|
|
88
|
+
hash_sequence(input) // Commit to sequence value for each input.
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if ((sigflag & 0x03) < 2 || (sigflag & 0x03) > 3) {
|
|
93
|
+
// If neither SINGLE or NONE flags are set,
|
|
94
|
+
// include a commitment to all outputs.
|
|
95
|
+
preimage.push(hash_outputs(output))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// At this step, we include the spend type.
|
|
99
|
+
preimage.push(Buff.num(spendType, 1))
|
|
100
|
+
|
|
101
|
+
if (is_anypay) {
|
|
102
|
+
// If ANYONE_CAN_PAY flag is set, then we will
|
|
103
|
+
// provide a commitment to the input being signed.
|
|
104
|
+
const { value, script_pk } = get_prevout(txinput)
|
|
105
|
+
preimage.push(
|
|
106
|
+
encode_txin_txid(txid), // Commit to the input txid.
|
|
107
|
+
encode_txin_vout(vout), // Commit to the input vout index.
|
|
108
|
+
encode_vout_value(value), // Commit to the input's prevout value.
|
|
109
|
+
prefix_script_size(script_pk), // Commit to the input's prevout script.
|
|
110
|
+
encode_txin_sequence(sequence) // Commit to the input's sequence value.
|
|
111
|
+
)
|
|
112
|
+
} else {
|
|
113
|
+
// Otherwise, we must have already included a commitment
|
|
114
|
+
// to all inputs in the tx, so simply add a commitment to
|
|
115
|
+
// the index of the input we are signing for.
|
|
116
|
+
Assert.ok(typeof txindex === 'number')
|
|
117
|
+
preimage.push(Buff.num(txindex, 4).reverse())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (annex !== undefined) {
|
|
121
|
+
// If an annex has been set, include it here.
|
|
122
|
+
preimage.push(annex)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ((sigflag & 0x03) === 0x03) {
|
|
126
|
+
// If the SINGLE flag is set, then include a
|
|
127
|
+
// commitment to the output which is adjacent
|
|
128
|
+
// to the input that we are signing for.
|
|
129
|
+
Assert.ok(typeof txindex === 'number')
|
|
130
|
+
preimage.push(hash_output(output[txindex]))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (extension !== undefined) {
|
|
134
|
+
// If we are extending this signature to include
|
|
135
|
+
// other commitments (such as a tapleaf), then we
|
|
136
|
+
// will add it to the preimage here.
|
|
137
|
+
preimage.push(
|
|
138
|
+
Buff.bytes(extension), // Extention data (in bytes).
|
|
139
|
+
Buff.num(key_version), // Key version (reserved for future upgrades).
|
|
140
|
+
Buff.num(separator_pos, 4) // If OP_CODESEPARATOR is used, this must be set.
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Useful for debugging the preimage stack.
|
|
145
|
+
// console.log(preimage.map(e => Buff.raw(e).hex))
|
|
146
|
+
|
|
147
|
+
return sha256(Buff.join(preimage))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function hash_outpoints (
|
|
151
|
+
vin : TxInput[]
|
|
152
|
+
) : Buff {
|
|
153
|
+
const stack = []
|
|
154
|
+
for (const { txid, vout } of vin) {
|
|
155
|
+
stack.push(encode_txin_txid(txid))
|
|
156
|
+
stack.push(encode_txin_vout(vout))
|
|
157
|
+
}
|
|
158
|
+
return sha256(Buff.join(stack))
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function hash_sequence (
|
|
162
|
+
vin : TxInput[]
|
|
163
|
+
) : Buff {
|
|
164
|
+
const stack = []
|
|
165
|
+
for (const { sequence } of vin) {
|
|
166
|
+
stack.push(encode_txin_sequence(sequence))
|
|
167
|
+
}
|
|
168
|
+
return sha256(Buff.join(stack))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function hash_amounts (
|
|
172
|
+
prevouts : TxOutput[]
|
|
173
|
+
) : Buff {
|
|
174
|
+
const stack = []
|
|
175
|
+
for (const { value } of prevouts) {
|
|
176
|
+
stack.push(encode_vout_value(value))
|
|
177
|
+
}
|
|
178
|
+
return sha256(Buff.join(stack))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function hash_scripts (
|
|
182
|
+
prevouts : TxOutput[]
|
|
183
|
+
) : Buff {
|
|
184
|
+
const stack = []
|
|
185
|
+
for (const { script_pk } of prevouts) {
|
|
186
|
+
stack.push(prefix_script_size(script_pk))
|
|
187
|
+
}
|
|
188
|
+
return sha256(Buff.join(stack))
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function hash_outputs (
|
|
192
|
+
vout : TxOutput[]
|
|
193
|
+
) : Buff {
|
|
194
|
+
const stack = []
|
|
195
|
+
for (const { value, script_pk } of vout) {
|
|
196
|
+
stack.push(encode_vout_value(value))
|
|
197
|
+
stack.push(prefix_script_size(script_pk))
|
|
198
|
+
}
|
|
199
|
+
return sha256(Buff.join(stack))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function hash_output (
|
|
203
|
+
vout : TxOutput
|
|
204
|
+
) : Buff {
|
|
205
|
+
return sha256(
|
|
206
|
+
encode_vout_value(vout.value),
|
|
207
|
+
prefix_script_size(vout.script_pk)
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function get_annex_data (
|
|
212
|
+
witness ?: string[]
|
|
213
|
+
) : Buff | undefined {
|
|
214
|
+
// If no witness exists, return undefined.
|
|
215
|
+
if (witness === undefined) return
|
|
216
|
+
// If there are less than two elements, return undefined.
|
|
217
|
+
if (witness.length < 2) return
|
|
218
|
+
// Define the last element as the annex.
|
|
219
|
+
const annex = witness.at(-1)
|
|
220
|
+
// If the annex is a string and starts with '50',
|
|
221
|
+
if (typeof annex === 'string' && annex.startsWith('50')) {
|
|
222
|
+
// Convert the annex to a buffer with a varint prefix.
|
|
223
|
+
const bytes = Buff.hex(annex).prefix_varint('be')
|
|
224
|
+
// Return the sha256 of the annex.
|
|
225
|
+
return sha256(bytes)
|
|
226
|
+
}
|
|
227
|
+
// Else, return undefined.
|
|
228
|
+
return undefined
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function get_prevout (vin : TxInput) : TxOutput {
|
|
232
|
+
if (vin.prevout === null) {
|
|
233
|
+
throw new Error('Prevout data missing for input: ' + String(vin.txid))
|
|
234
|
+
}
|
|
235
|
+
return vin.prevout
|
|
236
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
SigHashOptions,
|
|
6
|
+
TxInput,
|
|
7
|
+
TxData,
|
|
8
|
+
} from '@/types/index.js'
|
|
9
|
+
|
|
10
|
+
export function parse_txinput (
|
|
11
|
+
txdata : TxData,
|
|
12
|
+
config ?: SigHashOptions
|
|
13
|
+
) : TxInput {
|
|
14
|
+
let { txindex, txinput } = config ?? {}
|
|
15
|
+
if (txindex !== undefined) {
|
|
16
|
+
if (txindex >= txdata.vin.length) {
|
|
17
|
+
// If index is out of bounds, throw error.
|
|
18
|
+
throw new Error('Input index out of bounds: ' + String(txindex))
|
|
19
|
+
}
|
|
20
|
+
txinput = txdata.vin.at(txindex)
|
|
21
|
+
}
|
|
22
|
+
Assert.ok(txinput !== undefined)
|
|
23
|
+
return txinput
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function format_sigflag (flag : number) {
|
|
27
|
+
return (flag !== 0) ? Buff.num(flag, 1).hex : ''
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// import { Buff, Bytes, Stream } from '@vbyte/buff'
|
|
2
|
+
// import { safeThrow } from '@/lib/utils.js'
|
|
3
|
+
// import { checkPath } from '@/lib/tap/key.js'
|
|
4
|
+
// import { getTapLeaf } from '@/lib/tap/tree.js'
|
|
5
|
+
// import { Tx } from '@/lib/tx/index.js'
|
|
6
|
+
// import { Script } from '@/lib/script/index.js'
|
|
7
|
+
// import { HashConfig, TxData } from '@/types/index.js'
|
|
8
|
+
// import { TxTemplate } from '@/schema/types.js'
|
|
9
|
+
// import { hashTx } from './segwit.js'
|
|
10
|
+
|
|
11
|
+
// export function verify_signature (
|
|
12
|
+
// txdata : TxData | Bytes,
|
|
13
|
+
// index : number,
|
|
14
|
+
// config : HashConfig = {}
|
|
15
|
+
// ) : boolean {
|
|
16
|
+
// const tx = Tx.fmt.toJson(txdata)
|
|
17
|
+
// const { throws = false } = config
|
|
18
|
+
// const { prevout, witness = [] } = tx.vin[index]
|
|
19
|
+
// const witnessData = Tx.util.readWitness(witness)
|
|
20
|
+
// const { cblock, script, params } = witnessData
|
|
21
|
+
|
|
22
|
+
// let pub : Buff
|
|
23
|
+
|
|
24
|
+
// if (params.length < 1) {
|
|
25
|
+
// return safeThrow('Invalid witness data: ' + String(witness), throws)
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
// const { scriptPubKey } = prevout ?? {}
|
|
29
|
+
|
|
30
|
+
// if (scriptPubKey === undefined) {
|
|
31
|
+
// return safeThrow('Prevout scriptPubKey is empty!', throws)
|
|
32
|
+
// }
|
|
33
|
+
|
|
34
|
+
// const { type, data: tapkey } = Tx.util.readScriptPubKey(scriptPubKey)
|
|
35
|
+
|
|
36
|
+
// if (type !== 'p2tr') {
|
|
37
|
+
// return safeThrow('Prevout script is not a valid taproot output:' + tapkey.hex, throws)
|
|
38
|
+
// }
|
|
39
|
+
|
|
40
|
+
// if (tapkey.length !== 32) {
|
|
41
|
+
// return safeThrow('Invalid tapkey length: ' + String(tapkey.length), throws)
|
|
42
|
+
// }
|
|
43
|
+
|
|
44
|
+
// if (
|
|
45
|
+
// cblock !== null &&
|
|
46
|
+
// script !== null
|
|
47
|
+
// ) {
|
|
48
|
+
// const version = cblock[0] & 0xfe
|
|
49
|
+
// const target = getTapLeaf(script, version)
|
|
50
|
+
// config.extension = target
|
|
51
|
+
|
|
52
|
+
// if (!checkPath(tapkey, target, cblock, { throws })) {
|
|
53
|
+
// return safeThrow('cblock verification failed!', throws)
|
|
54
|
+
// }
|
|
55
|
+
// }
|
|
56
|
+
|
|
57
|
+
// if (config.pubkey !== undefined) {
|
|
58
|
+
// pub = Buff.bytes(config.pubkey)
|
|
59
|
+
// } else if (params.length > 1 && params[1].length === 32) {
|
|
60
|
+
// pub = Buff.bytes(params[1])
|
|
61
|
+
// } else {
|
|
62
|
+
// pub = Buff.bytes(tapkey)
|
|
63
|
+
// }
|
|
64
|
+
|
|
65
|
+
// const rawsig = Script.fmt.toParam(params[0])
|
|
66
|
+
// const stream = new Stream(rawsig)
|
|
67
|
+
// const signature = stream.read(64).raw
|
|
68
|
+
|
|
69
|
+
// if (stream.size === 1) {
|
|
70
|
+
// config.sigflag = stream.read(1).num
|
|
71
|
+
// if (config.sigflag === 0x00) {
|
|
72
|
+
// return safeThrow('0x00 is not a valid appended sigflag!', throws)
|
|
73
|
+
// }
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
// const hash = hashTx(tx, index, config)
|
|
77
|
+
|
|
78
|
+
// if (!verify(signature, hash, pub, throws)) {
|
|
79
|
+
// return safeThrow('Invalid signature!', throws)
|
|
80
|
+
// }
|
|
81
|
+
|
|
82
|
+
// return true
|
|
83
|
+
// }
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Buff, Bytes } from '@vbyte/buff'
|
|
2
|
+
import { Assert, ECC } from '@vbyte/micro-lib'
|
|
3
|
+
import { merkleize } from './tree.js'
|
|
4
|
+
import { TAPLEAF_DEFAULT_VERSION } from '@/const.js'
|
|
5
|
+
import * as Schema from '@/schema/index.js'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
encode_tapbranch,
|
|
9
|
+
encode_taptweak
|
|
10
|
+
} from './encode.js'
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
parse_pubkey_parity,
|
|
14
|
+
parse_cblock
|
|
15
|
+
} from './parse.js'
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
TaprootConfig,
|
|
19
|
+
TaprootContext
|
|
20
|
+
} from '@/types/index.js'
|
|
21
|
+
|
|
22
|
+
const DEFAULT_VERSION = TAPLEAF_DEFAULT_VERSION
|
|
23
|
+
|
|
24
|
+
export function create_taproot (config : TaprootConfig) : TaprootContext {
|
|
25
|
+
Schema.taproot.config.parse(config)
|
|
26
|
+
|
|
27
|
+
const { pubkey, version = DEFAULT_VERSION } = config
|
|
28
|
+
|
|
29
|
+
const leaves = config.leaves ?? []
|
|
30
|
+
|
|
31
|
+
const target = (config.target !== undefined)
|
|
32
|
+
? Buff.bytes(config.target).hex
|
|
33
|
+
: undefined
|
|
34
|
+
|
|
35
|
+
let path : string[] = [],
|
|
36
|
+
taproot : string | undefined
|
|
37
|
+
|
|
38
|
+
if (leaves.length > 0) {
|
|
39
|
+
// Merkelize the leaves into a root hash (with proof).
|
|
40
|
+
const [ root, _, proofs ] = merkleize(leaves, target)
|
|
41
|
+
// Get the control path from the merkelized output.
|
|
42
|
+
path = proofs
|
|
43
|
+
// Get the tapped key from the internal key.
|
|
44
|
+
taproot = root
|
|
45
|
+
} else {
|
|
46
|
+
// Get the tapped key from the single tapleaf.
|
|
47
|
+
taproot = target
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const taptweak = encode_taptweak(pubkey, taproot)
|
|
51
|
+
const twk_key = ECC.tweak_pubkey(pubkey, taptweak, 'ecdsa')
|
|
52
|
+
const parity = parse_pubkey_parity(twk_key)
|
|
53
|
+
const tapkey = ECC.serialize_pubkey(twk_key, 'bip340')
|
|
54
|
+
// Get the block version / parity bit.
|
|
55
|
+
const cbit = Buff.num(version + parity)
|
|
56
|
+
// Stack the initial control block data.
|
|
57
|
+
const block : Bytes[] = [ cbit, Buff.bytes(pubkey) ]
|
|
58
|
+
// If there is more than one path, add to block.
|
|
59
|
+
if (path.length > 0) {
|
|
60
|
+
path.forEach(e => block.push(e))
|
|
61
|
+
}
|
|
62
|
+
// Merge the data together into one array.
|
|
63
|
+
const cblock = Buff.join(block)
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
int_key : Buff.bytes(pubkey).hex,
|
|
67
|
+
parity,
|
|
68
|
+
taproot : taproot ?? null,
|
|
69
|
+
cblock : cblock.hex,
|
|
70
|
+
tapkey : tapkey.hex,
|
|
71
|
+
taptweak : taptweak.hex
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function verify_taproot (
|
|
76
|
+
tapkey : string,
|
|
77
|
+
target : string,
|
|
78
|
+
cblock : string
|
|
79
|
+
) : boolean {
|
|
80
|
+
Assert.size(tapkey, 32)
|
|
81
|
+
const { parity, path, int_key } = parse_cblock(cblock)
|
|
82
|
+
|
|
83
|
+
const ext_key = Buff.join([ parity, tapkey ])
|
|
84
|
+
|
|
85
|
+
let branch = Buff.bytes(target).hex
|
|
86
|
+
|
|
87
|
+
for (const leaf of path) {
|
|
88
|
+
branch = encode_tapbranch(branch, leaf).hex
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const tap_tweak = encode_taptweak(int_key, branch)
|
|
92
|
+
const tweaked_key = ECC.tweak_pubkey(int_key, tap_tweak, 'ecdsa')
|
|
93
|
+
|
|
94
|
+
return (ext_key.hex === tweaked_key.hex)
|
|
95
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff'
|
|
2
|
+
import { hash340 } from '@vbyte/micro-lib/hash'
|
|
3
|
+
import { Assert } from '@vbyte/micro-lib'
|
|
4
|
+
import { prefix_script_size } from '@/lib/script/index.js'
|
|
5
|
+
|
|
6
|
+
import { TAPLEAF_DEFAULT_VERSION } from '@/const.js'
|
|
7
|
+
|
|
8
|
+
const DEFAULT_VERSION = TAPLEAF_DEFAULT_VERSION
|
|
9
|
+
|
|
10
|
+
export function encode_tapscript (
|
|
11
|
+
script : string | Uint8Array,
|
|
12
|
+
version = DEFAULT_VERSION
|
|
13
|
+
) : Buff {
|
|
14
|
+
const preimg = prefix_script_size(script)
|
|
15
|
+
return encode_tapleaf(preimg, version)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function encode_tapleaf (
|
|
19
|
+
data : string | Uint8Array,
|
|
20
|
+
version = DEFAULT_VERSION
|
|
21
|
+
) : Buff {
|
|
22
|
+
const vbyte = encode_leaf_version(version)
|
|
23
|
+
return hash340('TapLeaf', vbyte, data)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function encode_tapbranch (
|
|
27
|
+
leaf_a : string,
|
|
28
|
+
leaf_b : string
|
|
29
|
+
) : Buff {
|
|
30
|
+
// Compare leaves in lexical order.
|
|
31
|
+
if (leaf_b < leaf_a) {
|
|
32
|
+
// Swap leaves if needed.
|
|
33
|
+
[ leaf_a, leaf_b ] = [ leaf_b, leaf_a ]
|
|
34
|
+
}
|
|
35
|
+
// Return digest of leaves as a branch hash.
|
|
36
|
+
return hash340('TapBranch', leaf_a, leaf_b)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function encode_leaf_version (version = 0xc0) : number {
|
|
40
|
+
return version & 0xfe
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function encode_taptweak (
|
|
44
|
+
pubkey : string | Uint8Array,
|
|
45
|
+
data : string | Uint8Array = new Uint8Array()
|
|
46
|
+
) : Buff {
|
|
47
|
+
Assert.size(pubkey, 32)
|
|
48
|
+
return hash340('TapTweak', pubkey, data)
|
|
49
|
+
}
|