@btc-vision/bitcoin 6.3.1 → 6.3.2
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/.babelrc +4 -0
- package/.gitattributes +2 -0
- package/.nyc_output/6368a5b2-daa5-4821-8ed0-b742d6fc7eab.json +1 -0
- package/.nyc_output/processinfo/6368a5b2-daa5-4821-8ed0-b742d6fc7eab.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.prettierrc.json +12 -0
- package/CHANGELOG.md +403 -0
- package/CONTRIBUTING.md +83 -0
- package/browser/address.d.ts +16 -0
- package/{src → browser}/bip66.d.ts +6 -7
- package/{src → browser}/block.d.ts +29 -30
- package/{src → browser}/bufferutils.d.ts +34 -54
- package/browser/crypto/crypto.d.ts +1 -0
- package/{src → browser}/crypto.d.ts +13 -18
- package/browser/ecc_lib.d.ts +3 -0
- package/browser/hooks/AdvancedSignatureManager.d.ts +16 -0
- package/{src → browser}/hooks/HookedSigner.d.ts +4 -4
- package/browser/hooks/SignatureManager.d.ts +13 -0
- package/browser/index.d.ts +58 -0
- package/browser/index.js +2 -0
- package/browser/index.js.LICENSE.txt +14 -0
- package/browser/merkle.d.ts +1 -0
- package/browser/networks.d.ts +23 -0
- package/{src → browser}/ops.d.ts +126 -126
- package/browser/payments/bip341.d.ts +23 -0
- package/browser/payments/embed.d.ts +2 -0
- package/browser/payments/index.d.ts +41 -0
- package/{src → browser}/payments/lazy.d.ts +2 -2
- package/browser/payments/p2ms.d.ts +2 -0
- package/browser/payments/p2pk.d.ts +2 -0
- package/browser/payments/p2pkh.d.ts +2 -0
- package/browser/payments/p2sh.d.ts +2 -0
- package/browser/payments/p2tr.d.ts +2 -0
- package/browser/payments/p2wpkh.d.ts +2 -0
- package/browser/payments/p2wsh.d.ts +2 -0
- package/browser/psbt/bip371.d.ts +16 -0
- package/browser/psbt/psbtutils.d.ts +26 -0
- package/{src → browser}/psbt.d.ts +167 -238
- package/browser/push_data.d.ts +7 -0
- package/browser/script.d.ts +17 -0
- package/browser/script_number.d.ts +2 -0
- package/browser/script_signature.d.ts +7 -0
- package/{src → browser}/transaction.d.ts +48 -60
- package/{src → browser}/types.d.ts +37 -54
- package/build/address.d.ts +16 -0
- package/build/address.js +148 -0
- package/build/bip66.d.ts +6 -0
- package/build/bip66.js +99 -0
- package/build/block.d.ts +29 -0
- package/build/block.js +181 -0
- package/build/bufferutils.d.ts +34 -0
- package/build/bufferutils.js +141 -0
- package/build/crypto/crypto.d.ts +1 -0
- package/build/crypto/crypto.js +1 -0
- package/build/crypto.d.ts +13 -0
- package/build/crypto.js +87 -0
- package/build/ecc_lib.d.ts +3 -0
- package/build/ecc_lib.js +61 -0
- package/build/hooks/AdvancedSignatureManager.d.ts +16 -0
- package/build/hooks/AdvancedSignatureManager.js +52 -0
- package/build/hooks/HookedSigner.d.ts +4 -0
- package/build/hooks/HookedSigner.js +64 -0
- package/build/hooks/SignatureManager.d.ts +13 -0
- package/build/hooks/SignatureManager.js +45 -0
- package/build/index.d.ts +58 -0
- package/build/index.js +32 -0
- package/build/merkle.d.ts +1 -0
- package/build/merkle.js +19 -0
- package/build/networks.d.ts +23 -0
- package/build/networks.js +121 -0
- package/build/ops.d.ts +126 -0
- package/{src → build}/ops.js +127 -131
- package/build/payments/bip341.d.ts +23 -0
- package/build/payments/bip341.js +82 -0
- package/build/payments/embed.d.ts +2 -0
- package/build/payments/embed.js +39 -0
- package/build/payments/index.d.ts +41 -0
- package/build/payments/index.js +10 -0
- package/build/payments/lazy.d.ts +2 -0
- package/{src → build}/payments/lazy.js +28 -32
- package/build/payments/p2ms.d.ts +2 -0
- package/{src → build}/payments/p2ms.js +128 -158
- package/build/payments/p2pk.d.ts +2 -0
- package/build/payments/p2pk.js +68 -0
- package/build/payments/p2pkh.d.ts +2 -0
- package/build/payments/p2pkh.js +135 -0
- package/build/payments/p2sh.d.ts +2 -0
- package/build/payments/p2sh.js +175 -0
- package/build/payments/p2tr.d.ts +2 -0
- package/build/payments/p2tr.js +254 -0
- package/build/payments/p2wpkh.d.ts +2 -0
- package/build/payments/p2wpkh.js +130 -0
- package/build/payments/p2wsh.d.ts +2 -0
- package/build/payments/p2wsh.js +180 -0
- package/build/psbt/bip371.d.ts +16 -0
- package/build/psbt/bip371.js +246 -0
- package/build/psbt/psbtutils.d.ts +26 -0
- package/build/psbt/psbtutils.js +170 -0
- package/build/psbt.d.ts +167 -0
- package/build/psbt.js +1305 -0
- package/build/push_data.d.ts +7 -0
- package/build/push_data.js +57 -0
- package/build/script.d.ts +17 -0
- package/build/script.js +167 -0
- package/build/script_number.d.ts +2 -0
- package/build/script_number.js +49 -0
- package/build/script_signature.d.ts +7 -0
- package/build/script_signature.js +49 -0
- package/build/transaction.d.ts +48 -0
- package/build/transaction.js +445 -0
- package/build/types.d.ts +37 -0
- package/build/types.js +73 -0
- package/cjs/package.json +3 -0
- package/eslint.config.js +56 -0
- package/gulpfile.js +42 -0
- package/package.json +105 -50
- package/src/{address.js → address.ts} +93 -73
- package/src/{bip66.js → bip66.ts} +23 -19
- package/src/{block.js → block.ts} +114 -105
- package/src/{bufferutils.js → bufferutils.ts} +65 -67
- package/src/crypto/crypto-browser.js +75 -0
- package/src/crypto/crypto.ts +1 -0
- package/src/crypto.ts +108 -0
- package/src/{ecc_lib.js → ecc_lib.ts} +25 -53
- package/src/hooks/{AdvancedSignatureManager.js → AdvancedSignatureManager.ts} +34 -18
- package/src/hooks/HookedSigner.ts +108 -0
- package/src/hooks/{SignatureManager.js → SignatureManager.ts} +26 -14
- package/src/index.ts +86 -0
- package/src/{merkle.js → merkle.ts} +8 -7
- package/src/{networks.js → networks.ts} +44 -29
- package/src/ops.ts +282 -0
- package/src/payments/bip341.ts +140 -0
- package/src/payments/embed.ts +55 -0
- package/src/payments/{index.d.ts → index.ts} +20 -10
- package/src/payments/lazy.ts +28 -0
- package/src/payments/p2ms.ts +150 -0
- package/src/payments/{p2pk.js → p2pk.ts} +32 -29
- package/src/payments/{p2pkh.js → p2pkh.ts} +53 -47
- package/src/payments/{p2sh.js → p2sh.ts} +72 -71
- package/src/payments/{p2tr.js → p2tr.ts} +114 -125
- package/src/payments/{p2wpkh.js → p2wpkh.ts} +51 -56
- package/src/payments/{p2wsh.js → p2wsh.ts} +69 -81
- package/src/psbt/{bip371.js → bip371.ts} +191 -174
- package/src/psbt/psbtutils.ts +299 -0
- package/src/{psbt.js → psbt.ts} +1025 -679
- package/src/{push_data.js → push_data.ts} +35 -21
- package/src/{script.js → script.ts} +93 -77
- package/src/{script_number.js → script_number.ts} +15 -21
- package/src/{script_signature.js → script_signature.ts} +26 -14
- package/src/{transaction.js → transaction.ts} +247 -167
- package/src/types.ts +122 -0
- package/test/address.spec.js +124 -0
- package/test/address.spec.ts +177 -0
- package/test/bitcoin.core.spec.js +170 -0
- package/test/bitcoin.core.spec.ts +234 -0
- package/test/block.spec.js +141 -0
- package/test/block.spec.ts +194 -0
- package/test/bufferutils.spec.js +427 -0
- package/test/bufferutils.spec.ts +513 -0
- package/test/crypto.spec.js +41 -0
- package/test/crypto.spec.ts +55 -0
- package/test/fixtures/address.json +329 -0
- package/test/fixtures/block.json +148 -0
- package/test/fixtures/bufferutils.json +102 -0
- package/test/fixtures/core/README.md +26 -0
- package/test/fixtures/core/base58_encode_decode.json +50 -0
- package/test/fixtures/core/base58_keys_invalid.json +152 -0
- package/test/fixtures/core/base58_keys_valid.json +452 -0
- package/test/fixtures/core/blocks.json +27 -0
- package/test/fixtures/core/sig_canonical.json +7 -0
- package/test/fixtures/core/sig_noncanonical.json +33 -0
- package/test/fixtures/core/sighash.json +3505 -0
- package/test/fixtures/core/tx_valid.json +2023 -0
- package/test/fixtures/crypto.json +43 -0
- package/test/fixtures/ecdsa.json +217 -0
- package/test/fixtures/ecpair.json +141 -0
- package/test/fixtures/embed.json +108 -0
- package/test/fixtures/p2ms.json +434 -0
- package/test/fixtures/p2pk.json +179 -0
- package/test/fixtures/p2pkh.json +276 -0
- package/test/fixtures/p2sh.json +508 -0
- package/test/fixtures/p2tr.json +1198 -0
- package/test/fixtures/p2wpkh.json +290 -0
- package/test/fixtures/p2wsh.json +489 -0
- package/test/fixtures/psbt.json +924 -0
- package/test/fixtures/script.json +465 -0
- package/test/fixtures/script_number.json +225 -0
- package/test/fixtures/signature.json +140 -0
- package/test/fixtures/transaction.json +916 -0
- package/test/integration/_regtest.js +7 -0
- package/test/integration/_regtest.ts +6 -0
- package/test/integration/addresses.spec.js +116 -0
- package/test/integration/addresses.spec.ts +154 -0
- package/test/integration/bip32.spec.js +85 -0
- package/test/integration/bip32.spec.ts +151 -0
- package/test/integration/blocks.spec.js +26 -0
- package/test/integration/blocks.spec.ts +28 -0
- package/test/integration/cltv.spec.js +199 -0
- package/test/integration/cltv.spec.ts +283 -0
- package/test/integration/csv.spec.js +362 -0
- package/test/integration/csv.spec.ts +527 -0
- package/test/integration/payments.spec.js +98 -0
- package/test/integration/payments.spec.ts +135 -0
- package/test/integration/taproot.spec.js +532 -0
- package/test/integration/taproot.spec.ts +707 -0
- package/test/integration/transactions.spec.js +561 -0
- package/test/integration/transactions.spec.ts +769 -0
- package/test/payments.spec.js +97 -0
- package/test/payments.spec.ts +125 -0
- package/test/payments.utils.js +190 -0
- package/test/payments.utils.ts +208 -0
- package/test/psbt.spec.js +1044 -0
- package/test/psbt.spec.ts +1414 -0
- package/test/script.spec.js +151 -0
- package/test/script.spec.ts +210 -0
- package/test/script_number.spec.js +24 -0
- package/test/script_number.spec.ts +29 -0
- package/test/script_signature.spec.js +52 -0
- package/test/script_signature.spec.ts +66 -0
- package/test/transaction.spec.js +269 -0
- package/test/transaction.spec.ts +387 -0
- package/test/ts-node-register.js +5 -0
- package/test/tsconfig.json +45 -0
- package/test/types.spec.js +46 -0
- package/test/types.spec.ts +58 -0
- package/tsconfig.base.json +27 -0
- package/tsconfig.json +19 -0
- package/tsconfig.webpack.json +18 -0
- package/webpack.config.js +79 -0
- package/src/address.d.ts +0 -42
- package/src/crypto.js +0 -128
- package/src/ecc_lib.d.ts +0 -17
- package/src/hooks/AdvancedSignatureManager.d.ts +0 -44
- package/src/hooks/HookedSigner.js +0 -90
- package/src/hooks/SignatureManager.d.ts +0 -35
- package/src/index.d.ts +0 -42
- package/src/index.js +0 -87
- package/src/merkle.d.ts +0 -10
- package/src/networks.d.ts +0 -83
- package/src/payments/bip341.d.ts +0 -49
- package/src/payments/bip341.js +0 -124
- package/src/payments/embed.d.ts +0 -9
- package/src/payments/embed.js +0 -54
- package/src/payments/index.js +0 -69
- package/src/payments/p2ms.d.ts +0 -9
- package/src/payments/p2pk.d.ts +0 -10
- package/src/payments/p2pkh.d.ts +0 -10
- package/src/payments/p2sh.d.ts +0 -10
- package/src/payments/p2tr.d.ts +0 -10
- package/src/payments/p2wpkh.d.ts +0 -10
- package/src/payments/p2wsh.d.ts +0 -10
- package/src/psbt/bip371.d.ts +0 -42
- package/src/psbt/psbtutils.d.ts +0 -64
- package/src/psbt/psbtutils.js +0 -191
- package/src/push_data.d.ts +0 -29
- package/src/script.d.ts +0 -42
- package/src/script_number.d.ts +0 -19
- package/src/script_signature.d.ts +0 -21
- package/src/types.js +0 -106
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { bech32 } from 'bech32';
|
|
2
|
+
import * as bcrypto from '../crypto.js';
|
|
3
|
+
import { bitcoin as BITCOIN_NETWORK } from '../networks.js';
|
|
4
|
+
import * as bscript from '../script.js';
|
|
5
|
+
import { isPoint, stacksEqual, typeforce as typef } from '../types.js';
|
|
6
|
+
import * as lazy from './lazy.js';
|
|
7
|
+
const OPS = bscript.OPS;
|
|
8
|
+
const EMPTY_BUFFER = Buffer.alloc(0);
|
|
9
|
+
function chunkHasUncompressedPubkey(chunk) {
|
|
10
|
+
if (Buffer.isBuffer(chunk) && chunk.length === 65 && chunk[0] === 0x04 && isPoint(chunk)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function p2wsh(a, opts) {
|
|
18
|
+
if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness)
|
|
19
|
+
throw new TypeError('Not enough data');
|
|
20
|
+
opts = Object.assign({ validate: true }, opts || {});
|
|
21
|
+
typef({
|
|
22
|
+
network: typef.maybe(typef.Object),
|
|
23
|
+
address: typef.maybe(typef.String),
|
|
24
|
+
hash: typef.maybe(typef.BufferN(32)),
|
|
25
|
+
output: typef.maybe(typef.BufferN(34)),
|
|
26
|
+
redeem: typef.maybe({
|
|
27
|
+
input: typef.maybe(typef.Buffer),
|
|
28
|
+
network: typef.maybe(typef.Object),
|
|
29
|
+
output: typef.maybe(typef.Buffer),
|
|
30
|
+
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
|
31
|
+
}),
|
|
32
|
+
input: typef.maybe(typef.BufferN(0)),
|
|
33
|
+
witness: typef.maybe(typef.arrayOf(typef.Buffer)),
|
|
34
|
+
}, a);
|
|
35
|
+
const _address = lazy.value(() => {
|
|
36
|
+
const result = bech32.decode(a.address);
|
|
37
|
+
const version = result.words.shift();
|
|
38
|
+
const data = bech32.fromWords(result.words);
|
|
39
|
+
return {
|
|
40
|
+
version,
|
|
41
|
+
prefix: result.prefix,
|
|
42
|
+
data: Buffer.from(data),
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
const _rchunks = lazy.value(() => {
|
|
46
|
+
return bscript.decompile(a.redeem.input);
|
|
47
|
+
});
|
|
48
|
+
let network = a.network;
|
|
49
|
+
if (!network) {
|
|
50
|
+
network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK;
|
|
51
|
+
}
|
|
52
|
+
const o = { network };
|
|
53
|
+
lazy.prop(o, 'address', () => {
|
|
54
|
+
if (!o.hash)
|
|
55
|
+
return;
|
|
56
|
+
const words = bech32.toWords(o.hash);
|
|
57
|
+
words.unshift(0x00);
|
|
58
|
+
return bech32.encode(network.bech32, words);
|
|
59
|
+
});
|
|
60
|
+
lazy.prop(o, 'hash', () => {
|
|
61
|
+
if (a.output)
|
|
62
|
+
return a.output.slice(2);
|
|
63
|
+
if (a.address)
|
|
64
|
+
return _address().data;
|
|
65
|
+
if (o.redeem && o.redeem.output)
|
|
66
|
+
return bcrypto.sha256(o.redeem.output);
|
|
67
|
+
});
|
|
68
|
+
lazy.prop(o, 'output', () => {
|
|
69
|
+
if (!o.hash)
|
|
70
|
+
return;
|
|
71
|
+
return bscript.compile([OPS.OP_0, o.hash]);
|
|
72
|
+
});
|
|
73
|
+
lazy.prop(o, 'redeem', () => {
|
|
74
|
+
if (!a.witness)
|
|
75
|
+
return;
|
|
76
|
+
return {
|
|
77
|
+
output: a.witness[a.witness.length - 1],
|
|
78
|
+
input: EMPTY_BUFFER,
|
|
79
|
+
witness: a.witness.slice(0, -1),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
lazy.prop(o, 'input', () => {
|
|
83
|
+
if (!o.witness)
|
|
84
|
+
return;
|
|
85
|
+
return EMPTY_BUFFER;
|
|
86
|
+
});
|
|
87
|
+
lazy.prop(o, 'witness', () => {
|
|
88
|
+
if (a.redeem &&
|
|
89
|
+
a.redeem.input &&
|
|
90
|
+
a.redeem.input.length > 0 &&
|
|
91
|
+
a.redeem.output &&
|
|
92
|
+
a.redeem.output.length > 0) {
|
|
93
|
+
const stack = bscript.toStack(_rchunks());
|
|
94
|
+
o.redeem = Object.assign({ witness: stack }, a.redeem);
|
|
95
|
+
o.redeem.input = EMPTY_BUFFER;
|
|
96
|
+
return [].concat(stack, a.redeem.output);
|
|
97
|
+
}
|
|
98
|
+
if (!a.redeem)
|
|
99
|
+
return;
|
|
100
|
+
if (!a.redeem.output)
|
|
101
|
+
return;
|
|
102
|
+
if (!a.redeem.witness)
|
|
103
|
+
return;
|
|
104
|
+
return [].concat(a.redeem.witness, a.redeem.output);
|
|
105
|
+
});
|
|
106
|
+
lazy.prop(o, 'name', () => {
|
|
107
|
+
const nameParts = ['p2wsh'];
|
|
108
|
+
if (o.redeem !== undefined && o.redeem.name !== undefined)
|
|
109
|
+
nameParts.push(o.redeem.name);
|
|
110
|
+
return nameParts.join('-');
|
|
111
|
+
});
|
|
112
|
+
if (opts.validate) {
|
|
113
|
+
let hash = Buffer.from([]);
|
|
114
|
+
if (a.address) {
|
|
115
|
+
if (_address().prefix !== network.bech32)
|
|
116
|
+
throw new TypeError('Invalid prefix or Network mismatch');
|
|
117
|
+
if (_address().version !== 0x00)
|
|
118
|
+
throw new TypeError('Invalid address version');
|
|
119
|
+
if (_address().data.length !== 32)
|
|
120
|
+
throw new TypeError('Invalid address data');
|
|
121
|
+
hash = _address().data;
|
|
122
|
+
}
|
|
123
|
+
if (a.hash) {
|
|
124
|
+
if (hash.length > 0 && !hash.equals(a.hash))
|
|
125
|
+
throw new TypeError('Hash mismatch');
|
|
126
|
+
else
|
|
127
|
+
hash = a.hash;
|
|
128
|
+
}
|
|
129
|
+
if (a.output) {
|
|
130
|
+
if (a.output.length !== 34 || a.output[0] !== OPS.OP_0 || a.output[1] !== 0x20)
|
|
131
|
+
throw new TypeError('Output is invalid');
|
|
132
|
+
const hash2 = a.output.slice(2);
|
|
133
|
+
if (hash.length > 0 && !hash.equals(hash2))
|
|
134
|
+
throw new TypeError('Hash mismatch');
|
|
135
|
+
else
|
|
136
|
+
hash = hash2;
|
|
137
|
+
}
|
|
138
|
+
if (a.redeem) {
|
|
139
|
+
if (a.redeem.network && a.redeem.network !== network)
|
|
140
|
+
throw new TypeError('Network mismatch');
|
|
141
|
+
if (a.redeem.input &&
|
|
142
|
+
a.redeem.input.length > 0 &&
|
|
143
|
+
a.redeem.witness &&
|
|
144
|
+
a.redeem.witness.length > 0)
|
|
145
|
+
throw new TypeError('Ambiguous witness source');
|
|
146
|
+
if (a.redeem.output) {
|
|
147
|
+
const decompile = bscript.decompile(a.redeem.output);
|
|
148
|
+
if (!decompile || decompile.length < 1)
|
|
149
|
+
throw new TypeError('Redeem.output is invalid');
|
|
150
|
+
if (a.redeem.output.byteLength > 3600)
|
|
151
|
+
throw new TypeError('Redeem.output unspendable if larger than 3600 bytes');
|
|
152
|
+
if (bscript.countNonPushOnlyOPs(decompile) > 201)
|
|
153
|
+
throw new TypeError('Redeem.output unspendable with more than 201 non-push ops');
|
|
154
|
+
const hash2 = bcrypto.sha256(a.redeem.output);
|
|
155
|
+
if (hash.length > 0 && !hash.equals(hash2))
|
|
156
|
+
throw new TypeError('Hash mismatch');
|
|
157
|
+
else
|
|
158
|
+
hash = hash2;
|
|
159
|
+
}
|
|
160
|
+
if (a.redeem.input && !bscript.isPushOnly(_rchunks()))
|
|
161
|
+
throw new TypeError('Non push-only scriptSig');
|
|
162
|
+
if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness))
|
|
163
|
+
throw new TypeError('Witness and redeem.witness mismatch');
|
|
164
|
+
if ((a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) ||
|
|
165
|
+
(a.redeem.output &&
|
|
166
|
+
(bscript.decompile(a.redeem.output) || []).some(chunkHasUncompressedPubkey))) {
|
|
167
|
+
throw new TypeError('redeem.input or redeem.output contains uncompressed pubkey');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (a.witness && a.witness.length > 0) {
|
|
171
|
+
const wScript = a.witness[a.witness.length - 1];
|
|
172
|
+
if (a.redeem && a.redeem.output && !a.redeem.output.equals(wScript))
|
|
173
|
+
throw new TypeError('Witness and redeem.output mismatch');
|
|
174
|
+
if (a.witness.some(chunkHasUncompressedPubkey) ||
|
|
175
|
+
(bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey))
|
|
176
|
+
throw new TypeError('Witness contains uncompressed pubkey');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return Object.assign(o, a);
|
|
180
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PsbtInput, PsbtOutput, TapLeaf } from 'bip174/src/lib/interfaces.js';
|
|
2
|
+
import { Taptree } from '../types.js';
|
|
3
|
+
export declare const toXOnly: (pubKey: Buffer | Uint8Array) => Buffer;
|
|
4
|
+
export declare function tapScriptFinalizer(inputIndex: number, input: PsbtInput, tapLeafHashToFinalize?: Buffer): {
|
|
5
|
+
finalScriptWitness: Buffer | undefined;
|
|
6
|
+
};
|
|
7
|
+
export declare function serializeTaprootSignature(sig: Buffer, sighashType?: number): Buffer;
|
|
8
|
+
export declare function isTaprootInput(input: PsbtInput): boolean;
|
|
9
|
+
export declare function isTaprootOutput(output: PsbtOutput, script?: Buffer): boolean;
|
|
10
|
+
export declare function checkTaprootInputFields(inputData: PsbtInput, newInputData: PsbtInput, action: string): void;
|
|
11
|
+
export declare function checkTaprootOutputFields(outputData: PsbtOutput, newOutputData: PsbtOutput, action: string): void;
|
|
12
|
+
export declare function tweakInternalPubKey(inputIndex: number, input: PsbtInput): Buffer;
|
|
13
|
+
export declare function tapTreeToList(tree: Taptree): TapLeaf[];
|
|
14
|
+
export declare function tapTreeFromList(leaves?: TapLeaf[]): Taptree;
|
|
15
|
+
export declare function checkTaprootInputForSigs(input: PsbtInput, action: string): boolean;
|
|
16
|
+
export declare function getTapKeySigFromWitness(finalScriptWitness?: Buffer): Buffer | undefined;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { isTapleaf, isTaptree } from '../types.js';
|
|
2
|
+
import { Transaction } from '../transaction.js';
|
|
3
|
+
import { LEAF_VERSION_TAPSCRIPT, MAX_TAPTREE_DEPTH, rootHashFromPath, tapleafHash, tweakKey, } from '../payments/bip341.js';
|
|
4
|
+
import { p2tr } from '../payments/p2tr.js';
|
|
5
|
+
import { isP2TR, pubkeyPositionInScript, signatureBlocksAction, witnessStackToScriptWitness, } from './psbtutils.js';
|
|
6
|
+
export const toXOnly = (pubKey) => {
|
|
7
|
+
const buffer = pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
|
|
8
|
+
return Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
|
|
9
|
+
};
|
|
10
|
+
export function tapScriptFinalizer(inputIndex, input, tapLeafHashToFinalize) {
|
|
11
|
+
const tapLeaf = findTapLeafToFinalize(input, inputIndex, tapLeafHashToFinalize);
|
|
12
|
+
try {
|
|
13
|
+
const sigs = sortSignatures(input, tapLeaf);
|
|
14
|
+
const witness = sigs.concat(tapLeaf.script).concat(tapLeaf.controlBlock);
|
|
15
|
+
return { finalScriptWitness: witnessStackToScriptWitness(witness) };
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
throw new Error(`Can not finalize taproot input #${inputIndex}: ${err}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function serializeTaprootSignature(sig, sighashType) {
|
|
22
|
+
const sighashTypeByte = sighashType ? Buffer.from([sighashType]) : Buffer.from([]);
|
|
23
|
+
return Buffer.concat([sig, sighashTypeByte]);
|
|
24
|
+
}
|
|
25
|
+
export function isTaprootInput(input) {
|
|
26
|
+
return (input &&
|
|
27
|
+
!!(input.tapInternalKey ||
|
|
28
|
+
input.tapMerkleRoot ||
|
|
29
|
+
(input.tapLeafScript && input.tapLeafScript.length) ||
|
|
30
|
+
(input.tapBip32Derivation && input.tapBip32Derivation.length) ||
|
|
31
|
+
(input.witnessUtxo && isP2TR(input.witnessUtxo.script))));
|
|
32
|
+
}
|
|
33
|
+
export function isTaprootOutput(output, script) {
|
|
34
|
+
return (output &&
|
|
35
|
+
!!(output.tapInternalKey ||
|
|
36
|
+
output.tapTree ||
|
|
37
|
+
(output.tapBip32Derivation && output.tapBip32Derivation.length) ||
|
|
38
|
+
(script && isP2TR(script))));
|
|
39
|
+
}
|
|
40
|
+
export function checkTaprootInputFields(inputData, newInputData, action) {
|
|
41
|
+
checkMixedTaprootAndNonTaprootInputFields(inputData, newInputData, action);
|
|
42
|
+
checkIfTapLeafInTree(inputData, newInputData, action);
|
|
43
|
+
}
|
|
44
|
+
export function checkTaprootOutputFields(outputData, newOutputData, action) {
|
|
45
|
+
checkMixedTaprootAndNonTaprootOutputFields(outputData, newOutputData, action);
|
|
46
|
+
checkTaprootScriptPubkey(outputData, newOutputData);
|
|
47
|
+
}
|
|
48
|
+
function checkTaprootScriptPubkey(outputData, newOutputData) {
|
|
49
|
+
if (!newOutputData.tapTree && !newOutputData.tapInternalKey)
|
|
50
|
+
return;
|
|
51
|
+
const tapInternalKey = newOutputData.tapInternalKey || outputData.tapInternalKey;
|
|
52
|
+
const tapTree = newOutputData.tapTree || outputData.tapTree;
|
|
53
|
+
if (tapInternalKey) {
|
|
54
|
+
const { script: scriptPubkey } = outputData;
|
|
55
|
+
const script = getTaprootScripPubkey(tapInternalKey, tapTree);
|
|
56
|
+
if (scriptPubkey && !scriptPubkey.equals(script))
|
|
57
|
+
throw new Error('Error adding output. Script or address missmatch.');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function getTaprootScripPubkey(tapInternalKey, tapTree) {
|
|
61
|
+
const scriptTree = tapTree && tapTreeFromList(tapTree.leaves);
|
|
62
|
+
const { output } = p2tr({
|
|
63
|
+
internalPubkey: tapInternalKey,
|
|
64
|
+
scriptTree,
|
|
65
|
+
});
|
|
66
|
+
return output;
|
|
67
|
+
}
|
|
68
|
+
export function tweakInternalPubKey(inputIndex, input) {
|
|
69
|
+
const tapInternalKey = input.tapInternalKey;
|
|
70
|
+
const outputKey = tapInternalKey && tweakKey(tapInternalKey, input.tapMerkleRoot);
|
|
71
|
+
if (!outputKey)
|
|
72
|
+
throw new Error(`Cannot tweak tap internal key for input #${inputIndex}. Public key: ${tapInternalKey && tapInternalKey.toString('hex')}`);
|
|
73
|
+
return outputKey.x;
|
|
74
|
+
}
|
|
75
|
+
export function tapTreeToList(tree) {
|
|
76
|
+
if (!isTaptree(tree))
|
|
77
|
+
throw new Error('Cannot convert taptree to tapleaf list. Expecting a tapree structure.');
|
|
78
|
+
return _tapTreeToList(tree);
|
|
79
|
+
}
|
|
80
|
+
export function tapTreeFromList(leaves = []) {
|
|
81
|
+
if (leaves.length === 1 && leaves[0].depth === 0)
|
|
82
|
+
return {
|
|
83
|
+
output: leaves[0].script,
|
|
84
|
+
version: leaves[0].leafVersion,
|
|
85
|
+
};
|
|
86
|
+
return insertLeavesInTree(leaves);
|
|
87
|
+
}
|
|
88
|
+
export function checkTaprootInputForSigs(input, action) {
|
|
89
|
+
const sigs = extractTaprootSigs(input);
|
|
90
|
+
return sigs.some((sig) => signatureBlocksAction(sig, decodeSchnorrSignature, action));
|
|
91
|
+
}
|
|
92
|
+
function decodeSchnorrSignature(signature) {
|
|
93
|
+
return {
|
|
94
|
+
signature: signature.slice(0, 64),
|
|
95
|
+
hashType: signature.slice(64)[0] || Transaction.SIGHASH_DEFAULT,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function extractTaprootSigs(input) {
|
|
99
|
+
const sigs = [];
|
|
100
|
+
if (input.tapKeySig)
|
|
101
|
+
sigs.push(input.tapKeySig);
|
|
102
|
+
if (input.tapScriptSig)
|
|
103
|
+
sigs.push(...input.tapScriptSig.map((s) => s.signature));
|
|
104
|
+
if (!sigs.length) {
|
|
105
|
+
const finalTapKeySig = getTapKeySigFromWitness(input.finalScriptWitness);
|
|
106
|
+
if (finalTapKeySig)
|
|
107
|
+
sigs.push(finalTapKeySig);
|
|
108
|
+
}
|
|
109
|
+
return sigs;
|
|
110
|
+
}
|
|
111
|
+
export function getTapKeySigFromWitness(finalScriptWitness) {
|
|
112
|
+
if (!finalScriptWitness)
|
|
113
|
+
return;
|
|
114
|
+
const witness = finalScriptWitness.slice(2);
|
|
115
|
+
if (witness.length === 64 || witness.length === 65)
|
|
116
|
+
return witness;
|
|
117
|
+
}
|
|
118
|
+
function _tapTreeToList(tree, leaves = [], depth = 0) {
|
|
119
|
+
if (depth > MAX_TAPTREE_DEPTH)
|
|
120
|
+
throw new Error('Max taptree depth exceeded.');
|
|
121
|
+
if (!tree)
|
|
122
|
+
return [];
|
|
123
|
+
if (isTapleaf(tree)) {
|
|
124
|
+
leaves.push({
|
|
125
|
+
depth,
|
|
126
|
+
leafVersion: tree.version || LEAF_VERSION_TAPSCRIPT,
|
|
127
|
+
script: tree.output,
|
|
128
|
+
});
|
|
129
|
+
return leaves;
|
|
130
|
+
}
|
|
131
|
+
if (tree[0])
|
|
132
|
+
_tapTreeToList(tree[0], leaves, depth + 1);
|
|
133
|
+
if (tree[1])
|
|
134
|
+
_tapTreeToList(tree[1], leaves, depth + 1);
|
|
135
|
+
return leaves;
|
|
136
|
+
}
|
|
137
|
+
function insertLeavesInTree(leaves) {
|
|
138
|
+
let tree;
|
|
139
|
+
for (const leaf of leaves) {
|
|
140
|
+
tree = insertLeafInTree(leaf, tree);
|
|
141
|
+
if (!tree)
|
|
142
|
+
throw new Error(`No room left to insert tapleaf in tree`);
|
|
143
|
+
}
|
|
144
|
+
return tree;
|
|
145
|
+
}
|
|
146
|
+
function insertLeafInTree(leaf, tree, depth = 0) {
|
|
147
|
+
if (depth > MAX_TAPTREE_DEPTH)
|
|
148
|
+
throw new Error('Max taptree depth exceeded.');
|
|
149
|
+
if (leaf.depth === depth) {
|
|
150
|
+
if (!tree)
|
|
151
|
+
return {
|
|
152
|
+
output: leaf.script,
|
|
153
|
+
version: leaf.leafVersion,
|
|
154
|
+
};
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (isTapleaf(tree))
|
|
158
|
+
return;
|
|
159
|
+
const leftSide = insertLeafInTree(leaf, tree && tree[0], depth + 1);
|
|
160
|
+
if (leftSide)
|
|
161
|
+
return [leftSide, tree && tree[1]];
|
|
162
|
+
const rightSide = insertLeafInTree(leaf, tree && tree[1], depth + 1);
|
|
163
|
+
if (rightSide)
|
|
164
|
+
return [tree && tree[0], rightSide];
|
|
165
|
+
}
|
|
166
|
+
function checkMixedTaprootAndNonTaprootInputFields(inputData, newInputData, action) {
|
|
167
|
+
const isBadTaprootUpdate = isTaprootInput(inputData) && hasNonTaprootFields(newInputData);
|
|
168
|
+
const isBadNonTaprootUpdate = hasNonTaprootFields(inputData) && isTaprootInput(newInputData);
|
|
169
|
+
const hasMixedFields = inputData === newInputData &&
|
|
170
|
+
isTaprootInput(newInputData) &&
|
|
171
|
+
hasNonTaprootFields(newInputData);
|
|
172
|
+
if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
|
|
173
|
+
throw new Error(`Invalid arguments for Psbt.${action}. ` +
|
|
174
|
+
`Cannot use both taproot and non-taproot fields.`);
|
|
175
|
+
}
|
|
176
|
+
function checkMixedTaprootAndNonTaprootOutputFields(inputData, newInputData, action) {
|
|
177
|
+
const isBadTaprootUpdate = isTaprootOutput(inputData) && hasNonTaprootFields(newInputData);
|
|
178
|
+
const isBadNonTaprootUpdate = hasNonTaprootFields(inputData) && isTaprootOutput(newInputData);
|
|
179
|
+
const hasMixedFields = inputData === newInputData &&
|
|
180
|
+
isTaprootOutput(newInputData) &&
|
|
181
|
+
hasNonTaprootFields(newInputData);
|
|
182
|
+
if (isBadTaprootUpdate || isBadNonTaprootUpdate || hasMixedFields)
|
|
183
|
+
throw new Error(`Invalid arguments for Psbt.${action}. ` +
|
|
184
|
+
`Cannot use both taproot and non-taproot fields.`);
|
|
185
|
+
}
|
|
186
|
+
function checkIfTapLeafInTree(inputData, newInputData, action) {
|
|
187
|
+
if (newInputData.tapMerkleRoot) {
|
|
188
|
+
const newLeafsInTree = (newInputData.tapLeafScript || []).every((l) => isTapLeafInTree(l, newInputData.tapMerkleRoot));
|
|
189
|
+
const oldLeafsInTree = (inputData.tapLeafScript || []).every((l) => isTapLeafInTree(l, newInputData.tapMerkleRoot));
|
|
190
|
+
if (!newLeafsInTree || !oldLeafsInTree)
|
|
191
|
+
throw new Error(`Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`);
|
|
192
|
+
}
|
|
193
|
+
else if (inputData.tapMerkleRoot) {
|
|
194
|
+
const newLeafsInTree = (newInputData.tapLeafScript || []).every((l) => isTapLeafInTree(l, inputData.tapMerkleRoot));
|
|
195
|
+
if (!newLeafsInTree)
|
|
196
|
+
throw new Error(`Invalid arguments for Psbt.${action}. Tapleaf not part of taptree.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function isTapLeafInTree(tapLeaf, merkleRoot) {
|
|
200
|
+
if (!merkleRoot)
|
|
201
|
+
return true;
|
|
202
|
+
const leafHash = tapleafHash({
|
|
203
|
+
output: tapLeaf.script,
|
|
204
|
+
version: tapLeaf.leafVersion,
|
|
205
|
+
});
|
|
206
|
+
const rootHash = rootHashFromPath(tapLeaf.controlBlock, leafHash);
|
|
207
|
+
return rootHash.equals(merkleRoot);
|
|
208
|
+
}
|
|
209
|
+
function sortSignatures(input, tapLeaf) {
|
|
210
|
+
const leafHash = tapleafHash({
|
|
211
|
+
output: tapLeaf.script,
|
|
212
|
+
version: tapLeaf.leafVersion,
|
|
213
|
+
});
|
|
214
|
+
return (input.tapScriptSig || [])
|
|
215
|
+
.filter((tss) => tss.leafHash.equals(leafHash))
|
|
216
|
+
.map((tss) => addPubkeyPositionInScript(tapLeaf.script, tss))
|
|
217
|
+
.sort((t1, t2) => t2.positionInScript - t1.positionInScript)
|
|
218
|
+
.map((t) => t.signature);
|
|
219
|
+
}
|
|
220
|
+
function addPubkeyPositionInScript(script, tss) {
|
|
221
|
+
return Object.assign({
|
|
222
|
+
positionInScript: pubkeyPositionInScript(tss.pubkey, script),
|
|
223
|
+
}, tss);
|
|
224
|
+
}
|
|
225
|
+
function findTapLeafToFinalize(input, inputIndex, leafHashToFinalize) {
|
|
226
|
+
if (!input.tapScriptSig || !input.tapScriptSig.length)
|
|
227
|
+
throw new Error(`Can not finalize taproot input #${inputIndex}. No tapleaf script signature provided.`);
|
|
228
|
+
const tapLeaf = (input.tapLeafScript || [])
|
|
229
|
+
.sort((a, b) => a.controlBlock.length - b.controlBlock.length)
|
|
230
|
+
.find((leaf) => canFinalizeLeaf(leaf, input.tapScriptSig, leafHashToFinalize));
|
|
231
|
+
if (!tapLeaf)
|
|
232
|
+
throw new Error(`Can not finalize taproot input #${inputIndex}. Signature for tapleaf script not found.`);
|
|
233
|
+
return tapLeaf;
|
|
234
|
+
}
|
|
235
|
+
function canFinalizeLeaf(leaf, tapScriptSig, hash) {
|
|
236
|
+
const leafHash = tapleafHash({
|
|
237
|
+
output: leaf.script,
|
|
238
|
+
version: leaf.leafVersion,
|
|
239
|
+
});
|
|
240
|
+
const whiteListedHash = !hash || hash.equals(leafHash);
|
|
241
|
+
return (whiteListedHash && tapScriptSig.find((tss) => tss.leafHash.equals(leafHash)) !== undefined);
|
|
242
|
+
}
|
|
243
|
+
function hasNonTaprootFields(io) {
|
|
244
|
+
return (io &&
|
|
245
|
+
!!(io.redeemScript || io.witnessScript || (io.bip32Derivation && io.bip32Derivation.length)));
|
|
246
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces.js';
|
|
2
|
+
export declare const isP2MS: (script: Buffer) => boolean;
|
|
3
|
+
export declare const isP2PK: (script: Buffer) => boolean;
|
|
4
|
+
export declare const isP2PKH: (script: Buffer) => boolean;
|
|
5
|
+
export declare const isP2WPKH: (script: Buffer) => boolean;
|
|
6
|
+
export declare const isP2WSHScript: (script: Buffer) => boolean;
|
|
7
|
+
export declare const isP2SHScript: (script: Buffer) => boolean;
|
|
8
|
+
export declare const isP2TR: (script: Buffer) => boolean;
|
|
9
|
+
export declare function witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
10
|
+
export interface UncompressedPublicKey {
|
|
11
|
+
hybrid: Buffer;
|
|
12
|
+
uncompressed: Buffer;
|
|
13
|
+
}
|
|
14
|
+
export declare function decompressPublicKey(realPubKey: Uint8Array | Buffer): UncompressedPublicKey;
|
|
15
|
+
export declare function bigIntTo32Bytes(num: bigint): Buffer;
|
|
16
|
+
export declare function pubkeysMatch(a: Buffer, b: Buffer): boolean;
|
|
17
|
+
export declare function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number;
|
|
18
|
+
export declare function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean;
|
|
19
|
+
export declare function checkInputForSig(input: PsbtInput, action: string): boolean;
|
|
20
|
+
type SignatureDecodeFunc = (buffer: Buffer) => {
|
|
21
|
+
signature: Buffer;
|
|
22
|
+
hashType: number;
|
|
23
|
+
};
|
|
24
|
+
export declare function signatureBlocksAction(signature: Buffer, signatureDecodeFn: SignatureDecodeFunc, action: string): boolean;
|
|
25
|
+
export declare function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[];
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { ProjectivePoint } from '@noble/secp256k1';
|
|
2
|
+
import * as varuint from 'bip174/src/lib/converter/varint.js';
|
|
3
|
+
import { hash160 } from '../crypto.js';
|
|
4
|
+
import { p2ms } from '../payments/p2ms.js';
|
|
5
|
+
import { p2pk } from '../payments/p2pk.js';
|
|
6
|
+
import { p2pkh } from '../payments/p2pkh.js';
|
|
7
|
+
import { p2sh } from '../payments/p2sh.js';
|
|
8
|
+
import { p2tr } from '../payments/p2tr.js';
|
|
9
|
+
import { p2wpkh } from '../payments/p2wpkh.js';
|
|
10
|
+
import { p2wsh } from '../payments/p2wsh.js';
|
|
11
|
+
import * as bscript from '../script.js';
|
|
12
|
+
import { Transaction } from '../transaction.js';
|
|
13
|
+
import { toXOnly } from './bip371.js';
|
|
14
|
+
function isPaymentFactory(payment) {
|
|
15
|
+
return (script) => {
|
|
16
|
+
try {
|
|
17
|
+
payment({ output: script });
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export const isP2MS = isPaymentFactory(p2ms);
|
|
26
|
+
export const isP2PK = isPaymentFactory(p2pk);
|
|
27
|
+
export const isP2PKH = isPaymentFactory(p2pkh);
|
|
28
|
+
export const isP2WPKH = isPaymentFactory(p2wpkh);
|
|
29
|
+
export const isP2WSHScript = isPaymentFactory(p2wsh);
|
|
30
|
+
export const isP2SHScript = isPaymentFactory(p2sh);
|
|
31
|
+
export const isP2TR = isPaymentFactory(p2tr);
|
|
32
|
+
export function witnessStackToScriptWitness(witness) {
|
|
33
|
+
let buffer = Buffer.allocUnsafe(0);
|
|
34
|
+
function writeSlice(slice) {
|
|
35
|
+
buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
|
36
|
+
}
|
|
37
|
+
function writeVarInt(i) {
|
|
38
|
+
const currentLen = buffer.length;
|
|
39
|
+
const varintLen = varuint.encodingLength(i);
|
|
40
|
+
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
41
|
+
varuint.encode(i, buffer, currentLen);
|
|
42
|
+
}
|
|
43
|
+
function writeVarSlice(slice) {
|
|
44
|
+
writeVarInt(slice.length);
|
|
45
|
+
writeSlice(slice);
|
|
46
|
+
}
|
|
47
|
+
function writeVector(vector) {
|
|
48
|
+
writeVarInt(vector.length);
|
|
49
|
+
vector.forEach(writeVarSlice);
|
|
50
|
+
}
|
|
51
|
+
writeVector(witness);
|
|
52
|
+
return buffer;
|
|
53
|
+
}
|
|
54
|
+
export function decompressPublicKey(realPubKey) {
|
|
55
|
+
if (![33, 65].includes(realPubKey.length)) {
|
|
56
|
+
throw new Error(`Unsupported key length=${realPubKey.length}. Must be 33 (compressed) or 65 (uncompressed).`);
|
|
57
|
+
}
|
|
58
|
+
let point;
|
|
59
|
+
try {
|
|
60
|
+
point = ProjectivePoint.fromHex(realPubKey);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
throw new Error('Invalid secp256k1 public key bytes. Cannot parse.');
|
|
64
|
+
}
|
|
65
|
+
const xBuf = bigIntTo32Bytes(point.x);
|
|
66
|
+
const yBuf = bigIntTo32Bytes(point.y);
|
|
67
|
+
const isEven = point.y % 2n === 0n;
|
|
68
|
+
const prefix = isEven ? 0x06 : 0x07;
|
|
69
|
+
const hybridPubKey = Buffer.alloc(65);
|
|
70
|
+
hybridPubKey[0] = prefix;
|
|
71
|
+
xBuf.copy(hybridPubKey, 1);
|
|
72
|
+
yBuf.copy(hybridPubKey, 33);
|
|
73
|
+
const uncompressedPubKey = Buffer.concat([Buffer.from([0x04]), xBuf, yBuf]);
|
|
74
|
+
return {
|
|
75
|
+
hybrid: hybridPubKey,
|
|
76
|
+
uncompressed: uncompressedPubKey,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function bigIntTo32Bytes(num) {
|
|
80
|
+
let hex = num.toString(16);
|
|
81
|
+
hex = hex.padStart(64, '0');
|
|
82
|
+
if (hex.length > 64) {
|
|
83
|
+
hex = hex.slice(-64);
|
|
84
|
+
}
|
|
85
|
+
return Buffer.from(hex, 'hex');
|
|
86
|
+
}
|
|
87
|
+
export function pubkeysMatch(a, b) {
|
|
88
|
+
if (a.equals(b))
|
|
89
|
+
return true;
|
|
90
|
+
if (a.length === 65 && b.length === 65) {
|
|
91
|
+
const aCopy = Buffer.from(a);
|
|
92
|
+
const bCopy = Buffer.from(b);
|
|
93
|
+
if (aCopy[0] === 0x06 || aCopy[0] === 0x07)
|
|
94
|
+
aCopy[0] = 0x04;
|
|
95
|
+
if (bCopy[0] === 0x06 || bCopy[0] === 0x07)
|
|
96
|
+
bCopy[0] = 0x04;
|
|
97
|
+
return aCopy.equals(bCopy);
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
export function pubkeyPositionInScript(pubkey, script) {
|
|
102
|
+
const pubkeyHash = hash160(pubkey);
|
|
103
|
+
const pubkeyXOnly = toXOnly(pubkey);
|
|
104
|
+
const uncompressed = decompressPublicKey(pubkey);
|
|
105
|
+
const decompiled = bscript.decompile(script);
|
|
106
|
+
if (decompiled === null)
|
|
107
|
+
throw new Error('Unknown script error');
|
|
108
|
+
return decompiled.findIndex((element) => {
|
|
109
|
+
if (typeof element === 'number')
|
|
110
|
+
return false;
|
|
111
|
+
if (pubkeysMatch(element, pubkey))
|
|
112
|
+
return true;
|
|
113
|
+
if (pubkeysMatch(element, pubkeyXOnly))
|
|
114
|
+
return true;
|
|
115
|
+
if (pubkeysMatch(element, uncompressed.uncompressed))
|
|
116
|
+
return true;
|
|
117
|
+
if (pubkeysMatch(element, uncompressed.hybrid))
|
|
118
|
+
return true;
|
|
119
|
+
return element.equals(pubkeyHash);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
export function pubkeyInScript(pubkey, script) {
|
|
123
|
+
return pubkeyPositionInScript(pubkey, script) !== -1;
|
|
124
|
+
}
|
|
125
|
+
export function checkInputForSig(input, action) {
|
|
126
|
+
const pSigs = extractPartialSigs(input);
|
|
127
|
+
return pSigs.some((pSig) => signatureBlocksAction(pSig, bscript.signature.decode, action));
|
|
128
|
+
}
|
|
129
|
+
export function signatureBlocksAction(signature, signatureDecodeFn, action) {
|
|
130
|
+
const { hashType } = signatureDecodeFn(signature);
|
|
131
|
+
const whitelist = [];
|
|
132
|
+
const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
|
|
133
|
+
if (isAnyoneCanPay)
|
|
134
|
+
whitelist.push('addInput');
|
|
135
|
+
const hashMod = hashType & 0x1f;
|
|
136
|
+
switch (hashMod) {
|
|
137
|
+
case Transaction.SIGHASH_ALL:
|
|
138
|
+
break;
|
|
139
|
+
case Transaction.SIGHASH_SINGLE:
|
|
140
|
+
case Transaction.SIGHASH_NONE:
|
|
141
|
+
whitelist.push('addOutput');
|
|
142
|
+
whitelist.push('setInputSequence');
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
return whitelist.indexOf(action) === -1;
|
|
146
|
+
}
|
|
147
|
+
function extractPartialSigs(input) {
|
|
148
|
+
let pSigs = [];
|
|
149
|
+
if ((input.partialSig || []).length === 0) {
|
|
150
|
+
if (!input.finalScriptSig && !input.finalScriptWitness)
|
|
151
|
+
return [];
|
|
152
|
+
pSigs = getPsigsFromInputFinalScripts(input);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
pSigs = input.partialSig;
|
|
156
|
+
}
|
|
157
|
+
return pSigs.map((p) => p.signature);
|
|
158
|
+
}
|
|
159
|
+
export function getPsigsFromInputFinalScripts(input) {
|
|
160
|
+
const scriptItems = !input.finalScriptSig ? [] : bscript.decompile(input.finalScriptSig) || [];
|
|
161
|
+
const witnessItems = !input.finalScriptWitness
|
|
162
|
+
? []
|
|
163
|
+
: bscript.decompile(input.finalScriptWitness) || [];
|
|
164
|
+
return scriptItems
|
|
165
|
+
.concat(witnessItems)
|
|
166
|
+
.filter((item) => {
|
|
167
|
+
return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
|
|
168
|
+
})
|
|
169
|
+
.map((sig) => ({ signature: sig }));
|
|
170
|
+
}
|