@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
package/src/{psbt.js → psbt.ts}
RENAMED
|
@@ -1,28 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { Psbt as PsbtBase } from 'bip174';
|
|
2
|
+
import * as varuint from 'bip174/src/lib/converter/varint.js';
|
|
3
|
+
import {
|
|
4
|
+
Bip32Derivation,
|
|
5
|
+
KeyValue,
|
|
6
|
+
PartialSig,
|
|
7
|
+
PsbtGlobal,
|
|
8
|
+
PsbtGlobalUpdate,
|
|
9
|
+
PsbtInput,
|
|
10
|
+
PsbtInputUpdate,
|
|
11
|
+
PsbtOutput,
|
|
12
|
+
PsbtOutputUpdate,
|
|
13
|
+
TapKeySig,
|
|
14
|
+
TapScriptSig,
|
|
15
|
+
Transaction as ITransaction,
|
|
16
|
+
TransactionFromBuffer,
|
|
17
|
+
} from 'bip174/src/lib/interfaces.js';
|
|
18
|
+
import { checkForInput, checkForOutput } from 'bip174/src/lib/utils.js';
|
|
19
|
+
import { BIP32Interface } from 'bip32';
|
|
20
|
+
import { ECPairInterface } from 'ecpair';
|
|
21
|
+
import { fromOutputScript, toOutputScript } from './address.js';
|
|
22
|
+
import { cloneBuffer, reverseBuffer } from './bufferutils.js';
|
|
23
|
+
import { hookSigner } from './hooks/HookedSigner.js';
|
|
24
|
+
import { payments } from './index.js';
|
|
25
|
+
import { bitcoin as btcNetwork, Network } from './networks.js';
|
|
26
|
+
import { tapleafHash } from './payments/bip341.js';
|
|
27
|
+
import { Payment, PaymentOpts } from './payments/index.js';
|
|
28
|
+
import {
|
|
29
|
+
checkTaprootInputFields,
|
|
30
|
+
checkTaprootInputForSigs,
|
|
31
|
+
checkTaprootOutputFields,
|
|
32
|
+
isTaprootInput,
|
|
33
|
+
serializeTaprootSignature,
|
|
34
|
+
tapScriptFinalizer,
|
|
35
|
+
toXOnly,
|
|
36
|
+
} from './psbt/bip371.js';
|
|
37
|
+
import {
|
|
38
|
+
checkInputForSig,
|
|
39
|
+
isP2MS,
|
|
40
|
+
isP2PK,
|
|
41
|
+
isP2PKH,
|
|
42
|
+
isP2SHScript,
|
|
43
|
+
isP2TR,
|
|
44
|
+
isP2WPKH,
|
|
45
|
+
isP2WSHScript,
|
|
46
|
+
pubkeyInScript,
|
|
47
|
+
witnessStackToScriptWitness,
|
|
48
|
+
} from './psbt/psbtutils.js';
|
|
49
|
+
import * as bscript from './script.js';
|
|
50
|
+
import { Output, Transaction } from './transaction.js';
|
|
51
|
+
|
|
52
|
+
export interface TransactionInput {
|
|
53
|
+
hash: string | Buffer;
|
|
54
|
+
index: number;
|
|
55
|
+
sequence?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface PsbtTxInput extends TransactionInput {
|
|
59
|
+
hash: Buffer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface TransactionOutput {
|
|
63
|
+
script: Buffer;
|
|
64
|
+
value: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface PsbtTxOutput extends TransactionOutput {
|
|
68
|
+
address: string | undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// msghash is 32 byte hash of preimage, signature is 64 byte compact signature (r,s 32 bytes each)
|
|
72
|
+
export type ValidateSigFunction = (pubkey: Buffer, msghash: Buffer, signature: Buffer) => boolean;
|
|
73
|
+
|
|
17
74
|
/**
|
|
18
75
|
* These are the default arguments for a Psbt instance.
|
|
19
76
|
*/
|
|
20
|
-
const DEFAULT_OPTS = {
|
|
77
|
+
const DEFAULT_OPTS: PsbtOpts = {
|
|
21
78
|
/**
|
|
22
79
|
* A bitcoinjs Network object. This is only used if you pass an `address`
|
|
23
80
|
* parameter to addOutput. Otherwise it is not needed and can be left default.
|
|
24
81
|
*/
|
|
25
|
-
network:
|
|
82
|
+
network: btcNetwork,
|
|
26
83
|
/**
|
|
27
84
|
* When extractTransaction is called, the fee rate is checked.
|
|
28
85
|
* THIS IS NOT TO BE RELIED ON.
|
|
@@ -30,6 +87,13 @@ const DEFAULT_OPTS = {
|
|
|
30
87
|
*/
|
|
31
88
|
maximumFeeRate: 5000, // satoshi per byte
|
|
32
89
|
};
|
|
90
|
+
|
|
91
|
+
// Not a breaking change.
|
|
92
|
+
export interface PsbtBaseExtended extends Omit<PsbtBase, 'inputs'> {
|
|
93
|
+
inputs: PsbtInput[];
|
|
94
|
+
globalMap: PsbtGlobal;
|
|
95
|
+
}
|
|
96
|
+
|
|
33
97
|
/**
|
|
34
98
|
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
35
99
|
* There are 6 roles that this class fulfills. (Explained in BIP174)
|
|
@@ -70,103 +134,112 @@ const DEFAULT_OPTS = {
|
|
|
70
134
|
/**
|
|
71
135
|
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
72
136
|
*/
|
|
73
|
-
class Psbt {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
constructor(
|
|
78
|
-
|
|
137
|
+
export class Psbt {
|
|
138
|
+
private readonly __CACHE: PsbtCache;
|
|
139
|
+
private readonly opts: PsbtOpts;
|
|
140
|
+
|
|
141
|
+
constructor(
|
|
142
|
+
opts: PsbtOptsOptional = {},
|
|
143
|
+
public data: PsbtBaseExtended = new PsbtBase(new PsbtTransaction()),
|
|
144
|
+
) {
|
|
79
145
|
this.opts = Object.assign({}, DEFAULT_OPTS, opts);
|
|
80
146
|
this.__CACHE = {
|
|
81
147
|
__NON_WITNESS_UTXO_TX_CACHE: [],
|
|
82
148
|
__NON_WITNESS_UTXO_BUF_CACHE: [],
|
|
83
149
|
__TX_IN_CACHE: {},
|
|
84
150
|
// @ts-expect-error no.
|
|
85
|
-
__TX: this.data.globalMap.unsignedTx.tx,
|
|
151
|
+
__TX: this.data.globalMap.unsignedTx.tx, // Now TypeScript knows unsignedTx is a PsbtTransaction
|
|
86
152
|
__UNSAFE_SIGN_NONSEGWIT: false,
|
|
87
153
|
};
|
|
88
154
|
if (this.data.inputs.length === 0) this.setVersion(2);
|
|
89
|
-
|
|
155
|
+
|
|
156
|
+
const dpew = <T>(obj: T, attr: string, enumerable: boolean, writable: boolean): void => {
|
|
90
157
|
Object.defineProperty(obj, attr, {
|
|
91
158
|
enumerable,
|
|
92
159
|
writable,
|
|
93
160
|
});
|
|
94
161
|
};
|
|
162
|
+
|
|
95
163
|
dpew(this, '__CACHE', false, true);
|
|
96
164
|
dpew(this, 'opts', false, true);
|
|
97
165
|
}
|
|
98
|
-
|
|
166
|
+
|
|
167
|
+
get inputCount(): number {
|
|
99
168
|
return this.data.inputs.length;
|
|
100
169
|
}
|
|
101
|
-
|
|
170
|
+
|
|
171
|
+
get version(): number {
|
|
102
172
|
return this.__CACHE.__TX.version;
|
|
103
173
|
}
|
|
104
|
-
|
|
174
|
+
|
|
175
|
+
set version(version: number) {
|
|
105
176
|
this.setVersion(version);
|
|
106
177
|
}
|
|
107
|
-
|
|
178
|
+
|
|
179
|
+
get locktime(): number {
|
|
108
180
|
return this.__CACHE.__TX.locktime;
|
|
109
181
|
}
|
|
110
|
-
|
|
182
|
+
|
|
183
|
+
set locktime(locktime: number) {
|
|
111
184
|
this.setLocktime(locktime);
|
|
112
185
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
186
|
+
|
|
187
|
+
get txInputs(): PsbtTxInput[] {
|
|
188
|
+
return this.__CACHE.__TX.ins.map((input) => ({
|
|
189
|
+
hash: cloneBuffer(input.hash),
|
|
116
190
|
index: input.index,
|
|
117
191
|
sequence: input.sequence,
|
|
118
192
|
}));
|
|
119
193
|
}
|
|
120
|
-
|
|
121
|
-
|
|
194
|
+
|
|
195
|
+
get txOutputs(): PsbtTxOutput[] {
|
|
196
|
+
return this.__CACHE.__TX.outs.map((output) => {
|
|
122
197
|
let address;
|
|
123
198
|
try {
|
|
124
|
-
address = (
|
|
125
|
-
output.script,
|
|
126
|
-
this.opts.network,
|
|
127
|
-
);
|
|
199
|
+
address = fromOutputScript(output.script, this.opts.network);
|
|
128
200
|
} catch (_) {}
|
|
129
201
|
return {
|
|
130
|
-
script:
|
|
202
|
+
script: cloneBuffer(output.script),
|
|
131
203
|
value: output.value,
|
|
132
204
|
address,
|
|
133
205
|
};
|
|
134
206
|
});
|
|
135
207
|
}
|
|
136
|
-
|
|
208
|
+
|
|
209
|
+
static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
137
210
|
const buffer = Buffer.from(data, 'base64');
|
|
138
211
|
return this.fromBuffer(buffer, opts);
|
|
139
212
|
}
|
|
140
|
-
|
|
213
|
+
|
|
214
|
+
static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
141
215
|
const buffer = Buffer.from(data, 'hex');
|
|
142
216
|
return this.fromBuffer(buffer, opts);
|
|
143
217
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
transactionFromBuffer,
|
|
148
|
-
);
|
|
218
|
+
|
|
219
|
+
static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt {
|
|
220
|
+
const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
|
|
149
221
|
const psbt = new Psbt(opts, psbtBase);
|
|
150
222
|
checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
|
|
151
223
|
return psbt;
|
|
152
224
|
}
|
|
153
|
-
|
|
154
|
-
|
|
225
|
+
|
|
226
|
+
combine(...those: Psbt[]): this {
|
|
227
|
+
this.data.combine(...those.map((o) => o.data));
|
|
155
228
|
return this;
|
|
156
229
|
}
|
|
157
|
-
|
|
230
|
+
|
|
231
|
+
clone(): Psbt {
|
|
158
232
|
// TODO: more efficient cloning
|
|
159
|
-
const res = Psbt.fromBuffer(
|
|
160
|
-
this.data.toBuffer(),
|
|
161
|
-
JSON.parse(JSON.stringify(this.opts)),
|
|
162
|
-
);
|
|
233
|
+
const res = Psbt.fromBuffer(this.data.toBuffer(), JSON.parse(JSON.stringify(this.opts)));
|
|
163
234
|
return res;
|
|
164
235
|
}
|
|
165
|
-
|
|
236
|
+
|
|
237
|
+
setMaximumFeeRate(satoshiPerByte: number): void {
|
|
166
238
|
check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
|
|
167
239
|
this.opts.maximumFeeRate = satoshiPerByte;
|
|
168
240
|
}
|
|
169
|
-
|
|
241
|
+
|
|
242
|
+
setVersion(version: number): this {
|
|
170
243
|
check32Bit(version);
|
|
171
244
|
checkInputsForPartialSig(this.data.inputs, 'setVersion');
|
|
172
245
|
const c = this.__CACHE;
|
|
@@ -174,7 +247,8 @@ class Psbt {
|
|
|
174
247
|
c.__EXTRACTED_TX = undefined;
|
|
175
248
|
return this;
|
|
176
249
|
}
|
|
177
|
-
|
|
250
|
+
|
|
251
|
+
setLocktime(locktime: number): this {
|
|
178
252
|
check32Bit(locktime);
|
|
179
253
|
checkInputsForPartialSig(this.data.inputs, 'setLocktime');
|
|
180
254
|
const c = this.__CACHE;
|
|
@@ -182,7 +256,8 @@ class Psbt {
|
|
|
182
256
|
c.__EXTRACTED_TX = undefined;
|
|
183
257
|
return this;
|
|
184
258
|
}
|
|
185
|
-
|
|
259
|
+
|
|
260
|
+
setInputSequence(inputIndex: number, sequence: number): this {
|
|
186
261
|
check32Bit(sequence);
|
|
187
262
|
checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
|
|
188
263
|
const c = this.__CACHE;
|
|
@@ -193,32 +268,32 @@ class Psbt {
|
|
|
193
268
|
c.__EXTRACTED_TX = undefined;
|
|
194
269
|
return this;
|
|
195
270
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
271
|
+
|
|
272
|
+
addInputs(inputDatas: PsbtInputExtended[], checkPartialSigs: boolean = true): this {
|
|
273
|
+
inputDatas.forEach((inputData) => this.addInput(inputData, checkPartialSigs));
|
|
200
274
|
return this;
|
|
201
275
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
inputData.hash === undefined ||
|
|
206
|
-
inputData.index === undefined
|
|
207
|
-
) {
|
|
276
|
+
|
|
277
|
+
addInput(inputData: PsbtInputExtended, checkPartialSigs: boolean = true): this {
|
|
278
|
+
if (!inputData || inputData.hash === undefined || inputData.index === undefined) {
|
|
208
279
|
throw new Error(
|
|
209
280
|
`Invalid arguments for Psbt.addInput. ` +
|
|
210
281
|
`Requires single object with at least [hash] and [index]`,
|
|
211
282
|
);
|
|
212
283
|
}
|
|
213
|
-
|
|
284
|
+
|
|
285
|
+
checkTaprootInputFields(inputData, inputData, 'addInput');
|
|
286
|
+
|
|
214
287
|
if (checkPartialSigs) {
|
|
215
288
|
checkInputsForPartialSig(this.data.inputs, 'addInput');
|
|
216
289
|
}
|
|
290
|
+
|
|
217
291
|
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
|
|
218
292
|
const c = this.__CACHE;
|
|
219
293
|
this.data.addInput(inputData);
|
|
220
294
|
const txIn = c.__TX.ins[c.__TX.ins.length - 1];
|
|
221
295
|
checkTxInputCache(c, txIn);
|
|
296
|
+
|
|
222
297
|
const inputIndex = this.data.inputs.length - 1;
|
|
223
298
|
const input = this.data.inputs[inputIndex];
|
|
224
299
|
if (input.nonWitnessUtxo) {
|
|
@@ -229,17 +304,18 @@ class Psbt {
|
|
|
229
304
|
c.__EXTRACTED_TX = undefined;
|
|
230
305
|
return this;
|
|
231
306
|
}
|
|
232
|
-
|
|
233
|
-
|
|
307
|
+
|
|
308
|
+
addOutputs(outputDatas: PsbtOutputExtended[]): this {
|
|
309
|
+
outputDatas.forEach((outputData) => this.addOutput(outputData));
|
|
234
310
|
return this;
|
|
235
311
|
}
|
|
236
|
-
|
|
312
|
+
|
|
313
|
+
addOutput(outputData: PsbtOutputExtended): this {
|
|
237
314
|
if (
|
|
238
315
|
arguments.length > 1 ||
|
|
239
316
|
!outputData ||
|
|
240
317
|
outputData.value === undefined ||
|
|
241
|
-
(outputData.address === undefined &&
|
|
242
|
-
outputData.script === undefined)
|
|
318
|
+
((outputData as any).address === undefined && (outputData as any).script === undefined)
|
|
243
319
|
) {
|
|
244
320
|
throw new Error(
|
|
245
321
|
`Invalid arguments for Psbt.addOutput. ` +
|
|
@@ -247,17 +323,14 @@ class Psbt {
|
|
|
247
323
|
);
|
|
248
324
|
}
|
|
249
325
|
checkInputsForPartialSig(this.data.inputs, 'addOutput');
|
|
250
|
-
const { address } = outputData;
|
|
326
|
+
const { address } = outputData as any;
|
|
251
327
|
if (typeof address === 'string') {
|
|
252
328
|
const { network } = this.opts;
|
|
253
|
-
const script =
|
|
329
|
+
const script = toOutputScript(address, network);
|
|
254
330
|
outputData = Object.assign({}, outputData, { script });
|
|
255
331
|
}
|
|
256
|
-
(
|
|
257
|
-
|
|
258
|
-
outputData,
|
|
259
|
-
'addOutput',
|
|
260
|
-
);
|
|
332
|
+
checkTaprootOutputFields(outputData, outputData, 'addOutput');
|
|
333
|
+
|
|
261
334
|
const c = this.__CACHE;
|
|
262
335
|
this.data.addOutput(outputData);
|
|
263
336
|
c.__FEE = undefined;
|
|
@@ -265,68 +338,71 @@ class Psbt {
|
|
|
265
338
|
c.__EXTRACTED_TX = undefined;
|
|
266
339
|
return this;
|
|
267
340
|
}
|
|
268
|
-
|
|
341
|
+
|
|
342
|
+
extractTransaction(disableFeeCheck?: boolean, disableOutputChecks?: boolean): Transaction {
|
|
269
343
|
if (disableOutputChecks) {
|
|
270
|
-
this.data.inputs = this.data.inputs.filter(i => !i.partialSig);
|
|
344
|
+
this.data.inputs = this.data.inputs.filter((i) => !i.partialSig);
|
|
271
345
|
}
|
|
272
|
-
|
|
273
|
-
|
|
346
|
+
|
|
347
|
+
if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
|
|
274
348
|
const c = this.__CACHE;
|
|
275
349
|
if (!disableFeeCheck) {
|
|
276
350
|
checkFees(this, c, this.opts);
|
|
277
351
|
}
|
|
278
352
|
if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
|
|
279
353
|
const tx = c.__TX.clone();
|
|
280
|
-
inputFinalizeGetAmts(
|
|
281
|
-
this.data.inputs,
|
|
282
|
-
tx,
|
|
283
|
-
c,
|
|
284
|
-
true,
|
|
285
|
-
disableOutputChecks,
|
|
286
|
-
);
|
|
354
|
+
inputFinalizeGetAmts(this.data.inputs, tx, c, true, disableOutputChecks);
|
|
287
355
|
return tx;
|
|
288
356
|
}
|
|
289
|
-
|
|
357
|
+
|
|
358
|
+
getFeeRate(disableOutputChecks: boolean = false): number {
|
|
290
359
|
return getTxCacheValue(
|
|
291
360
|
'__FEE_RATE',
|
|
292
361
|
'fee rate',
|
|
293
362
|
this.data.inputs,
|
|
294
363
|
this.__CACHE,
|
|
295
364
|
disableOutputChecks,
|
|
296
|
-
)
|
|
365
|
+
)!;
|
|
297
366
|
}
|
|
298
|
-
|
|
367
|
+
|
|
368
|
+
getFee(disableOutputChecks: boolean = false): number {
|
|
299
369
|
return getTxCacheValue(
|
|
300
370
|
'__FEE',
|
|
301
371
|
'fee',
|
|
302
372
|
this.data.inputs,
|
|
303
373
|
this.__CACHE,
|
|
304
374
|
disableOutputChecks,
|
|
305
|
-
)
|
|
375
|
+
)!;
|
|
306
376
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
377
|
+
|
|
378
|
+
finalizeAllInputs(): this {
|
|
379
|
+
checkForInput(this.data.inputs, 0); // making sure we have at least one
|
|
380
|
+
range(this.data.inputs.length).forEach((idx) => this.finalizeInput(idx));
|
|
310
381
|
return this;
|
|
311
382
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
383
|
+
|
|
384
|
+
finalizeInput(
|
|
385
|
+
inputIndex: number,
|
|
386
|
+
finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc,
|
|
387
|
+
): this {
|
|
388
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
389
|
+
if (isTaprootInput(input))
|
|
315
390
|
return this._finalizeTaprootInput(
|
|
316
391
|
inputIndex,
|
|
317
392
|
input,
|
|
318
393
|
undefined,
|
|
319
|
-
finalScriptsFunc,
|
|
394
|
+
finalScriptsFunc as FinalTaprootScriptsFunc,
|
|
320
395
|
);
|
|
321
|
-
return this._finalizeInput(inputIndex, input, finalScriptsFunc);
|
|
396
|
+
return this._finalizeInput(inputIndex, input, finalScriptsFunc as FinalScriptsFunc);
|
|
322
397
|
}
|
|
398
|
+
|
|
323
399
|
finalizeTaprootInput(
|
|
324
|
-
inputIndex,
|
|
325
|
-
tapLeafHashToFinalize,
|
|
326
|
-
finalScriptsFunc =
|
|
327
|
-
) {
|
|
328
|
-
const input =
|
|
329
|
-
if (
|
|
400
|
+
inputIndex: number,
|
|
401
|
+
tapLeafHashToFinalize?: Buffer,
|
|
402
|
+
finalScriptsFunc: FinalTaprootScriptsFunc = tapScriptFinalizer,
|
|
403
|
+
): this {
|
|
404
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
405
|
+
if (isTaprootInput(input))
|
|
330
406
|
return this._finalizeTaprootInput(
|
|
331
407
|
inputIndex,
|
|
332
408
|
input,
|
|
@@ -335,77 +411,70 @@ class Psbt {
|
|
|
335
411
|
);
|
|
336
412
|
throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
|
|
337
413
|
}
|
|
338
|
-
|
|
339
|
-
|
|
414
|
+
|
|
415
|
+
getInputType(inputIndex: number): AllScriptType {
|
|
416
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
340
417
|
const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
|
|
341
418
|
const result = getMeaningfulScript(
|
|
342
419
|
script,
|
|
343
420
|
inputIndex,
|
|
344
421
|
'input',
|
|
345
|
-
input.redeemScript ||
|
|
346
|
-
|
|
347
|
-
input.witnessScript ||
|
|
348
|
-
redeemFromFinalWitnessScript(input.finalScriptWitness),
|
|
422
|
+
input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig),
|
|
423
|
+
input.witnessScript || redeemFromFinalWitnessScript(input.finalScriptWitness),
|
|
349
424
|
);
|
|
350
425
|
const type = result.type === 'raw' ? '' : result.type + '-';
|
|
351
426
|
const mainType = classifyScript(result.meaningfulScript);
|
|
352
|
-
return type + mainType;
|
|
427
|
+
return (type + mainType) as AllScriptType;
|
|
353
428
|
}
|
|
354
|
-
|
|
355
|
-
|
|
429
|
+
|
|
430
|
+
inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean {
|
|
431
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
356
432
|
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
|
|
357
433
|
}
|
|
358
|
-
|
|
359
|
-
|
|
434
|
+
|
|
435
|
+
inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
|
|
436
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
360
437
|
const derivationIsMine = bip32DerivationIsMine(root);
|
|
361
|
-
return (
|
|
362
|
-
!!input.bip32Derivation &&
|
|
363
|
-
input.bip32Derivation.some(derivationIsMine)
|
|
364
|
-
);
|
|
438
|
+
return !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine);
|
|
365
439
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
outputIndex,
|
|
370
|
-
);
|
|
440
|
+
|
|
441
|
+
outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean {
|
|
442
|
+
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
371
443
|
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
|
|
372
444
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
outputIndex,
|
|
377
|
-
);
|
|
445
|
+
|
|
446
|
+
outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
|
|
447
|
+
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
378
448
|
const derivationIsMine = bip32DerivationIsMine(root);
|
|
379
|
-
return (
|
|
380
|
-
!!output.bip32Derivation &&
|
|
381
|
-
output.bip32Derivation.some(derivationIsMine)
|
|
382
|
-
);
|
|
449
|
+
return !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine);
|
|
383
450
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
451
|
+
|
|
452
|
+
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean {
|
|
453
|
+
checkForInput(this.data.inputs, 0); // making sure we have at least one
|
|
454
|
+
const results = range(this.data.inputs.length).map((idx) =>
|
|
387
455
|
this.validateSignaturesOfInput(idx, validator),
|
|
388
456
|
);
|
|
389
457
|
return results.reduce((final, res) => res === true && final, true);
|
|
390
458
|
}
|
|
391
|
-
|
|
459
|
+
|
|
460
|
+
validateSignaturesOfInput(
|
|
461
|
+
inputIndex: number,
|
|
462
|
+
validator: ValidateSigFunction,
|
|
463
|
+
pubkey?: Buffer,
|
|
464
|
+
): boolean {
|
|
392
465
|
const input = this.data.inputs[inputIndex];
|
|
393
|
-
if (
|
|
394
|
-
return this.validateSignaturesOfTaprootInput(
|
|
395
|
-
|
|
396
|
-
validator,
|
|
397
|
-
pubkey,
|
|
398
|
-
);
|
|
466
|
+
if (isTaprootInput(input))
|
|
467
|
+
return this.validateSignaturesOfTaprootInput(inputIndex, validator, pubkey);
|
|
468
|
+
|
|
399
469
|
return this._validateSignaturesOfInput(inputIndex, validator, pubkey);
|
|
400
470
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
|
404
|
-
) {
|
|
471
|
+
|
|
472
|
+
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes: number[] = [Transaction.SIGHASH_ALL]): this {
|
|
405
473
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
406
474
|
throw new Error('Need HDSigner to sign input');
|
|
407
475
|
}
|
|
408
|
-
|
|
476
|
+
|
|
477
|
+
const results: boolean[] = [];
|
|
409
478
|
for (const i of range(this.data.inputs.length)) {
|
|
410
479
|
try {
|
|
411
480
|
this.signInputHD(i, hdKeyPair, sighashTypes);
|
|
@@ -414,21 +483,23 @@ class Psbt {
|
|
|
414
483
|
results.push(false);
|
|
415
484
|
}
|
|
416
485
|
}
|
|
417
|
-
if (results.every(v => v === false)) {
|
|
486
|
+
if (results.every((v) => v === false)) {
|
|
418
487
|
throw new Error('No inputs were signed');
|
|
419
488
|
}
|
|
420
489
|
return this;
|
|
421
490
|
}
|
|
491
|
+
|
|
422
492
|
signAllInputsHDAsync(
|
|
423
|
-
hdKeyPair,
|
|
424
|
-
sighashTypes = [
|
|
425
|
-
) {
|
|
426
|
-
return new Promise((resolve, reject) => {
|
|
493
|
+
hdKeyPair: HDSigner | HDSignerAsync,
|
|
494
|
+
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
495
|
+
): Promise<void> {
|
|
496
|
+
return new Promise((resolve, reject): any => {
|
|
427
497
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
428
498
|
return reject(new Error('Need HDSigner to sign input'));
|
|
429
499
|
}
|
|
430
|
-
|
|
431
|
-
const
|
|
500
|
+
|
|
501
|
+
const results: boolean[] = [];
|
|
502
|
+
const promises: Array<Promise<void>> = [];
|
|
432
503
|
for (const i of range(this.data.inputs.length)) {
|
|
433
504
|
promises.push(
|
|
434
505
|
this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
|
|
@@ -442,47 +513,39 @@ class Psbt {
|
|
|
442
513
|
);
|
|
443
514
|
}
|
|
444
515
|
return Promise.all(promises).then(() => {
|
|
445
|
-
if (results.every(v => v === false)) {
|
|
516
|
+
if (results.every((v) => v === false)) {
|
|
446
517
|
return reject(new Error('No inputs were signed'));
|
|
447
518
|
}
|
|
448
519
|
resolve();
|
|
449
520
|
});
|
|
450
521
|
});
|
|
451
522
|
}
|
|
523
|
+
|
|
452
524
|
signInputHD(
|
|
453
|
-
inputIndex,
|
|
454
|
-
hdKeyPair,
|
|
455
|
-
sighashTypes = [
|
|
456
|
-
) {
|
|
525
|
+
inputIndex: number,
|
|
526
|
+
hdKeyPair: HDSigner,
|
|
527
|
+
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
528
|
+
): this {
|
|
457
529
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
458
530
|
throw new Error('Need HDSigner to sign input');
|
|
459
531
|
}
|
|
460
|
-
const signers = getSignersFromHD(
|
|
461
|
-
|
|
462
|
-
this.data.inputs,
|
|
463
|
-
hdKeyPair,
|
|
464
|
-
);
|
|
465
|
-
signers.forEach(signer =>
|
|
466
|
-
this.signInput(inputIndex, signer, sighashTypes),
|
|
467
|
-
);
|
|
532
|
+
const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair) as Signer[];
|
|
533
|
+
signers.forEach((signer) => this.signInput(inputIndex, signer, sighashTypes));
|
|
468
534
|
return this;
|
|
469
535
|
}
|
|
536
|
+
|
|
470
537
|
signInputHDAsync(
|
|
471
|
-
inputIndex,
|
|
472
|
-
hdKeyPair,
|
|
473
|
-
sighashTypes = [
|
|
474
|
-
) {
|
|
475
|
-
return new Promise((resolve, reject) => {
|
|
538
|
+
inputIndex: number,
|
|
539
|
+
hdKeyPair: HDSigner | HDSignerAsync,
|
|
540
|
+
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
541
|
+
): Promise<void> {
|
|
542
|
+
return new Promise((resolve, reject): any => {
|
|
476
543
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
477
544
|
return reject(new Error('Need HDSigner to sign input'));
|
|
478
545
|
}
|
|
479
|
-
const signers = getSignersFromHD(
|
|
480
|
-
|
|
481
|
-
this.
|
|
482
|
-
hdKeyPair,
|
|
483
|
-
);
|
|
484
|
-
const promises = signers.map(signer =>
|
|
485
|
-
this.signInputAsync(inputIndex, signer, sighashTypes),
|
|
546
|
+
const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
|
547
|
+
const promises = signers.map((signer) =>
|
|
548
|
+
this.signInputAsync(inputIndex, signer as unknown as Signer, sighashTypes),
|
|
486
549
|
);
|
|
487
550
|
return Promise.all(promises)
|
|
488
551
|
.then(() => {
|
|
@@ -491,13 +554,17 @@ class Psbt {
|
|
|
491
554
|
.catch(reject);
|
|
492
555
|
});
|
|
493
556
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
557
|
+
|
|
558
|
+
signAllInputs(
|
|
559
|
+
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
560
|
+
sighashTypes?: number[],
|
|
561
|
+
): this {
|
|
562
|
+
if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
|
|
563
|
+
|
|
497
564
|
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
498
565
|
// as input information is added, then eventually
|
|
499
566
|
// optimize this method.
|
|
500
|
-
const results = [];
|
|
567
|
+
const results: boolean[] = [];
|
|
501
568
|
for (const i of range(this.data.inputs.length)) {
|
|
502
569
|
try {
|
|
503
570
|
this.signInput(i, keyPair, sighashTypes);
|
|
@@ -506,20 +573,25 @@ class Psbt {
|
|
|
506
573
|
results.push(false);
|
|
507
574
|
}
|
|
508
575
|
}
|
|
509
|
-
if (results.every(v => v === false)) {
|
|
576
|
+
if (results.every((v) => v === false)) {
|
|
510
577
|
throw new Error('No inputs were signed');
|
|
511
578
|
}
|
|
512
579
|
return this;
|
|
513
580
|
}
|
|
514
|
-
|
|
515
|
-
|
|
581
|
+
|
|
582
|
+
signAllInputsAsync(
|
|
583
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
|
|
584
|
+
sighashTypes?: number[],
|
|
585
|
+
): Promise<void> {
|
|
586
|
+
return new Promise((resolve, reject): any => {
|
|
516
587
|
if (!keyPair || !keyPair.publicKey)
|
|
517
588
|
return reject(new Error('Need Signer to sign input'));
|
|
589
|
+
|
|
518
590
|
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
519
591
|
// as input information is added, then eventually
|
|
520
592
|
// optimize this method.
|
|
521
|
-
const results = [];
|
|
522
|
-
const promises = [];
|
|
593
|
+
const results: boolean[] = [];
|
|
594
|
+
const promises: Array<Promise<void>> = [];
|
|
523
595
|
for (const [i] of this.data.inputs.entries()) {
|
|
524
596
|
promises.push(
|
|
525
597
|
this.signInputAsync(i, keyPair, sighashTypes).then(
|
|
@@ -533,35 +605,43 @@ class Psbt {
|
|
|
533
605
|
);
|
|
534
606
|
}
|
|
535
607
|
return Promise.all(promises).then(() => {
|
|
536
|
-
if (results.every(v => v === false)) {
|
|
608
|
+
if (results.every((v) => v === false)) {
|
|
537
609
|
return reject(new Error('No inputs were signed'));
|
|
538
610
|
}
|
|
539
611
|
resolve();
|
|
540
612
|
});
|
|
541
613
|
});
|
|
542
614
|
}
|
|
543
|
-
|
|
615
|
+
|
|
616
|
+
signInput(
|
|
617
|
+
inputIndex: number,
|
|
618
|
+
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
619
|
+
sighashTypes?: number[],
|
|
620
|
+
): this {
|
|
544
621
|
if (!keyPair || !keyPair.publicKey) {
|
|
545
622
|
throw new Error('Need Signer to sign input');
|
|
546
623
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
input,
|
|
552
|
-
keyPair,
|
|
553
|
-
undefined,
|
|
554
|
-
sighashTypes,
|
|
555
|
-
);
|
|
624
|
+
|
|
625
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
626
|
+
if (isTaprootInput(input)) {
|
|
627
|
+
return this._signTaprootInput(inputIndex, input, keyPair, undefined, sighashTypes);
|
|
556
628
|
}
|
|
629
|
+
|
|
557
630
|
return this._signInput(inputIndex, keyPair, sighashTypes);
|
|
558
631
|
}
|
|
559
|
-
|
|
632
|
+
|
|
633
|
+
signTaprootInput(
|
|
634
|
+
inputIndex: number,
|
|
635
|
+
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
636
|
+
tapLeafHashToSign?: Buffer,
|
|
637
|
+
sighashTypes?: number[],
|
|
638
|
+
): this {
|
|
560
639
|
if (!keyPair || !keyPair.publicKey) {
|
|
561
640
|
throw new Error('Need Signer to sign input');
|
|
562
641
|
}
|
|
563
|
-
|
|
564
|
-
|
|
642
|
+
|
|
643
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
644
|
+
if (isTaprootInput(input)) {
|
|
565
645
|
return this._signTaprootInput(
|
|
566
646
|
inputIndex,
|
|
567
647
|
input,
|
|
@@ -570,17 +650,20 @@ class Psbt {
|
|
|
570
650
|
sighashTypes,
|
|
571
651
|
);
|
|
572
652
|
}
|
|
653
|
+
|
|
573
654
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
574
655
|
}
|
|
575
|
-
|
|
656
|
+
|
|
657
|
+
signInputAsync(
|
|
658
|
+
inputIndex: number,
|
|
659
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
|
|
660
|
+
sighashTypes?: number[],
|
|
661
|
+
): Promise<void> {
|
|
576
662
|
return Promise.resolve().then(() => {
|
|
577
|
-
if (!keyPair || !keyPair.publicKey)
|
|
578
|
-
|
|
579
|
-
const input = (
|
|
580
|
-
|
|
581
|
-
inputIndex,
|
|
582
|
-
);
|
|
583
|
-
if ((0, bip371_1.isTaprootInput)(input))
|
|
663
|
+
if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
|
|
664
|
+
|
|
665
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
666
|
+
if (isTaprootInput(input))
|
|
584
667
|
return this._signTaprootInputAsync(
|
|
585
668
|
inputIndex,
|
|
586
669
|
input,
|
|
@@ -588,18 +671,22 @@ class Psbt {
|
|
|
588
671
|
undefined,
|
|
589
672
|
sighashTypes,
|
|
590
673
|
);
|
|
674
|
+
|
|
591
675
|
return this._signInputAsync(inputIndex, keyPair, sighashTypes);
|
|
592
676
|
});
|
|
593
677
|
}
|
|
594
|
-
|
|
678
|
+
|
|
679
|
+
signTaprootInputAsync(
|
|
680
|
+
inputIndex: number,
|
|
681
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
|
|
682
|
+
tapLeafHash?: Buffer,
|
|
683
|
+
sighashTypes?: number[],
|
|
684
|
+
): Promise<void> {
|
|
595
685
|
return Promise.resolve().then(() => {
|
|
596
|
-
if (!keyPair || !keyPair.publicKey)
|
|
597
|
-
|
|
598
|
-
const input = (
|
|
599
|
-
|
|
600
|
-
inputIndex,
|
|
601
|
-
);
|
|
602
|
-
if ((0, bip371_1.isTaprootInput)(input))
|
|
686
|
+
if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
|
|
687
|
+
|
|
688
|
+
const input = checkForInput(this.data.inputs, inputIndex);
|
|
689
|
+
if (isTaprootInput(input))
|
|
603
690
|
return this._signTaprootInputAsync(
|
|
604
691
|
inputIndex,
|
|
605
692
|
input,
|
|
@@ -607,80 +694,79 @@ class Psbt {
|
|
|
607
694
|
tapLeafHash,
|
|
608
695
|
sighashTypes,
|
|
609
696
|
);
|
|
697
|
+
|
|
610
698
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
611
699
|
});
|
|
612
700
|
}
|
|
613
|
-
|
|
701
|
+
|
|
702
|
+
toBuffer(): Buffer {
|
|
614
703
|
checkCache(this.__CACHE);
|
|
615
704
|
return this.data.toBuffer();
|
|
616
705
|
}
|
|
617
|
-
|
|
706
|
+
|
|
707
|
+
toHex(): string {
|
|
618
708
|
checkCache(this.__CACHE);
|
|
619
709
|
return this.data.toHex();
|
|
620
710
|
}
|
|
621
|
-
|
|
711
|
+
|
|
712
|
+
toBase64(): string {
|
|
622
713
|
checkCache(this.__CACHE);
|
|
623
714
|
return this.data.toBase64();
|
|
624
715
|
}
|
|
625
|
-
|
|
716
|
+
|
|
717
|
+
updateGlobal(updateData: PsbtGlobalUpdate): this {
|
|
626
718
|
this.data.updateGlobal(updateData);
|
|
627
719
|
return this;
|
|
628
720
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
(
|
|
633
|
-
this.data.inputs[inputIndex],
|
|
634
|
-
updateData,
|
|
635
|
-
'updateInput',
|
|
636
|
-
);
|
|
721
|
+
|
|
722
|
+
updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
|
|
723
|
+
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
|
|
724
|
+
checkTaprootInputFields(this.data.inputs[inputIndex], updateData, 'updateInput');
|
|
637
725
|
this.data.updateInput(inputIndex, updateData);
|
|
638
726
|
if (updateData.nonWitnessUtxo) {
|
|
639
|
-
addNonWitnessTxCache(
|
|
640
|
-
this.__CACHE,
|
|
641
|
-
this.data.inputs[inputIndex],
|
|
642
|
-
inputIndex,
|
|
643
|
-
);
|
|
727
|
+
addNonWitnessTxCache(this.__CACHE, this.data.inputs[inputIndex], inputIndex);
|
|
644
728
|
}
|
|
645
729
|
return this;
|
|
646
730
|
}
|
|
647
|
-
|
|
731
|
+
|
|
732
|
+
updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
|
|
648
733
|
const outputData = this.data.outputs[outputIndex];
|
|
649
|
-
(
|
|
650
|
-
|
|
651
|
-
updateData,
|
|
652
|
-
'updateOutput',
|
|
653
|
-
);
|
|
734
|
+
checkTaprootOutputFields(outputData, updateData, 'updateOutput');
|
|
735
|
+
|
|
654
736
|
this.data.updateOutput(outputIndex, updateData);
|
|
655
737
|
return this;
|
|
656
738
|
}
|
|
657
|
-
|
|
739
|
+
|
|
740
|
+
addUnknownKeyValToGlobal(keyVal: KeyValue): this {
|
|
658
741
|
this.data.addUnknownKeyValToGlobal(keyVal);
|
|
659
742
|
return this;
|
|
660
743
|
}
|
|
661
|
-
|
|
744
|
+
|
|
745
|
+
addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
|
|
662
746
|
this.data.addUnknownKeyValToInput(inputIndex, keyVal);
|
|
663
747
|
return this;
|
|
664
748
|
}
|
|
665
|
-
|
|
749
|
+
|
|
750
|
+
addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
|
|
666
751
|
this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
|
|
667
752
|
return this;
|
|
668
753
|
}
|
|
669
|
-
|
|
754
|
+
|
|
755
|
+
clearFinalizedInput(inputIndex: number): this {
|
|
670
756
|
this.data.clearFinalizedInput(inputIndex);
|
|
671
757
|
return this;
|
|
672
758
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
759
|
+
|
|
760
|
+
public checkTaprootHashesForSig(
|
|
761
|
+
inputIndex: number,
|
|
762
|
+
input: PsbtInput,
|
|
763
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
|
|
764
|
+
tapLeafHashToSign?: Buffer,
|
|
765
|
+
allowedSighashTypes?: number[],
|
|
766
|
+
): { hash: Buffer; leafHash?: Buffer }[] {
|
|
680
767
|
if (typeof keyPair.signSchnorr !== 'function')
|
|
681
|
-
throw new Error(
|
|
682
|
-
|
|
683
|
-
);
|
|
768
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
769
|
+
|
|
684
770
|
const hashesForSig = getTaprootHashesForSig(
|
|
685
771
|
inputIndex,
|
|
686
772
|
input,
|
|
@@ -690,23 +776,31 @@ class Psbt {
|
|
|
690
776
|
tapLeafHashToSign,
|
|
691
777
|
allowedSighashTypes,
|
|
692
778
|
);
|
|
779
|
+
|
|
693
780
|
if (!hashesForSig || !hashesForSig.length)
|
|
694
781
|
throw new Error(
|
|
695
782
|
`Can not sign for input #${inputIndex} with the key ${keyPair.publicKey.toString(
|
|
696
783
|
'hex',
|
|
697
784
|
)}`,
|
|
698
785
|
);
|
|
786
|
+
|
|
699
787
|
return hashesForSig;
|
|
700
788
|
}
|
|
701
|
-
|
|
789
|
+
|
|
790
|
+
private _finalizeInput(
|
|
791
|
+
inputIndex: number,
|
|
792
|
+
input: PsbtInput,
|
|
793
|
+
finalScriptsFunc: FinalScriptsFunc = getFinalScripts,
|
|
794
|
+
): this {
|
|
702
795
|
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
|
|
703
796
|
inputIndex,
|
|
704
797
|
input,
|
|
705
798
|
this.__CACHE,
|
|
706
799
|
);
|
|
707
|
-
if (!script)
|
|
708
|
-
|
|
800
|
+
if (!script) throw new Error(`No script found for input #${inputIndex}`);
|
|
801
|
+
|
|
709
802
|
checkPartialSigSighashes(input);
|
|
803
|
+
|
|
710
804
|
const { finalScriptSig, finalScriptWitness } = finalScriptsFunc(
|
|
711
805
|
inputIndex,
|
|
712
806
|
input,
|
|
@@ -715,33 +809,32 @@ class Psbt {
|
|
|
715
809
|
isP2SH,
|
|
716
810
|
isP2WSH,
|
|
717
811
|
);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
if (finalScriptWitness)
|
|
721
|
-
this.data.updateInput(inputIndex, { finalScriptWitness });
|
|
812
|
+
|
|
813
|
+
if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig });
|
|
814
|
+
if (finalScriptWitness) this.data.updateInput(inputIndex, { finalScriptWitness });
|
|
722
815
|
if (!finalScriptSig && !finalScriptWitness)
|
|
723
816
|
throw new Error(`Unknown error finalizing input #${inputIndex}`);
|
|
817
|
+
|
|
724
818
|
this.data.clearFinalizedInput(inputIndex);
|
|
725
819
|
return this;
|
|
726
820
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
821
|
+
|
|
822
|
+
private _finalizeTaprootInput(
|
|
823
|
+
inputIndex: number,
|
|
824
|
+
input: PsbtInput,
|
|
825
|
+
tapLeafHashToFinalize?: Buffer,
|
|
826
|
+
finalScriptsFunc = tapScriptFinalizer,
|
|
827
|
+
): this {
|
|
733
828
|
if (!input.witnessUtxo)
|
|
734
|
-
throw new Error(
|
|
735
|
-
|
|
736
|
-
);
|
|
829
|
+
throw new Error(`Cannot finalize input #${inputIndex}. Missing witness utxo.`);
|
|
830
|
+
|
|
737
831
|
// Check key spend first. Increased privacy and reduced block space.
|
|
738
832
|
if (input.tapKeySig) {
|
|
739
833
|
const payment = payments.p2tr({
|
|
740
834
|
output: input.witnessUtxo.script,
|
|
741
835
|
signature: input.tapKeySig,
|
|
742
836
|
});
|
|
743
|
-
const finalScriptWitness = (
|
|
744
|
-
psbtutils_1.witnessStackToScriptWitness)(payment.witness);
|
|
837
|
+
const finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
|
|
745
838
|
this.data.updateInput(inputIndex, { finalScriptWitness });
|
|
746
839
|
} else {
|
|
747
840
|
const { finalScriptWitness } = finalScriptsFunc(
|
|
@@ -751,28 +844,33 @@ class Psbt {
|
|
|
751
844
|
);
|
|
752
845
|
this.data.updateInput(inputIndex, { finalScriptWitness });
|
|
753
846
|
}
|
|
847
|
+
|
|
754
848
|
this.data.clearFinalizedInput(inputIndex);
|
|
849
|
+
|
|
755
850
|
return this;
|
|
756
851
|
}
|
|
757
|
-
|
|
852
|
+
|
|
853
|
+
private _validateSignaturesOfInput(
|
|
854
|
+
inputIndex: number,
|
|
855
|
+
validator: ValidateSigFunction,
|
|
856
|
+
pubkey?: Buffer,
|
|
857
|
+
): boolean {
|
|
758
858
|
const input = this.data.inputs[inputIndex];
|
|
759
859
|
const partialSig = (input || {}).partialSig;
|
|
760
860
|
if (!input || !partialSig || partialSig.length < 1)
|
|
761
861
|
throw new Error('No signatures to validate');
|
|
762
862
|
if (typeof validator !== 'function')
|
|
763
863
|
throw new Error('Need validator function to validate signatures');
|
|
764
|
-
const mySigs = pubkey
|
|
765
|
-
? partialSig.filter(sig => sig.pubkey.equals(pubkey))
|
|
766
|
-
: partialSig;
|
|
864
|
+
const mySigs = pubkey ? partialSig.filter((sig) => sig.pubkey.equals(pubkey)) : partialSig;
|
|
767
865
|
if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
|
|
768
|
-
const results = [];
|
|
769
|
-
let hashCache;
|
|
770
|
-
let scriptCache;
|
|
771
|
-
let sighashCache;
|
|
866
|
+
const results: boolean[] = [];
|
|
867
|
+
let hashCache: Buffer;
|
|
868
|
+
let scriptCache: Buffer;
|
|
869
|
+
let sighashCache: number;
|
|
772
870
|
for (const pSig of mySigs) {
|
|
773
871
|
const sig = bscript.signature.decode(pSig.signature);
|
|
774
872
|
const { hash, script } =
|
|
775
|
-
sighashCache !== sig.hashType
|
|
873
|
+
sighashCache! !== sig.hashType
|
|
776
874
|
? getHashForSig(
|
|
777
875
|
inputIndex,
|
|
778
876
|
Object.assign({}, input, {
|
|
@@ -781,16 +879,21 @@ class Psbt {
|
|
|
781
879
|
this.__CACHE,
|
|
782
880
|
true,
|
|
783
881
|
)
|
|
784
|
-
: { hash: hashCache
|
|
882
|
+
: { hash: hashCache!, script: scriptCache! };
|
|
785
883
|
sighashCache = sig.hashType;
|
|
786
884
|
hashCache = hash;
|
|
787
885
|
scriptCache = script;
|
|
788
886
|
checkScriptForPubkey(pSig.pubkey, script, 'verify');
|
|
789
887
|
results.push(validator(pSig.pubkey, hash, sig.signature));
|
|
790
888
|
}
|
|
791
|
-
return results.every(res => res === true);
|
|
792
|
-
}
|
|
793
|
-
|
|
889
|
+
return results.every((res) => res === true);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
private validateSignaturesOfTaprootInput(
|
|
893
|
+
inputIndex: number,
|
|
894
|
+
validator: ValidateSigFunction,
|
|
895
|
+
pubkey?: Buffer,
|
|
896
|
+
): boolean {
|
|
794
897
|
const input = this.data.inputs[inputIndex];
|
|
795
898
|
const tapKeySig = (input || {}).tapKeySig;
|
|
796
899
|
const tapScriptSig = (input || {}).tapScriptSig;
|
|
@@ -798,24 +901,15 @@ class Psbt {
|
|
|
798
901
|
throw new Error('No signatures to validate');
|
|
799
902
|
if (typeof validator !== 'function')
|
|
800
903
|
throw new Error('Need validator function to validate signatures');
|
|
801
|
-
|
|
904
|
+
|
|
905
|
+
pubkey = pubkey && toXOnly(pubkey);
|
|
802
906
|
const allHashses = pubkey
|
|
803
|
-
? getTaprootHashesForSig(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
)
|
|
810
|
-
: getAllTaprootHashesForSig(
|
|
811
|
-
inputIndex,
|
|
812
|
-
input,
|
|
813
|
-
this.data.inputs,
|
|
814
|
-
this.__CACHE,
|
|
815
|
-
);
|
|
816
|
-
if (!allHashses.length)
|
|
817
|
-
throw new Error('No signatures for this pubkey');
|
|
818
|
-
const tapKeyHash = allHashses.find(h => !h.leafHash);
|
|
907
|
+
? getTaprootHashesForSig(inputIndex, input, this.data.inputs, pubkey, this.__CACHE)
|
|
908
|
+
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this.__CACHE);
|
|
909
|
+
|
|
910
|
+
if (!allHashses.length) throw new Error('No signatures for this pubkey');
|
|
911
|
+
|
|
912
|
+
const tapKeyHash = allHashses.find((h) => !h.leafHash);
|
|
819
913
|
let validationResultCount = 0;
|
|
820
914
|
if (tapKeySig && tapKeyHash) {
|
|
821
915
|
const isValidTapkeySig = validator(
|
|
@@ -826,11 +920,10 @@ class Psbt {
|
|
|
826
920
|
if (!isValidTapkeySig) return false;
|
|
827
921
|
validationResultCount++;
|
|
828
922
|
}
|
|
923
|
+
|
|
829
924
|
if (tapScriptSig) {
|
|
830
925
|
for (const tapSig of tapScriptSig) {
|
|
831
|
-
const tapSigHash = allHashses.find(h =>
|
|
832
|
-
tapSig.pubkey.equals(h.pubkey),
|
|
833
|
-
);
|
|
926
|
+
const tapSigHash = allHashses.find((h) => tapSig.pubkey.equals(h.pubkey));
|
|
834
927
|
if (tapSigHash) {
|
|
835
928
|
const isValidTapScriptSig = validator(
|
|
836
929
|
tapSig.pubkey,
|
|
@@ -842,14 +935,17 @@ class Psbt {
|
|
|
842
935
|
}
|
|
843
936
|
}
|
|
844
937
|
}
|
|
938
|
+
|
|
845
939
|
return validationResultCount > 0;
|
|
846
940
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
941
|
+
|
|
942
|
+
private _signInput(
|
|
943
|
+
inputIndex: number,
|
|
944
|
+
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
945
|
+
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
946
|
+
): this {
|
|
947
|
+
hookSigner(keyPair);
|
|
948
|
+
|
|
853
949
|
const { hash, sighashType } = getHashAndSighashType(
|
|
854
950
|
this.data.inputs,
|
|
855
951
|
inputIndex,
|
|
@@ -857,26 +953,27 @@ class Psbt {
|
|
|
857
953
|
this.__CACHE,
|
|
858
954
|
sighashTypes,
|
|
859
955
|
);
|
|
956
|
+
|
|
860
957
|
const partialSig = [
|
|
861
958
|
{
|
|
862
959
|
pubkey: keyPair.publicKey,
|
|
863
|
-
signature: bscript.signature.encode(
|
|
864
|
-
keyPair.sign(hash),
|
|
865
|
-
sighashType,
|
|
866
|
-
),
|
|
960
|
+
signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
|
|
867
961
|
},
|
|
868
962
|
];
|
|
963
|
+
|
|
869
964
|
this.data.updateInput(inputIndex, { partialSig });
|
|
870
965
|
return this;
|
|
871
966
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
967
|
+
|
|
968
|
+
private _signTaprootInput(
|
|
969
|
+
inputIndex: number,
|
|
970
|
+
input: PsbtInput,
|
|
971
|
+
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
972
|
+
tapLeafHashToSign?: Buffer,
|
|
973
|
+
allowedSighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
|
|
974
|
+
): this {
|
|
975
|
+
hookSigner(keyPair);
|
|
976
|
+
|
|
880
977
|
const hashesForSig = this.checkTaprootHashesForSig(
|
|
881
978
|
inputIndex,
|
|
882
979
|
input,
|
|
@@ -884,38 +981,45 @@ class Psbt {
|
|
|
884
981
|
tapLeafHashToSign,
|
|
885
982
|
allowedSighashTypes,
|
|
886
983
|
);
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
.
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
input.sighashType,
|
|
893
|
-
),
|
|
984
|
+
|
|
985
|
+
const tapKeySig: TapKeySig = hashesForSig
|
|
986
|
+
.filter((h) => !h.leafHash)
|
|
987
|
+
.map((h) =>
|
|
988
|
+
serializeTaprootSignature(keyPair.signSchnorr!(h.hash), input.sighashType),
|
|
894
989
|
)[0];
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
.
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
990
|
+
|
|
991
|
+
const tapScriptSig: TapScriptSig[] = hashesForSig
|
|
992
|
+
.filter((h) => !!h.leafHash)
|
|
993
|
+
.map(
|
|
994
|
+
(h) =>
|
|
995
|
+
({
|
|
996
|
+
pubkey: toXOnly(keyPair.publicKey),
|
|
997
|
+
signature: serializeTaprootSignature(
|
|
998
|
+
keyPair.signSchnorr!(h.hash),
|
|
999
|
+
input.sighashType,
|
|
1000
|
+
),
|
|
1001
|
+
leafHash: h.leafHash,
|
|
1002
|
+
}) as TapScriptSig,
|
|
1003
|
+
);
|
|
1004
|
+
|
|
905
1005
|
if (tapKeySig) {
|
|
906
1006
|
this.data.updateInput(inputIndex, { tapKeySig });
|
|
907
1007
|
}
|
|
1008
|
+
|
|
908
1009
|
if (tapScriptSig.length) {
|
|
909
1010
|
this.data.updateInput(inputIndex, { tapScriptSig });
|
|
910
1011
|
}
|
|
1012
|
+
|
|
911
1013
|
return this;
|
|
912
1014
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1015
|
+
|
|
1016
|
+
private _signInputAsync(
|
|
1017
|
+
inputIndex: number,
|
|
1018
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
|
|
1019
|
+
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
1020
|
+
): Promise<void> {
|
|
1021
|
+
hookSigner(keyPair);
|
|
1022
|
+
|
|
919
1023
|
const { hash, sighashType } = getHashAndSighashType(
|
|
920
1024
|
this.data.inputs,
|
|
921
1025
|
inputIndex,
|
|
@@ -923,24 +1027,28 @@ class Psbt {
|
|
|
923
1027
|
this.__CACHE,
|
|
924
1028
|
sighashTypes,
|
|
925
1029
|
);
|
|
926
|
-
|
|
1030
|
+
|
|
1031
|
+
return Promise.resolve(keyPair.sign(hash)).then((signature) => {
|
|
927
1032
|
const partialSig = [
|
|
928
1033
|
{
|
|
929
1034
|
pubkey: keyPair.publicKey,
|
|
930
1035
|
signature: bscript.signature.encode(signature, sighashType),
|
|
931
1036
|
},
|
|
932
1037
|
];
|
|
1038
|
+
|
|
933
1039
|
this.data.updateInput(inputIndex, { partialSig });
|
|
934
1040
|
});
|
|
935
1041
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1042
|
+
|
|
1043
|
+
private async _signTaprootInputAsync(
|
|
1044
|
+
inputIndex: number,
|
|
1045
|
+
input: PsbtInput,
|
|
1046
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
|
|
1047
|
+
tapLeafHash?: Buffer,
|
|
1048
|
+
sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
|
|
1049
|
+
): Promise<void> {
|
|
1050
|
+
hookSigner(keyPair);
|
|
1051
|
+
|
|
944
1052
|
const hashesForSig = this.checkTaprootHashesForSig(
|
|
945
1053
|
inputIndex,
|
|
946
1054
|
input,
|
|
@@ -948,73 +1056,187 @@ class Psbt {
|
|
|
948
1056
|
tapLeafHash,
|
|
949
1057
|
sighashTypes,
|
|
950
1058
|
);
|
|
951
|
-
|
|
952
|
-
const
|
|
1059
|
+
|
|
1060
|
+
const signaturePromises: Promise<
|
|
1061
|
+
{ tapKeySig: Buffer } | { tapScriptSig: TapScriptSig[] }
|
|
1062
|
+
>[] = [];
|
|
1063
|
+
|
|
1064
|
+
const tapKeyHash = hashesForSig.filter((h) => !h.leafHash)[0];
|
|
953
1065
|
if (tapKeyHash) {
|
|
954
|
-
const tapKeySigPromise = Promise.resolve(
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
),
|
|
962
|
-
};
|
|
963
|
-
});
|
|
1066
|
+
const tapKeySigPromise = Promise.resolve(keyPair.signSchnorr!(tapKeyHash.hash)).then(
|
|
1067
|
+
(sig) => {
|
|
1068
|
+
return {
|
|
1069
|
+
tapKeySig: serializeTaprootSignature(sig, input.sighashType),
|
|
1070
|
+
};
|
|
1071
|
+
},
|
|
1072
|
+
);
|
|
964
1073
|
signaturePromises.push(tapKeySigPromise);
|
|
965
1074
|
}
|
|
966
|
-
|
|
1075
|
+
|
|
1076
|
+
const tapScriptHashes = hashesForSig.filter((h) => !!h.leafHash);
|
|
967
1077
|
if (tapScriptHashes.length) {
|
|
968
|
-
const tapScriptSigPromises = tapScriptHashes.map(async tsh => {
|
|
969
|
-
const signature = await keyPair.signSchnorr(tsh.hash);
|
|
1078
|
+
const tapScriptSigPromises = tapScriptHashes.map(async (tsh) => {
|
|
1079
|
+
const signature = await keyPair.signSchnorr!(tsh.hash);
|
|
1080
|
+
|
|
970
1081
|
const tapScriptSig = [
|
|
971
1082
|
{
|
|
972
|
-
pubkey:
|
|
973
|
-
signature: (
|
|
974
|
-
signature,
|
|
975
|
-
input.sighashType,
|
|
976
|
-
),
|
|
1083
|
+
pubkey: toXOnly(keyPair.publicKey),
|
|
1084
|
+
signature: serializeTaprootSignature(signature, input.sighashType),
|
|
977
1085
|
leafHash: tsh.leafHash,
|
|
978
|
-
},
|
|
1086
|
+
} as TapScriptSig,
|
|
979
1087
|
];
|
|
1088
|
+
|
|
980
1089
|
return { tapScriptSig };
|
|
981
1090
|
});
|
|
982
1091
|
signaturePromises.push(...tapScriptSigPromises);
|
|
983
1092
|
}
|
|
1093
|
+
|
|
984
1094
|
const results = await Promise.all(signaturePromises);
|
|
985
1095
|
for (const v of results) {
|
|
986
1096
|
this.data.updateInput(inputIndex, v);
|
|
987
1097
|
}
|
|
988
1098
|
}
|
|
989
1099
|
}
|
|
990
|
-
|
|
1100
|
+
|
|
1101
|
+
interface PsbtCache {
|
|
1102
|
+
__NON_WITNESS_UTXO_TX_CACHE: Transaction[];
|
|
1103
|
+
__NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
|
|
1104
|
+
__TX_IN_CACHE: { [index: string]: number };
|
|
1105
|
+
__TX: Transaction;
|
|
1106
|
+
__FEE_RATE?: number;
|
|
1107
|
+
__FEE?: number;
|
|
1108
|
+
__EXTRACTED_TX?: Transaction;
|
|
1109
|
+
__UNSAFE_SIGN_NONSEGWIT: boolean;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
export interface PsbtOptsOptional {
|
|
1113
|
+
network?: Network;
|
|
1114
|
+
maximumFeeRate?: number;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
export interface PsbtOpts {
|
|
1118
|
+
network: Network;
|
|
1119
|
+
maximumFeeRate: number;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
export interface PsbtInputExtended extends PsbtInput, TransactionInput {}
|
|
1123
|
+
|
|
1124
|
+
export type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript;
|
|
1125
|
+
|
|
1126
|
+
export interface PsbtOutputExtendedAddress extends PsbtOutput {
|
|
1127
|
+
address: string;
|
|
1128
|
+
value: number;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
export interface PsbtOutputExtendedScript extends PsbtOutput {
|
|
1132
|
+
script: Buffer;
|
|
1133
|
+
value: number;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
interface HDSignerBase {
|
|
1137
|
+
/**
|
|
1138
|
+
* DER format compressed publicKey buffer
|
|
1139
|
+
*/
|
|
1140
|
+
publicKey: Buffer;
|
|
1141
|
+
/**
|
|
1142
|
+
* The first 4 bytes of the sha256-ripemd160 of the publicKey
|
|
1143
|
+
*/
|
|
1144
|
+
fingerprint: Buffer;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
export interface HDSigner extends HDSignerBase {
|
|
1148
|
+
/**
|
|
1149
|
+
* The path string must match /^m(\/\d+'?)+$/
|
|
1150
|
+
* ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
|
|
1151
|
+
*/
|
|
1152
|
+
derivePath(path: string): HDSigner;
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* Input hash (the "message digest") for the signature algorithm
|
|
1156
|
+
* Return a 64 byte signature (32 byte r and 32 byte s in that order)
|
|
1157
|
+
*/
|
|
1158
|
+
sign(hash: Buffer): Buffer;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Same as above but with async sign method
|
|
1163
|
+
*/
|
|
1164
|
+
export interface HDSignerAsync extends HDSignerBase {
|
|
1165
|
+
derivePath(path: string): HDSignerAsync;
|
|
1166
|
+
|
|
1167
|
+
sign(hash: Buffer): Promise<Buffer>;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
export interface SignerAlternative {
|
|
1171
|
+
publicKey: Buffer;
|
|
1172
|
+
lowR: boolean;
|
|
1173
|
+
|
|
1174
|
+
sign(hash: Buffer, lowR?: boolean): Buffer;
|
|
1175
|
+
|
|
1176
|
+
verify(hash: Buffer, signature: Buffer): boolean;
|
|
1177
|
+
|
|
1178
|
+
signSchnorr(hash: Buffer): Buffer;
|
|
1179
|
+
|
|
1180
|
+
verifySchnorr(hash: Buffer, signature: Buffer): boolean;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
export interface Signer {
|
|
1184
|
+
publicKey: Buffer;
|
|
1185
|
+
network?: Network;
|
|
1186
|
+
|
|
1187
|
+
sign(hash: Buffer, lowR?: boolean): Buffer;
|
|
1188
|
+
|
|
1189
|
+
signSchnorr?(hash: Buffer): Buffer;
|
|
1190
|
+
|
|
1191
|
+
getPublicKey?(): Buffer;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
export interface SignerAsync {
|
|
1195
|
+
publicKey: Buffer;
|
|
1196
|
+
network?: Network;
|
|
1197
|
+
|
|
1198
|
+
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
|
|
1199
|
+
|
|
1200
|
+
signSchnorr?(hash: Buffer): Promise<Buffer>;
|
|
1201
|
+
|
|
1202
|
+
getPublicKey?(): Buffer;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
991
1205
|
/**
|
|
992
1206
|
* This function is needed to pass to the bip174 base class's fromBuffer.
|
|
993
1207
|
* It takes the "transaction buffer" portion of the psbt buffer and returns a
|
|
994
1208
|
* Transaction (From the bip174 library) interface.
|
|
995
1209
|
*/
|
|
996
|
-
const transactionFromBuffer = buffer
|
|
1210
|
+
const transactionFromBuffer: TransactionFromBuffer = (buffer: Buffer): ITransaction =>
|
|
1211
|
+
new PsbtTransaction(buffer);
|
|
1212
|
+
|
|
997
1213
|
/**
|
|
998
1214
|
* This class implements the Transaction interface from bip174 library.
|
|
999
1215
|
* It contains a bitcoinjs-lib Transaction object.
|
|
1000
1216
|
*/
|
|
1001
|
-
class PsbtTransaction {
|
|
1002
|
-
tx;
|
|
1003
|
-
|
|
1004
|
-
|
|
1217
|
+
class PsbtTransaction implements ITransaction {
|
|
1218
|
+
tx: Transaction;
|
|
1219
|
+
|
|
1220
|
+
constructor(buffer: Buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
|
|
1221
|
+
this.tx = Transaction.fromBuffer(buffer);
|
|
1005
1222
|
checkTxEmpty(this.tx);
|
|
1006
1223
|
Object.defineProperty(this, 'tx', {
|
|
1007
1224
|
enumerable: false,
|
|
1008
1225
|
writable: true,
|
|
1009
1226
|
});
|
|
1010
1227
|
}
|
|
1011
|
-
|
|
1228
|
+
|
|
1229
|
+
getInputOutputCounts(): {
|
|
1230
|
+
inputCount: number;
|
|
1231
|
+
outputCount: number;
|
|
1232
|
+
} {
|
|
1012
1233
|
return {
|
|
1013
1234
|
inputCount: this.tx.ins.length,
|
|
1014
1235
|
outputCount: this.tx.outs.length,
|
|
1015
1236
|
};
|
|
1016
1237
|
}
|
|
1017
|
-
|
|
1238
|
+
|
|
1239
|
+
addInput(input: TransactionInput): void {
|
|
1018
1240
|
if (
|
|
1019
1241
|
input.hash === undefined ||
|
|
1020
1242
|
input.index === undefined ||
|
|
@@ -1025,13 +1247,13 @@ class PsbtTransaction {
|
|
|
1025
1247
|
}
|
|
1026
1248
|
const hash =
|
|
1027
1249
|
typeof input.hash === 'string'
|
|
1028
|
-
? (
|
|
1029
|
-
Buffer.from(input.hash, 'hex'),
|
|
1030
|
-
)
|
|
1250
|
+
? reverseBuffer(Buffer.from(input.hash, 'hex'))
|
|
1031
1251
|
: input.hash;
|
|
1252
|
+
|
|
1032
1253
|
this.tx.addInput(hash, input.index, input.sequence);
|
|
1033
1254
|
}
|
|
1034
|
-
|
|
1255
|
+
|
|
1256
|
+
addOutput(output: TransactionOutput): void {
|
|
1035
1257
|
if (
|
|
1036
1258
|
output.script === undefined ||
|
|
1037
1259
|
output.value === undefined ||
|
|
@@ -1042,11 +1264,13 @@ class PsbtTransaction {
|
|
|
1042
1264
|
}
|
|
1043
1265
|
this.tx.addOutput(output.script, output.value);
|
|
1044
1266
|
}
|
|
1045
|
-
|
|
1267
|
+
|
|
1268
|
+
toBuffer(): Buffer {
|
|
1046
1269
|
return this.tx.toBuffer();
|
|
1047
1270
|
}
|
|
1048
1271
|
}
|
|
1049
|
-
|
|
1272
|
+
|
|
1273
|
+
function canFinalize(input: PsbtInput, script: Buffer, scriptType: string): boolean {
|
|
1050
1274
|
switch (scriptType) {
|
|
1051
1275
|
case 'pubkey':
|
|
1052
1276
|
case 'pubkeyhash':
|
|
@@ -1054,61 +1278,60 @@ function canFinalize(input, script, scriptType) {
|
|
|
1054
1278
|
return hasSigs(1, input.partialSig);
|
|
1055
1279
|
case 'multisig':
|
|
1056
1280
|
const p2ms = payments.p2ms({ output: script });
|
|
1057
|
-
return hasSigs(p2ms.m
|
|
1281
|
+
return hasSigs(p2ms.m!, input.partialSig, p2ms.pubkeys);
|
|
1058
1282
|
default:
|
|
1059
1283
|
return false;
|
|
1060
1284
|
}
|
|
1061
1285
|
}
|
|
1062
|
-
|
|
1286
|
+
|
|
1287
|
+
function checkCache(cache: PsbtCache): void {
|
|
1063
1288
|
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
|
|
1064
1289
|
throw new Error('Not BIP174 compliant, can not export');
|
|
1065
1290
|
}
|
|
1066
1291
|
}
|
|
1067
|
-
|
|
1292
|
+
|
|
1293
|
+
function hasSigs(neededSigs: number, partialSig?: any[], pubkeys?: Buffer[]): boolean {
|
|
1068
1294
|
if (!partialSig) return false;
|
|
1069
|
-
let sigs;
|
|
1295
|
+
let sigs: any;
|
|
1070
1296
|
if (pubkeys) {
|
|
1071
1297
|
sigs = pubkeys
|
|
1072
|
-
.map(pkey => {
|
|
1298
|
+
.map((pkey) => {
|
|
1073
1299
|
const pubkey = compressPubkey(pkey);
|
|
1074
|
-
return partialSig.find(pSig => pSig.pubkey.equals(pubkey));
|
|
1300
|
+
return partialSig.find((pSig) => pSig.pubkey.equals(pubkey));
|
|
1075
1301
|
})
|
|
1076
|
-
.filter(v => !!v);
|
|
1302
|
+
.filter((v) => !!v);
|
|
1077
1303
|
} else {
|
|
1078
1304
|
sigs = partialSig;
|
|
1079
1305
|
}
|
|
1080
1306
|
if (sigs.length > neededSigs) throw new Error('Too many signatures');
|
|
1081
1307
|
return sigs.length === neededSigs;
|
|
1082
1308
|
}
|
|
1083
|
-
|
|
1309
|
+
|
|
1310
|
+
function isFinalized(input: PsbtInput): boolean {
|
|
1084
1311
|
return !!input.finalScriptSig || !!input.finalScriptWitness;
|
|
1085
1312
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1313
|
+
|
|
1314
|
+
function bip32DerivationIsMine(root: HDSigner): (d: Bip32Derivation) => boolean {
|
|
1315
|
+
return (d: Bip32Derivation): boolean => {
|
|
1088
1316
|
if (!d.masterFingerprint.equals(root.fingerprint)) return false;
|
|
1089
1317
|
if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false;
|
|
1090
1318
|
return true;
|
|
1091
1319
|
};
|
|
1092
1320
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
num !== Math.floor(num) ||
|
|
1097
|
-
num > 0xffffffff ||
|
|
1098
|
-
num < 0
|
|
1099
|
-
) {
|
|
1321
|
+
|
|
1322
|
+
function check32Bit(num: number): void {
|
|
1323
|
+
if (typeof num !== 'number' || num !== Math.floor(num) || num > 0xffffffff || num < 0) {
|
|
1100
1324
|
throw new Error('Invalid 32 bit integer');
|
|
1101
1325
|
}
|
|
1102
1326
|
}
|
|
1103
|
-
|
|
1327
|
+
|
|
1328
|
+
function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
|
|
1104
1329
|
const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
|
|
1105
|
-
const vsize = cache.__EXTRACTED_TX
|
|
1330
|
+
const vsize = cache.__EXTRACTED_TX!.virtualSize();
|
|
1106
1331
|
const satoshis = feeRate * vsize;
|
|
1107
1332
|
if (feeRate >= opts.maximumFeeRate) {
|
|
1108
1333
|
throw new Error(
|
|
1109
|
-
`Warning: You are paying around ${(satoshis / 1e8).toFixed(
|
|
1110
|
-
8,
|
|
1111
|
-
)} in ` +
|
|
1334
|
+
`Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
|
|
1112
1335
|
`fees, which is ${feeRate} satoshi per byte for a transaction ` +
|
|
1113
1336
|
`with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
|
|
1114
1337
|
`byte). Use setMaximumFeeRate method to raise your threshold, or ` +
|
|
@@ -1116,39 +1339,36 @@ function checkFees(psbt, cache, opts) {
|
|
|
1116
1339
|
);
|
|
1117
1340
|
}
|
|
1118
1341
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1342
|
+
|
|
1343
|
+
function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
|
|
1344
|
+
inputs.forEach((input) => {
|
|
1345
|
+
const throws = isTaprootInput(input)
|
|
1346
|
+
? checkTaprootInputForSigs(input, action)
|
|
1347
|
+
: checkInputForSig(input, action);
|
|
1348
|
+
if (throws) throw new Error('Can not modify transaction, signatures exist.');
|
|
1126
1349
|
});
|
|
1127
1350
|
}
|
|
1128
|
-
|
|
1351
|
+
|
|
1352
|
+
function checkPartialSigSighashes(input: PsbtInput): void {
|
|
1129
1353
|
if (!input.sighashType || !input.partialSig) return;
|
|
1130
1354
|
const { partialSig, sighashType } = input;
|
|
1131
|
-
partialSig.forEach(pSig => {
|
|
1355
|
+
partialSig.forEach((pSig) => {
|
|
1132
1356
|
const { hashType } = bscript.signature.decode(pSig.signature);
|
|
1133
1357
|
if (sighashType !== hashType) {
|
|
1134
|
-
throw new Error(
|
|
1135
|
-
'Signature sighash does not match input sighash type',
|
|
1136
|
-
);
|
|
1358
|
+
throw new Error('Signature sighash does not match input sighash type');
|
|
1137
1359
|
}
|
|
1138
1360
|
});
|
|
1139
1361
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
'hex',
|
|
1145
|
-
)}`,
|
|
1146
|
-
);
|
|
1362
|
+
|
|
1363
|
+
function checkScriptForPubkey(pubkey: Buffer, script: Buffer, action: string): void {
|
|
1364
|
+
if (!pubkeyInScript(pubkey, script)) {
|
|
1365
|
+
throw new Error(`Can not ${action} for this input with the key ${pubkey.toString('hex')}`);
|
|
1147
1366
|
}
|
|
1148
1367
|
}
|
|
1149
|
-
|
|
1368
|
+
|
|
1369
|
+
function checkTxEmpty(tx: Transaction): void {
|
|
1150
1370
|
const isEmpty = tx.ins.every(
|
|
1151
|
-
input =>
|
|
1371
|
+
(input) =>
|
|
1152
1372
|
input.script &&
|
|
1153
1373
|
input.script.length === 0 &&
|
|
1154
1374
|
input.witness &&
|
|
@@ -1158,26 +1378,33 @@ function checkTxEmpty(tx) {
|
|
|
1158
1378
|
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
|
1159
1379
|
}
|
|
1160
1380
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1381
|
+
|
|
1382
|
+
function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void {
|
|
1383
|
+
tx.ins.forEach((input) => {
|
|
1163
1384
|
checkTxInputCache(cache, input);
|
|
1164
1385
|
});
|
|
1165
1386
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
'hex',
|
|
1170
|
-
) +
|
|
1171
|
-
':' +
|
|
1172
|
-
input.index;
|
|
1387
|
+
|
|
1388
|
+
function checkTxInputCache(cache: PsbtCache, input: { hash: Buffer; index: number }): void {
|
|
1389
|
+
const key = reverseBuffer(Buffer.from(input.hash)).toString('hex') + ':' + input.index;
|
|
1173
1390
|
if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
|
|
1174
1391
|
cache.__TX_IN_CACHE[key] = 1;
|
|
1175
1392
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1393
|
+
|
|
1394
|
+
function scriptCheckerFactory(
|
|
1395
|
+
payment: (a: Payment, opts?: PaymentOpts) => Payment,
|
|
1396
|
+
paymentScriptName: string,
|
|
1397
|
+
): (idx: number, scriptPubKey: Buffer, redeemScript: Buffer, ioType: 'input' | 'output') => void {
|
|
1398
|
+
return (
|
|
1399
|
+
inputIndex: number,
|
|
1400
|
+
scriptPubKey: Buffer,
|
|
1401
|
+
redeemScript: Buffer,
|
|
1402
|
+
ioType: 'input' | 'output',
|
|
1403
|
+
): void => {
|
|
1178
1404
|
const redeemScriptOutput = payment({
|
|
1179
1405
|
redeem: { output: redeemScript },
|
|
1180
|
-
}).output;
|
|
1406
|
+
}).output as Buffer;
|
|
1407
|
+
|
|
1181
1408
|
if (!scriptPubKey.equals(redeemScriptOutput)) {
|
|
1182
1409
|
throw new Error(
|
|
1183
1410
|
`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
|
|
@@ -1185,17 +1412,23 @@ function scriptCheckerFactory(payment, paymentScriptName) {
|
|
|
1185
1412
|
}
|
|
1186
1413
|
};
|
|
1187
1414
|
}
|
|
1415
|
+
|
|
1188
1416
|
const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
|
|
1189
|
-
const checkWitnessScript = scriptCheckerFactory(
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
function getTxCacheValue(
|
|
1194
|
-
|
|
1195
|
-
|
|
1417
|
+
const checkWitnessScript = scriptCheckerFactory(payments.p2wsh, 'Witness script');
|
|
1418
|
+
|
|
1419
|
+
type TxCacheNumberKey = '__FEE_RATE' | '__FEE';
|
|
1420
|
+
|
|
1421
|
+
function getTxCacheValue(
|
|
1422
|
+
key: TxCacheNumberKey,
|
|
1423
|
+
name: string,
|
|
1424
|
+
inputs: PsbtInput[],
|
|
1425
|
+
c: PsbtCache,
|
|
1426
|
+
disableOutputChecks: boolean = false,
|
|
1427
|
+
): number | undefined {
|
|
1428
|
+
if (!inputs.every(isFinalized)) throw new Error(`PSBT must be finalized to calculate ${name}`);
|
|
1196
1429
|
if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE;
|
|
1197
1430
|
if (key === '__FEE' && c.__FEE) return c.__FEE;
|
|
1198
|
-
let tx;
|
|
1431
|
+
let tx: Transaction;
|
|
1199
1432
|
let mustFinalize = true;
|
|
1200
1433
|
if (c.__EXTRACTED_TX) {
|
|
1201
1434
|
tx = c.__EXTRACTED_TX;
|
|
@@ -1204,46 +1437,76 @@ function getTxCacheValue(key, name, inputs, c, disableOutputChecks = false) {
|
|
|
1204
1437
|
tx = c.__TX.clone();
|
|
1205
1438
|
}
|
|
1206
1439
|
inputFinalizeGetAmts(inputs, tx, c, mustFinalize, disableOutputChecks);
|
|
1207
|
-
if (key === '__FEE_RATE') return c.__FEE_RATE
|
|
1208
|
-
else if (key === '__FEE') return c.__FEE
|
|
1440
|
+
if (key === '__FEE_RATE') return c.__FEE_RATE!;
|
|
1441
|
+
else if (key === '__FEE') return c.__FEE!;
|
|
1209
1442
|
}
|
|
1210
|
-
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* This function must do two things:
|
|
1446
|
+
* 1. Check if the `input` can be finalized. If it can not be finalized, throw.
|
|
1447
|
+
* ie. `Can not finalize input #${inputIndex}`
|
|
1448
|
+
* 2. Create the finalScriptSig and finalScriptWitness Buffers.
|
|
1449
|
+
*/
|
|
1450
|
+
type FinalScriptsFunc = (
|
|
1451
|
+
inputIndex: number, // Which input is it?
|
|
1452
|
+
input: PsbtInput, // The PSBT input contents
|
|
1453
|
+
script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
|
|
1454
|
+
isSegwit: boolean, // Is it segwit?
|
|
1455
|
+
isP2SH: boolean, // Is it P2SH?
|
|
1456
|
+
isP2WSH: boolean, // Is it P2WSH?
|
|
1457
|
+
) => {
|
|
1458
|
+
finalScriptSig: Buffer | undefined;
|
|
1459
|
+
finalScriptWitness: Buffer | undefined;
|
|
1460
|
+
};
|
|
1461
|
+
type FinalTaprootScriptsFunc = (
|
|
1462
|
+
inputIndex: number, // Which input is it?
|
|
1463
|
+
input: PsbtInput, // The PSBT input contents
|
|
1464
|
+
tapLeafHashToFinalize?: Buffer, // Only finalize this specific leaf
|
|
1465
|
+
) => {
|
|
1466
|
+
finalScriptWitness: Buffer | undefined;
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
export function getFinalScripts(
|
|
1470
|
+
inputIndex: number,
|
|
1471
|
+
input: PsbtInput,
|
|
1472
|
+
script: Buffer,
|
|
1473
|
+
isSegwit: boolean,
|
|
1474
|
+
isP2SH: boolean,
|
|
1475
|
+
isP2WSH: boolean,
|
|
1476
|
+
): {
|
|
1477
|
+
finalScriptSig: Buffer | undefined;
|
|
1478
|
+
finalScriptWitness: Buffer | undefined;
|
|
1479
|
+
} {
|
|
1211
1480
|
const scriptType = classifyScript(script);
|
|
1212
1481
|
if (!canFinalize(input, script, scriptType))
|
|
1213
1482
|
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
1214
|
-
return prepareFinalScripts(
|
|
1215
|
-
script,
|
|
1216
|
-
scriptType,
|
|
1217
|
-
input.partialSig,
|
|
1218
|
-
isSegwit,
|
|
1219
|
-
isP2SH,
|
|
1220
|
-
isP2WSH,
|
|
1221
|
-
);
|
|
1483
|
+
return prepareFinalScripts(script, scriptType, input.partialSig!, isSegwit, isP2SH, isP2WSH);
|
|
1222
1484
|
}
|
|
1223
|
-
|
|
1224
|
-
function prepareFinalScripts(
|
|
1225
|
-
script,
|
|
1226
|
-
scriptType,
|
|
1227
|
-
partialSig,
|
|
1228
|
-
isSegwit,
|
|
1229
|
-
isP2SH,
|
|
1230
|
-
isP2WSH,
|
|
1231
|
-
) {
|
|
1232
|
-
|
|
1233
|
-
|
|
1485
|
+
|
|
1486
|
+
export function prepareFinalScripts(
|
|
1487
|
+
script: Buffer,
|
|
1488
|
+
scriptType: string,
|
|
1489
|
+
partialSig: PartialSig[],
|
|
1490
|
+
isSegwit: boolean,
|
|
1491
|
+
isP2SH: boolean,
|
|
1492
|
+
isP2WSH: boolean,
|
|
1493
|
+
): {
|
|
1494
|
+
finalScriptSig: Buffer | undefined;
|
|
1495
|
+
finalScriptWitness: Buffer | undefined;
|
|
1496
|
+
} {
|
|
1497
|
+
let finalScriptSig: Buffer | undefined;
|
|
1498
|
+
let finalScriptWitness: Buffer | undefined;
|
|
1499
|
+
|
|
1234
1500
|
// Wow, the payments API is very handy
|
|
1235
|
-
const payment = getPayment(script, scriptType, partialSig);
|
|
1501
|
+
const payment: payments.Payment = getPayment(script, scriptType, partialSig);
|
|
1236
1502
|
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
|
|
1237
1503
|
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
|
|
1504
|
+
|
|
1238
1505
|
if (isSegwit) {
|
|
1239
1506
|
if (p2wsh) {
|
|
1240
|
-
finalScriptWitness = (
|
|
1241
|
-
p2wsh.witness,
|
|
1242
|
-
);
|
|
1507
|
+
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!);
|
|
1243
1508
|
} else {
|
|
1244
|
-
finalScriptWitness = (
|
|
1245
|
-
payment.witness,
|
|
1246
|
-
);
|
|
1509
|
+
finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
|
|
1247
1510
|
}
|
|
1248
1511
|
if (p2sh) {
|
|
1249
1512
|
finalScriptSig = p2sh.input;
|
|
@@ -1260,15 +1523,18 @@ function prepareFinalScripts(
|
|
|
1260
1523
|
finalScriptWitness,
|
|
1261
1524
|
};
|
|
1262
1525
|
}
|
|
1263
|
-
|
|
1526
|
+
|
|
1264
1527
|
function getHashAndSighashType(
|
|
1265
|
-
inputs,
|
|
1266
|
-
inputIndex,
|
|
1267
|
-
pubkey,
|
|
1268
|
-
cache,
|
|
1269
|
-
sighashTypes,
|
|
1270
|
-
) {
|
|
1271
|
-
|
|
1528
|
+
inputs: PsbtInput[],
|
|
1529
|
+
inputIndex: number,
|
|
1530
|
+
pubkey: Buffer,
|
|
1531
|
+
cache: PsbtCache,
|
|
1532
|
+
sighashTypes: number[],
|
|
1533
|
+
): {
|
|
1534
|
+
hash: Buffer;
|
|
1535
|
+
sighashType: number;
|
|
1536
|
+
} {
|
|
1537
|
+
const input = checkForInput(inputs, inputIndex);
|
|
1272
1538
|
const { hash, sighashType, script } = getHashForSig(
|
|
1273
1539
|
inputIndex,
|
|
1274
1540
|
input,
|
|
@@ -1276,40 +1542,53 @@ function getHashAndSighashType(
|
|
|
1276
1542
|
false,
|
|
1277
1543
|
sighashTypes,
|
|
1278
1544
|
);
|
|
1545
|
+
|
|
1279
1546
|
checkScriptForPubkey(pubkey, script, 'sign');
|
|
1280
1547
|
return {
|
|
1281
1548
|
hash,
|
|
1282
1549
|
sighashType,
|
|
1283
1550
|
};
|
|
1284
1551
|
}
|
|
1285
|
-
|
|
1552
|
+
|
|
1553
|
+
function getHashForSig(
|
|
1554
|
+
inputIndex: number,
|
|
1555
|
+
input: PsbtInput,
|
|
1556
|
+
cache: PsbtCache,
|
|
1557
|
+
forValidate: boolean,
|
|
1558
|
+
sighashTypes?: number[],
|
|
1559
|
+
): {
|
|
1560
|
+
script: Buffer;
|
|
1561
|
+
hash: Buffer;
|
|
1562
|
+
sighashType: number;
|
|
1563
|
+
} {
|
|
1286
1564
|
const unsignedTx = cache.__TX;
|
|
1287
|
-
const sighashType =
|
|
1288
|
-
input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
|
|
1565
|
+
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
|
1289
1566
|
checkSighashTypeAllowed(sighashType, sighashTypes);
|
|
1290
|
-
|
|
1291
|
-
let
|
|
1567
|
+
|
|
1568
|
+
let hash: Buffer;
|
|
1569
|
+
let prevout: Output;
|
|
1570
|
+
|
|
1292
1571
|
if (input.nonWitnessUtxo) {
|
|
1293
|
-
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
|
|
1294
|
-
|
|
1295
|
-
input,
|
|
1296
|
-
inputIndex,
|
|
1297
|
-
);
|
|
1572
|
+
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1573
|
+
|
|
1298
1574
|
const prevoutHash = unsignedTx.ins[inputIndex].hash;
|
|
1299
1575
|
const utxoHash = nonWitnessUtxoTx.getHash();
|
|
1576
|
+
|
|
1300
1577
|
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
|
|
1301
1578
|
if (!prevoutHash.equals(utxoHash)) {
|
|
1302
1579
|
throw new Error(
|
|
1303
1580
|
`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
|
|
1304
1581
|
);
|
|
1305
1582
|
}
|
|
1583
|
+
|
|
1306
1584
|
const prevoutIndex = unsignedTx.ins[inputIndex].index;
|
|
1307
|
-
prevout = nonWitnessUtxoTx.outs[prevoutIndex];
|
|
1585
|
+
prevout = nonWitnessUtxoTx.outs[prevoutIndex] as Output;
|
|
1308
1586
|
} else if (input.witnessUtxo) {
|
|
1309
1587
|
prevout = input.witnessUtxo;
|
|
1310
1588
|
} else {
|
|
1311
1589
|
throw new Error('Need a Utxo input item for signing');
|
|
1312
1590
|
}
|
|
1591
|
+
|
|
1313
1592
|
const { meaningfulScript, type } = getMeaningfulScript(
|
|
1314
1593
|
prevout.script,
|
|
1315
1594
|
inputIndex,
|
|
@@ -1317,6 +1596,7 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
1317
1596
|
input.redeemScript,
|
|
1318
1597
|
input.witnessScript,
|
|
1319
1598
|
);
|
|
1599
|
+
|
|
1320
1600
|
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
|
|
1321
1601
|
hash = unsignedTx.hashForWitnessV0(
|
|
1322
1602
|
inputIndex,
|
|
@@ -1324,23 +1604,15 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
1324
1604
|
prevout.value,
|
|
1325
1605
|
sighashType,
|
|
1326
1606
|
);
|
|
1327
|
-
} else if (
|
|
1607
|
+
} else if (isP2WPKH(meaningfulScript)) {
|
|
1328
1608
|
// P2WPKH uses the P2PKH template for prevoutScript when signing
|
|
1329
1609
|
const signingScript = payments.p2pkh({
|
|
1330
1610
|
hash: meaningfulScript.slice(2),
|
|
1331
|
-
}).output
|
|
1332
|
-
hash = unsignedTx.hashForWitnessV0(
|
|
1333
|
-
inputIndex,
|
|
1334
|
-
signingScript,
|
|
1335
|
-
prevout.value,
|
|
1336
|
-
sighashType,
|
|
1337
|
-
);
|
|
1611
|
+
}).output!;
|
|
1612
|
+
hash = unsignedTx.hashForWitnessV0(inputIndex, signingScript, prevout.value, sighashType);
|
|
1338
1613
|
} else {
|
|
1339
1614
|
// non-segwit
|
|
1340
|
-
if (
|
|
1341
|
-
input.nonWitnessUtxo === undefined &&
|
|
1342
|
-
cache.__UNSAFE_SIGN_NONSEGWIT === false
|
|
1343
|
-
)
|
|
1615
|
+
if (input.nonWitnessUtxo === undefined && cache.__UNSAFE_SIGN_NONSEGWIT === false)
|
|
1344
1616
|
throw new Error(
|
|
1345
1617
|
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
|
|
1346
1618
|
`${meaningfulScript.toString('hex')}`,
|
|
@@ -1355,19 +1627,22 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
1355
1627
|
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
|
|
1356
1628
|
'*********************',
|
|
1357
1629
|
);
|
|
1358
|
-
hash = unsignedTx.hashForSignature(
|
|
1359
|
-
inputIndex,
|
|
1360
|
-
meaningfulScript,
|
|
1361
|
-
sighashType,
|
|
1362
|
-
);
|
|
1630
|
+
hash = unsignedTx.hashForSignature(inputIndex, meaningfulScript, sighashType);
|
|
1363
1631
|
}
|
|
1632
|
+
|
|
1364
1633
|
return {
|
|
1365
1634
|
script: meaningfulScript,
|
|
1366
1635
|
sighashType,
|
|
1367
1636
|
hash,
|
|
1368
1637
|
};
|
|
1369
1638
|
}
|
|
1370
|
-
|
|
1639
|
+
|
|
1640
|
+
function getAllTaprootHashesForSig(
|
|
1641
|
+
inputIndex: number,
|
|
1642
|
+
input: PsbtInput,
|
|
1643
|
+
inputs: PsbtInput[],
|
|
1644
|
+
cache: PsbtCache,
|
|
1645
|
+
): { pubkey: Buffer; hash: Buffer; leafHash?: Buffer }[] {
|
|
1371
1646
|
const allPublicKeys = [];
|
|
1372
1647
|
if (input.tapInternalKey) {
|
|
1373
1648
|
const key = getPrevoutTaprootKey(inputIndex, input, cache);
|
|
@@ -1375,45 +1650,56 @@ function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) {
|
|
|
1375
1650
|
allPublicKeys.push(key);
|
|
1376
1651
|
}
|
|
1377
1652
|
}
|
|
1653
|
+
|
|
1378
1654
|
if (input.tapScriptSig) {
|
|
1379
|
-
const tapScriptPubkeys = input.tapScriptSig.map(tss => tss.pubkey);
|
|
1655
|
+
const tapScriptPubkeys = input.tapScriptSig.map((tss) => tss.pubkey);
|
|
1380
1656
|
allPublicKeys.push(...tapScriptPubkeys);
|
|
1381
1657
|
}
|
|
1382
|
-
|
|
1658
|
+
|
|
1659
|
+
const allHashes = allPublicKeys.map((pubicKey) =>
|
|
1383
1660
|
getTaprootHashesForSig(inputIndex, input, inputs, pubicKey, cache),
|
|
1384
1661
|
);
|
|
1662
|
+
|
|
1385
1663
|
return allHashes.flat();
|
|
1386
1664
|
}
|
|
1387
|
-
|
|
1665
|
+
|
|
1666
|
+
function getPrevoutTaprootKey(
|
|
1667
|
+
inputIndex: number,
|
|
1668
|
+
input: PsbtInput,
|
|
1669
|
+
cache: PsbtCache,
|
|
1670
|
+
): Buffer | null {
|
|
1388
1671
|
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
1389
|
-
return
|
|
1672
|
+
return isP2TR(script) ? script.subarray(2, 34) : null;
|
|
1390
1673
|
}
|
|
1391
|
-
|
|
1674
|
+
|
|
1675
|
+
function trimTaprootSig(signature: Buffer): Buffer {
|
|
1392
1676
|
return signature.length === 64 ? signature : signature.subarray(0, 64);
|
|
1393
1677
|
}
|
|
1678
|
+
|
|
1394
1679
|
function getTaprootHashesForSig(
|
|
1395
|
-
inputIndex,
|
|
1396
|
-
input,
|
|
1397
|
-
inputs,
|
|
1398
|
-
pubkey,
|
|
1399
|
-
cache,
|
|
1400
|
-
tapLeafHashToSign,
|
|
1401
|
-
allowedSighashTypes,
|
|
1402
|
-
) {
|
|
1680
|
+
inputIndex: number,
|
|
1681
|
+
input: PsbtInput,
|
|
1682
|
+
inputs: PsbtInput[],
|
|
1683
|
+
pubkey: Buffer,
|
|
1684
|
+
cache: PsbtCache,
|
|
1685
|
+
tapLeafHashToSign?: Buffer,
|
|
1686
|
+
allowedSighashTypes?: number[],
|
|
1687
|
+
): { pubkey: Buffer; hash: Buffer; leafHash?: Buffer }[] {
|
|
1403
1688
|
const unsignedTx = cache.__TX;
|
|
1404
|
-
|
|
1405
|
-
|
|
1689
|
+
|
|
1690
|
+
const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
|
|
1406
1691
|
checkSighashTypeAllowed(sighashType, allowedSighashTypes);
|
|
1407
|
-
|
|
1692
|
+
|
|
1693
|
+
const prevOuts: Output[] = inputs.map((i, index) =>
|
|
1408
1694
|
getScriptAndAmountFromUtxo(index, i, cache),
|
|
1409
1695
|
);
|
|
1410
|
-
const signingScripts = prevOuts.map(o => o.script);
|
|
1411
|
-
const values = prevOuts.map(o => o.value);
|
|
1696
|
+
const signingScripts = prevOuts.map((o) => o.script);
|
|
1697
|
+
const values = prevOuts.map((o) => o.value);
|
|
1698
|
+
|
|
1412
1699
|
const hashes = [];
|
|
1413
1700
|
if (input.tapInternalKey && !tapLeafHashToSign) {
|
|
1414
|
-
const outputKey =
|
|
1415
|
-
|
|
1416
|
-
if ((0, bip371_1.toXOnly)(pubkey).equals(outputKey)) {
|
|
1701
|
+
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || Buffer.from([]);
|
|
1702
|
+
if (toXOnly(pubkey).equals(outputKey)) {
|
|
1417
1703
|
const tapKeyHash = unsignedTx.hashForWitnessV1(
|
|
1418
1704
|
inputIndex,
|
|
1419
1705
|
signingScripts,
|
|
@@ -1423,22 +1709,18 @@ function getTaprootHashesForSig(
|
|
|
1423
1709
|
hashes.push({ pubkey, hash: tapKeyHash });
|
|
1424
1710
|
}
|
|
1425
1711
|
}
|
|
1712
|
+
|
|
1426
1713
|
const tapLeafHashes = (input.tapLeafScript || [])
|
|
1427
|
-
.filter(tapLeaf =>
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
.map(tapLeaf => {
|
|
1431
|
-
const hash = (0, bip341_1.tapleafHash)({
|
|
1714
|
+
.filter((tapLeaf) => pubkeyInScript(pubkey, tapLeaf.script))
|
|
1715
|
+
.map((tapLeaf) => {
|
|
1716
|
+
const hash = tapleafHash({
|
|
1432
1717
|
output: tapLeaf.script,
|
|
1433
1718
|
version: tapLeaf.leafVersion,
|
|
1434
1719
|
});
|
|
1435
1720
|
return Object.assign({ hash }, tapLeaf);
|
|
1436
1721
|
})
|
|
1437
|
-
.filter(
|
|
1438
|
-
|
|
1439
|
-
!tapLeafHashToSign || tapLeafHashToSign.equals(tapLeaf.hash),
|
|
1440
|
-
)
|
|
1441
|
-
.map(tapLeaf => {
|
|
1722
|
+
.filter((tapLeaf) => !tapLeafHashToSign || tapLeafHashToSign.equals(tapLeaf.hash))
|
|
1723
|
+
.map((tapLeaf) => {
|
|
1442
1724
|
const tapScriptHash = unsignedTx.hashForWitnessV1(
|
|
1443
1725
|
inputIndex,
|
|
1444
1726
|
signingScripts,
|
|
@@ -1446,15 +1728,18 @@ function getTaprootHashesForSig(
|
|
|
1446
1728
|
sighashType,
|
|
1447
1729
|
tapLeaf.hash,
|
|
1448
1730
|
);
|
|
1731
|
+
|
|
1449
1732
|
return {
|
|
1450
1733
|
pubkey,
|
|
1451
1734
|
hash: tapScriptHash,
|
|
1452
1735
|
leafHash: tapLeaf.hash,
|
|
1453
1736
|
};
|
|
1454
1737
|
});
|
|
1738
|
+
|
|
1455
1739
|
return hashes.concat(tapLeafHashes);
|
|
1456
1740
|
}
|
|
1457
|
-
|
|
1741
|
+
|
|
1742
|
+
function checkSighashTypeAllowed(sighashType: number, sighashTypes?: number[]): void {
|
|
1458
1743
|
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
|
|
1459
1744
|
const str = sighashTypeToString(sighashType);
|
|
1460
1745
|
throw new Error(
|
|
@@ -1463,8 +1748,13 @@ function checkSighashTypeAllowed(sighashType, sighashTypes) {
|
|
|
1463
1748
|
);
|
|
1464
1749
|
}
|
|
1465
1750
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1751
|
+
|
|
1752
|
+
function getPayment(
|
|
1753
|
+
script: Buffer,
|
|
1754
|
+
scriptType: string,
|
|
1755
|
+
partialSig: PartialSig[],
|
|
1756
|
+
): payments.Payment {
|
|
1757
|
+
let payment: payments.Payment;
|
|
1468
1758
|
switch (scriptType) {
|
|
1469
1759
|
case 'multisig':
|
|
1470
1760
|
const sigs = getSortedSigs(script, partialSig);
|
|
@@ -1494,11 +1784,23 @@ function getPayment(script, scriptType, partialSig) {
|
|
|
1494
1784
|
});
|
|
1495
1785
|
break;
|
|
1496
1786
|
}
|
|
1497
|
-
return payment
|
|
1787
|
+
return payment!;
|
|
1498
1788
|
}
|
|
1499
|
-
|
|
1789
|
+
|
|
1790
|
+
interface GetScriptReturn {
|
|
1791
|
+
script: Buffer | null;
|
|
1792
|
+
isSegwit: boolean;
|
|
1793
|
+
isP2SH: boolean;
|
|
1794
|
+
isP2WSH: boolean;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
function getScriptFromInput(
|
|
1798
|
+
inputIndex: number,
|
|
1799
|
+
input: PsbtInput,
|
|
1800
|
+
cache: PsbtCache,
|
|
1801
|
+
): GetScriptReturn {
|
|
1500
1802
|
const unsignedTx = cache.__TX;
|
|
1501
|
-
const res = {
|
|
1803
|
+
const res: GetScriptReturn = {
|
|
1502
1804
|
script: null,
|
|
1503
1805
|
isSegwit: false,
|
|
1504
1806
|
isP2SH: false,
|
|
@@ -1512,116 +1814,124 @@ function getScriptFromInput(inputIndex, input, cache) {
|
|
|
1512
1814
|
res.script = input.redeemScript;
|
|
1513
1815
|
} else {
|
|
1514
1816
|
if (input.nonWitnessUtxo) {
|
|
1515
|
-
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
|
|
1516
|
-
cache,
|
|
1517
|
-
input,
|
|
1518
|
-
inputIndex,
|
|
1519
|
-
);
|
|
1817
|
+
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1520
1818
|
const prevoutIndex = unsignedTx.ins[inputIndex].index;
|
|
1521
1819
|
res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
|
|
1522
1820
|
} else if (input.witnessUtxo) {
|
|
1523
1821
|
res.script = input.witnessUtxo.script;
|
|
1524
1822
|
}
|
|
1525
1823
|
}
|
|
1526
|
-
if (input.witnessScript ||
|
|
1824
|
+
if (input.witnessScript || isP2WPKH(res.script!)) {
|
|
1527
1825
|
res.isSegwit = true;
|
|
1528
1826
|
}
|
|
1529
1827
|
return res;
|
|
1530
1828
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1829
|
+
|
|
1830
|
+
function getSignersFromHD(
|
|
1831
|
+
inputIndex: number,
|
|
1832
|
+
inputs: PsbtInput[],
|
|
1833
|
+
hdKeyPair: HDSigner | HDSignerAsync,
|
|
1834
|
+
): (HDSigner | HDSignerAsync)[] {
|
|
1835
|
+
const input = checkForInput(inputs, inputIndex);
|
|
1533
1836
|
if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
|
|
1534
1837
|
throw new Error('Need bip32Derivation to sign with HD');
|
|
1535
1838
|
}
|
|
1536
1839
|
const myDerivations = input.bip32Derivation
|
|
1537
|
-
.map(bipDv => {
|
|
1840
|
+
.map((bipDv) => {
|
|
1538
1841
|
if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
|
|
1539
1842
|
return bipDv;
|
|
1540
1843
|
} else {
|
|
1541
1844
|
return;
|
|
1542
1845
|
}
|
|
1543
1846
|
})
|
|
1544
|
-
.filter(v => !!v);
|
|
1847
|
+
.filter((v) => !!v);
|
|
1545
1848
|
if (myDerivations.length === 0) {
|
|
1546
1849
|
throw new Error(
|
|
1547
1850
|
'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
|
|
1548
1851
|
);
|
|
1549
1852
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1853
|
+
|
|
1854
|
+
return myDerivations.map((bipDv) => {
|
|
1855
|
+
const node = hdKeyPair.derivePath(bipDv!.path);
|
|
1856
|
+
if (!bipDv!.pubkey.equals(node.publicKey)) {
|
|
1553
1857
|
throw new Error('pubkey did not match bip32Derivation');
|
|
1554
1858
|
}
|
|
1555
1859
|
return node;
|
|
1556
1860
|
});
|
|
1557
1861
|
}
|
|
1558
|
-
|
|
1862
|
+
|
|
1863
|
+
function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
|
|
1559
1864
|
const p2ms = payments.p2ms({ output: script });
|
|
1560
1865
|
// for each pubkey in order of p2ms script
|
|
1561
|
-
return p2ms
|
|
1562
|
-
.map(pk => {
|
|
1866
|
+
return p2ms
|
|
1867
|
+
.pubkeys!.map((pk) => {
|
|
1563
1868
|
// filter partialSig array by pubkey being equal
|
|
1564
1869
|
return (
|
|
1565
|
-
partialSig.filter(ps => {
|
|
1870
|
+
partialSig.filter((ps) => {
|
|
1566
1871
|
return ps.pubkey.equals(pk);
|
|
1567
1872
|
})[0] || {}
|
|
1568
1873
|
).signature;
|
|
1569
1874
|
// Any pubkey without a match will return undefined
|
|
1570
1875
|
// this last filter removes all the undefined items in the array.
|
|
1571
1876
|
})
|
|
1572
|
-
.filter(v => !!v);
|
|
1877
|
+
.filter((v) => !!v);
|
|
1573
1878
|
}
|
|
1574
|
-
|
|
1879
|
+
|
|
1880
|
+
function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
|
|
1575
1881
|
let offset = 0;
|
|
1576
|
-
|
|
1882
|
+
|
|
1883
|
+
function readSlice(n: number): Buffer {
|
|
1577
1884
|
offset += n;
|
|
1578
1885
|
return buffer.slice(offset - n, offset);
|
|
1579
1886
|
}
|
|
1580
|
-
|
|
1887
|
+
|
|
1888
|
+
function readVarInt(): number {
|
|
1581
1889
|
const vi = varuint.decode(buffer, offset);
|
|
1582
|
-
offset += varuint.decode.bytes;
|
|
1890
|
+
offset += (varuint.decode as any).bytes;
|
|
1583
1891
|
return vi;
|
|
1584
1892
|
}
|
|
1585
|
-
|
|
1893
|
+
|
|
1894
|
+
function readVarSlice(): Buffer {
|
|
1586
1895
|
return readSlice(readVarInt());
|
|
1587
1896
|
}
|
|
1588
|
-
|
|
1897
|
+
|
|
1898
|
+
function readVector(): Buffer[] {
|
|
1589
1899
|
const count = readVarInt();
|
|
1590
|
-
const vector = [];
|
|
1900
|
+
const vector: Buffer[] = [];
|
|
1591
1901
|
for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
|
1592
1902
|
return vector;
|
|
1593
1903
|
}
|
|
1904
|
+
|
|
1594
1905
|
return readVector();
|
|
1595
1906
|
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
? 'SIGHASH_ANYONECANPAY | '
|
|
1600
|
-
: '';
|
|
1907
|
+
|
|
1908
|
+
function sighashTypeToString(sighashType: number): string {
|
|
1909
|
+
let text = sighashType & Transaction.SIGHASH_ANYONECANPAY ? 'SIGHASH_ANYONECANPAY | ' : '';
|
|
1601
1910
|
const sigMod = sighashType & 0x1f;
|
|
1602
1911
|
switch (sigMod) {
|
|
1603
|
-
case
|
|
1912
|
+
case Transaction.SIGHASH_ALL:
|
|
1604
1913
|
text += 'SIGHASH_ALL';
|
|
1605
1914
|
break;
|
|
1606
|
-
case
|
|
1915
|
+
case Transaction.SIGHASH_SINGLE:
|
|
1607
1916
|
text += 'SIGHASH_SINGLE';
|
|
1608
1917
|
break;
|
|
1609
|
-
case
|
|
1918
|
+
case Transaction.SIGHASH_NONE:
|
|
1610
1919
|
text += 'SIGHASH_NONE';
|
|
1611
1920
|
break;
|
|
1612
1921
|
}
|
|
1613
1922
|
return text;
|
|
1614
1923
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] =
|
|
1924
|
+
|
|
1925
|
+
function addNonWitnessTxCache(cache: PsbtCache, input: PsbtInput, inputIndex: number): void {
|
|
1926
|
+
cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!;
|
|
1927
|
+
cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = Transaction.fromBuffer(input.nonWitnessUtxo!);
|
|
1928
|
+
|
|
1619
1929
|
const self = cache;
|
|
1620
1930
|
const selfIndex = inputIndex;
|
|
1621
1931
|
delete input.nonWitnessUtxo;
|
|
1622
1932
|
Object.defineProperty(input, 'nonWitnessUtxo', {
|
|
1623
1933
|
enumerable: true,
|
|
1624
|
-
get() {
|
|
1934
|
+
get(): Buffer {
|
|
1625
1935
|
const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
|
|
1626
1936
|
const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
|
|
1627
1937
|
if (buf !== undefined) {
|
|
@@ -1632,37 +1942,35 @@ function addNonWitnessTxCache(cache, input, inputIndex) {
|
|
|
1632
1942
|
return newBuf;
|
|
1633
1943
|
}
|
|
1634
1944
|
},
|
|
1635
|
-
set(data) {
|
|
1945
|
+
set(data: Buffer): void {
|
|
1636
1946
|
self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
|
|
1637
1947
|
},
|
|
1638
1948
|
});
|
|
1639
1949
|
}
|
|
1950
|
+
|
|
1640
1951
|
function inputFinalizeGetAmts(
|
|
1641
|
-
inputs,
|
|
1642
|
-
tx,
|
|
1643
|
-
cache,
|
|
1644
|
-
mustFinalize,
|
|
1645
|
-
disableOutputChecks,
|
|
1646
|
-
) {
|
|
1952
|
+
inputs: PsbtInput[],
|
|
1953
|
+
tx: Transaction,
|
|
1954
|
+
cache: PsbtCache,
|
|
1955
|
+
mustFinalize: boolean,
|
|
1956
|
+
disableOutputChecks?: boolean,
|
|
1957
|
+
): void {
|
|
1647
1958
|
let inputAmount = 0;
|
|
1648
1959
|
inputs.forEach((input, idx) => {
|
|
1649
|
-
if (mustFinalize && input.finalScriptSig)
|
|
1650
|
-
tx.ins[idx].script = input.finalScriptSig;
|
|
1960
|
+
if (mustFinalize && input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
|
|
1651
1961
|
if (mustFinalize && input.finalScriptWitness) {
|
|
1652
|
-
tx.ins[idx].witness = scriptWitnessToWitnessStack(
|
|
1653
|
-
input.finalScriptWitness,
|
|
1654
|
-
);
|
|
1962
|
+
tx.ins[idx].witness = scriptWitnessToWitnessStack(input.finalScriptWitness);
|
|
1655
1963
|
}
|
|
1656
1964
|
if (input.witnessUtxo) {
|
|
1657
1965
|
inputAmount += input.witnessUtxo.value;
|
|
1658
1966
|
} else if (input.nonWitnessUtxo) {
|
|
1659
1967
|
const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
|
|
1660
1968
|
const vout = tx.ins[idx].index;
|
|
1661
|
-
const out = nwTx.outs[vout];
|
|
1969
|
+
const out = nwTx.outs[vout] as Output;
|
|
1662
1970
|
inputAmount += out.value;
|
|
1663
1971
|
}
|
|
1664
1972
|
});
|
|
1665
|
-
const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
|
|
1973
|
+
const outputAmount = (tx.outs as Output[]).reduce((total, o) => total + o.value, 0);
|
|
1666
1974
|
const fee = inputAmount - outputAmount;
|
|
1667
1975
|
if (!disableOutputChecks) {
|
|
1668
1976
|
if (fee < 0) {
|
|
@@ -1674,36 +1982,49 @@ function inputFinalizeGetAmts(
|
|
|
1674
1982
|
cache.__EXTRACTED_TX = tx;
|
|
1675
1983
|
cache.__FEE_RATE = Math.floor(fee / bytes);
|
|
1676
1984
|
}
|
|
1677
|
-
|
|
1985
|
+
|
|
1986
|
+
function nonWitnessUtxoTxFromCache(
|
|
1987
|
+
cache: PsbtCache,
|
|
1988
|
+
input: PsbtInput,
|
|
1989
|
+
inputIndex: number,
|
|
1990
|
+
): Transaction {
|
|
1678
1991
|
const c = cache.__NON_WITNESS_UTXO_TX_CACHE;
|
|
1679
1992
|
if (!c[inputIndex]) {
|
|
1680
1993
|
addNonWitnessTxCache(cache, input, inputIndex);
|
|
1681
1994
|
}
|
|
1682
1995
|
return c[inputIndex];
|
|
1683
1996
|
}
|
|
1684
|
-
|
|
1997
|
+
|
|
1998
|
+
function getScriptFromUtxo(inputIndex: number, input: PsbtInput, cache: PsbtCache): Buffer {
|
|
1685
1999
|
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
1686
2000
|
return script;
|
|
1687
2001
|
}
|
|
1688
|
-
|
|
2002
|
+
|
|
2003
|
+
function getScriptAndAmountFromUtxo(
|
|
2004
|
+
inputIndex: number,
|
|
2005
|
+
input: PsbtInput,
|
|
2006
|
+
cache: PsbtCache,
|
|
2007
|
+
): { script: Buffer; value: number } {
|
|
1689
2008
|
if (input.witnessUtxo !== undefined) {
|
|
1690
2009
|
return {
|
|
1691
2010
|
script: input.witnessUtxo.script,
|
|
1692
2011
|
value: input.witnessUtxo.value,
|
|
1693
2012
|
};
|
|
1694
2013
|
} else if (input.nonWitnessUtxo !== undefined) {
|
|
1695
|
-
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
|
|
1696
|
-
cache,
|
|
1697
|
-
input,
|
|
1698
|
-
inputIndex,
|
|
1699
|
-
);
|
|
2014
|
+
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1700
2015
|
const o = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index];
|
|
1701
2016
|
return { script: o.script, value: o.value };
|
|
1702
2017
|
} else {
|
|
1703
2018
|
throw new Error("Can't find pubkey in input without Utxo data");
|
|
1704
2019
|
}
|
|
1705
2020
|
}
|
|
1706
|
-
|
|
2021
|
+
|
|
2022
|
+
function pubkeyInInput(
|
|
2023
|
+
pubkey: Buffer,
|
|
2024
|
+
input: PsbtInput,
|
|
2025
|
+
inputIndex: number,
|
|
2026
|
+
cache: PsbtCache,
|
|
2027
|
+
): boolean {
|
|
1707
2028
|
const script = getScriptFromUtxo(inputIndex, input, cache);
|
|
1708
2029
|
const { meaningfulScript } = getMeaningfulScript(
|
|
1709
2030
|
script,
|
|
@@ -1712,9 +2033,15 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) {
|
|
|
1712
2033
|
input.redeemScript,
|
|
1713
2034
|
input.witnessScript,
|
|
1714
2035
|
);
|
|
1715
|
-
return
|
|
2036
|
+
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1716
2037
|
}
|
|
1717
|
-
|
|
2038
|
+
|
|
2039
|
+
function pubkeyInOutput(
|
|
2040
|
+
pubkey: Buffer,
|
|
2041
|
+
output: PsbtOutput,
|
|
2042
|
+
outputIndex: number,
|
|
2043
|
+
cache: PsbtCache,
|
|
2044
|
+
): boolean {
|
|
1718
2045
|
const script = cache.__TX.outs[outputIndex].script;
|
|
1719
2046
|
const { meaningfulScript } = getMeaningfulScript(
|
|
1720
2047
|
script,
|
|
@@ -1723,24 +2050,21 @@ function pubkeyInOutput(pubkey, output, outputIndex, cache) {
|
|
|
1723
2050
|
output.redeemScript,
|
|
1724
2051
|
output.witnessScript,
|
|
1725
2052
|
);
|
|
1726
|
-
return
|
|
2053
|
+
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1727
2054
|
}
|
|
1728
|
-
|
|
2055
|
+
|
|
2056
|
+
function redeemFromFinalScriptSig(finalScript: Buffer | undefined): Buffer | undefined {
|
|
1729
2057
|
if (!finalScript) return;
|
|
1730
2058
|
const decomp = bscript.decompile(finalScript);
|
|
1731
2059
|
if (!decomp) return;
|
|
1732
2060
|
const lastItem = decomp[decomp.length - 1];
|
|
1733
|
-
if (
|
|
1734
|
-
!Buffer.isBuffer(lastItem) ||
|
|
1735
|
-
isPubkeyLike(lastItem) ||
|
|
1736
|
-
isSigLike(lastItem)
|
|
1737
|
-
)
|
|
1738
|
-
return;
|
|
2061
|
+
if (!Buffer.isBuffer(lastItem) || isPubkeyLike(lastItem) || isSigLike(lastItem)) return;
|
|
1739
2062
|
const sDecomp = bscript.decompile(lastItem);
|
|
1740
2063
|
if (!sDecomp) return;
|
|
1741
2064
|
return lastItem;
|
|
1742
2065
|
}
|
|
1743
|
-
|
|
2066
|
+
|
|
2067
|
+
function redeemFromFinalWitnessScript(finalScript: Buffer | undefined): Buffer | undefined {
|
|
1744
2068
|
if (!finalScript) return;
|
|
1745
2069
|
const decomp = scriptWitnessToWitnessStack(finalScript);
|
|
1746
2070
|
const lastItem = decomp[decomp.length - 1];
|
|
@@ -1749,7 +2073,8 @@ function redeemFromFinalWitnessScript(finalScript) {
|
|
|
1749
2073
|
if (!sDecomp) return;
|
|
1750
2074
|
return lastItem;
|
|
1751
2075
|
}
|
|
1752
|
-
|
|
2076
|
+
|
|
2077
|
+
function compressPubkey(pubkey: Buffer): Buffer {
|
|
1753
2078
|
if (pubkey.length === 65) {
|
|
1754
2079
|
const parity = pubkey[64] & 1;
|
|
1755
2080
|
const newKey = pubkey.slice(0, 33);
|
|
@@ -1758,71 +2083,92 @@ function compressPubkey(pubkey) {
|
|
|
1758
2083
|
}
|
|
1759
2084
|
return pubkey.slice();
|
|
1760
2085
|
}
|
|
1761
|
-
|
|
2086
|
+
|
|
2087
|
+
function isPubkeyLike(buf: Buffer): boolean {
|
|
1762
2088
|
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
|
|
1763
2089
|
}
|
|
1764
|
-
|
|
2090
|
+
|
|
2091
|
+
function isSigLike(buf: Buffer): boolean {
|
|
1765
2092
|
return bscript.isCanonicalScriptSignature(buf);
|
|
1766
2093
|
}
|
|
2094
|
+
|
|
1767
2095
|
function getMeaningfulScript(
|
|
1768
|
-
script,
|
|
1769
|
-
index,
|
|
1770
|
-
ioType,
|
|
1771
|
-
redeemScript,
|
|
1772
|
-
witnessScript,
|
|
1773
|
-
) {
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
const
|
|
2096
|
+
script: Buffer,
|
|
2097
|
+
index: number,
|
|
2098
|
+
ioType: 'input' | 'output',
|
|
2099
|
+
redeemScript?: Buffer,
|
|
2100
|
+
witnessScript?: Buffer,
|
|
2101
|
+
): {
|
|
2102
|
+
meaningfulScript: Buffer;
|
|
2103
|
+
type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw';
|
|
2104
|
+
} {
|
|
2105
|
+
const isP2SH = isP2SHScript(script);
|
|
2106
|
+
const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript);
|
|
2107
|
+
const isP2WSH = isP2WSHScript(script);
|
|
2108
|
+
|
|
1778
2109
|
if (isP2SH && redeemScript === undefined)
|
|
1779
2110
|
throw new Error('scriptPubkey is P2SH but redeemScript missing');
|
|
1780
2111
|
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
|
|
1781
|
-
throw new Error(
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
2112
|
+
throw new Error('scriptPubkey or redeemScript is P2WSH but witnessScript missing');
|
|
2113
|
+
|
|
2114
|
+
let meaningfulScript: Buffer;
|
|
2115
|
+
|
|
1785
2116
|
if (isP2SHP2WSH) {
|
|
1786
|
-
meaningfulScript = witnessScript
|
|
1787
|
-
checkRedeemScript(index, script, redeemScript
|
|
1788
|
-
checkWitnessScript(index, redeemScript
|
|
2117
|
+
meaningfulScript = witnessScript!;
|
|
2118
|
+
checkRedeemScript(index, script, redeemScript!, ioType);
|
|
2119
|
+
checkWitnessScript(index, redeemScript!, witnessScript!, ioType);
|
|
1789
2120
|
checkInvalidP2WSH(meaningfulScript);
|
|
1790
2121
|
} else if (isP2WSH) {
|
|
1791
|
-
meaningfulScript = witnessScript
|
|
1792
|
-
checkWitnessScript(index, script, witnessScript
|
|
2122
|
+
meaningfulScript = witnessScript!;
|
|
2123
|
+
checkWitnessScript(index, script, witnessScript!, ioType);
|
|
1793
2124
|
checkInvalidP2WSH(meaningfulScript);
|
|
1794
2125
|
} else if (isP2SH) {
|
|
1795
|
-
meaningfulScript = redeemScript
|
|
1796
|
-
checkRedeemScript(index, script, redeemScript
|
|
2126
|
+
meaningfulScript = redeemScript!;
|
|
2127
|
+
checkRedeemScript(index, script, redeemScript!, ioType);
|
|
1797
2128
|
} else {
|
|
1798
2129
|
meaningfulScript = script;
|
|
1799
2130
|
}
|
|
1800
2131
|
return {
|
|
1801
2132
|
meaningfulScript,
|
|
1802
|
-
type: isP2SHP2WSH
|
|
1803
|
-
? 'p2sh-p2wsh'
|
|
1804
|
-
: isP2SH
|
|
1805
|
-
? 'p2sh'
|
|
1806
|
-
: isP2WSH
|
|
1807
|
-
? 'p2wsh'
|
|
1808
|
-
: 'raw',
|
|
2133
|
+
type: isP2SHP2WSH ? 'p2sh-p2wsh' : isP2SH ? 'p2sh' : isP2WSH ? 'p2wsh' : 'raw',
|
|
1809
2134
|
};
|
|
1810
2135
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
(0, psbtutils_1.isP2SHScript)(script)
|
|
1815
|
-
) {
|
|
2136
|
+
|
|
2137
|
+
function checkInvalidP2WSH(script: Buffer): void {
|
|
2138
|
+
if (isP2WPKH(script) || isP2SHScript(script)) {
|
|
1816
2139
|
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
|
|
1817
2140
|
}
|
|
1818
2141
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2142
|
+
|
|
2143
|
+
type AllScriptType =
|
|
2144
|
+
| 'witnesspubkeyhash'
|
|
2145
|
+
| 'pubkeyhash'
|
|
2146
|
+
| 'multisig'
|
|
2147
|
+
| 'pubkey'
|
|
2148
|
+
| 'nonstandard'
|
|
2149
|
+
| 'p2sh-witnesspubkeyhash'
|
|
2150
|
+
| 'p2sh-pubkeyhash'
|
|
2151
|
+
| 'p2sh-multisig'
|
|
2152
|
+
| 'p2sh-pubkey'
|
|
2153
|
+
| 'p2sh-nonstandard'
|
|
2154
|
+
| 'p2wsh-pubkeyhash'
|
|
2155
|
+
| 'p2wsh-multisig'
|
|
2156
|
+
| 'p2wsh-pubkey'
|
|
2157
|
+
| 'p2wsh-nonstandard'
|
|
2158
|
+
| 'p2sh-p2wsh-pubkeyhash'
|
|
2159
|
+
| 'p2sh-p2wsh-multisig'
|
|
2160
|
+
| 'p2sh-p2wsh-pubkey'
|
|
2161
|
+
| 'p2sh-p2wsh-nonstandard';
|
|
2162
|
+
type ScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard';
|
|
2163
|
+
|
|
2164
|
+
function classifyScript(script: Buffer): ScriptType {
|
|
2165
|
+
if (isP2WPKH(script)) return 'witnesspubkeyhash';
|
|
2166
|
+
if (isP2PKH(script)) return 'pubkeyhash';
|
|
2167
|
+
if (isP2MS(script)) return 'multisig';
|
|
2168
|
+
if (isP2PK(script)) return 'pubkey';
|
|
1824
2169
|
return 'nonstandard';
|
|
1825
2170
|
}
|
|
1826
|
-
|
|
2171
|
+
|
|
2172
|
+
function range(n: number): number[] {
|
|
1827
2173
|
return [...Array(n).keys()];
|
|
1828
2174
|
}
|