@btc-vision/bitcoin 7.0.0-beta.0 → 7.0.0-beta.1
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/README.md +112 -13
- package/benchmark-compare/BENCHMARK.md +74 -59
- package/benchmark-compare/compare.bench.ts +249 -96
- package/benchmark-compare/harness.ts +23 -25
- package/benchmark-compare/package.json +1 -0
- package/browser/address.d.ts +4 -4
- package/browser/address.d.ts.map +1 -1
- package/browser/chunks/{psbt-parallel-B-dfm5GZ.js → psbt-parallel-jZ6QcCnM.js} +3128 -2731
- package/browser/index.d.ts +1 -1
- package/browser/index.d.ts.map +1 -1
- package/browser/index.js +603 -585
- package/browser/io/base58check.d.ts +1 -25
- package/browser/io/base58check.d.ts.map +1 -1
- package/browser/io/base64.d.ts.map +1 -1
- package/browser/networks.d.ts +1 -0
- package/browser/networks.d.ts.map +1 -1
- package/browser/payments/bip341.d.ts +17 -0
- package/browser/payments/bip341.d.ts.map +1 -1
- package/browser/payments/index.d.ts +3 -2
- package/browser/payments/index.d.ts.map +1 -1
- package/browser/payments/p2mr.d.ts +169 -0
- package/browser/payments/p2mr.d.ts.map +1 -0
- package/browser/payments/types.d.ts +11 -1
- package/browser/payments/types.d.ts.map +1 -1
- package/browser/psbt/bip371.d.ts +30 -0
- package/browser/psbt/bip371.d.ts.map +1 -1
- package/browser/psbt/psbtutils.d.ts +1 -0
- package/browser/psbt/psbtutils.d.ts.map +1 -1
- package/browser/psbt.d.ts.map +1 -1
- package/browser/workers/index.js +9 -9
- package/build/address.d.ts +4 -4
- package/build/address.d.ts.map +1 -1
- package/build/address.js +11 -1
- package/build/address.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/io/base58check.d.ts +1 -25
- package/build/io/base58check.d.ts.map +1 -1
- package/build/io/base58check.js +1 -31
- package/build/io/base58check.js.map +1 -1
- package/build/io/base64.d.ts.map +1 -1
- package/build/io/base64.js +3 -0
- package/build/io/base64.js.map +1 -1
- package/build/networks.d.ts +1 -0
- package/build/networks.d.ts.map +1 -1
- package/build/networks.js +12 -0
- package/build/networks.js.map +1 -1
- package/build/payments/bip341.d.ts +17 -0
- package/build/payments/bip341.d.ts.map +1 -1
- package/build/payments/bip341.js +32 -1
- package/build/payments/bip341.js.map +1 -1
- package/build/payments/index.d.ts +3 -2
- package/build/payments/index.d.ts.map +1 -1
- package/build/payments/index.js +2 -1
- package/build/payments/index.js.map +1 -1
- package/build/payments/p2mr.d.ts +178 -0
- package/build/payments/p2mr.d.ts.map +1 -0
- package/build/payments/p2mr.js +555 -0
- package/build/payments/p2mr.js.map +1 -0
- package/build/payments/types.d.ts +11 -1
- package/build/payments/types.d.ts.map +1 -1
- package/build/payments/types.js +1 -0
- package/build/payments/types.js.map +1 -1
- package/build/psbt/bip371.d.ts +30 -0
- package/build/psbt/bip371.d.ts.map +1 -1
- package/build/psbt/bip371.js +80 -15
- package/build/psbt/bip371.js.map +1 -1
- package/build/psbt/psbtutils.d.ts +1 -0
- package/build/psbt/psbtutils.d.ts.map +1 -1
- package/build/psbt/psbtutils.js +2 -0
- package/build/psbt/psbtutils.js.map +1 -1
- package/build/psbt.d.ts.map +1 -1
- package/build/psbt.js +3 -2
- package/build/psbt.js.map +1 -1
- package/build/pubkey.js +1 -1
- package/build/pubkey.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/documentation/README.md +122 -0
- package/documentation/address.md +820 -0
- package/documentation/block.md +679 -0
- package/documentation/crypto.md +461 -0
- package/documentation/ecc.md +584 -0
- package/documentation/errors.md +656 -0
- package/documentation/io.md +942 -0
- package/documentation/networks.md +625 -0
- package/documentation/p2mr.md +380 -0
- package/documentation/payments.md +1485 -0
- package/documentation/psbt.md +1400 -0
- package/documentation/script.md +730 -0
- package/documentation/taproot.md +670 -0
- package/documentation/transaction.md +943 -0
- package/documentation/types.md +587 -0
- package/documentation/workers.md +1007 -0
- package/eslint.config.js +3 -0
- package/package.json +17 -14
- package/src/address.ts +22 -10
- package/src/index.ts +1 -0
- package/src/io/base58check.ts +1 -35
- package/src/io/base64.ts +5 -0
- package/src/networks.ts +13 -0
- package/src/payments/bip341.ts +36 -1
- package/src/payments/index.ts +4 -0
- package/src/payments/p2mr.ts +660 -0
- package/src/payments/types.ts +12 -0
- package/src/psbt/bip371.ts +84 -13
- package/src/psbt/psbtutils.ts +2 -0
- package/src/psbt.ts +4 -2
- package/src/pubkey.ts +1 -1
- package/test/bitcoin.core.spec.ts +1 -1
- package/test/fixtures/p2mr.json +270 -0
- package/test/integration/taproot.spec.ts +7 -3
- package/test/opnetTestnet.spec.ts +302 -0
- package/test/payments.spec.ts +3 -1
- package/test/psbt.spec.ts +297 -2
- package/test/tsconfig.json +2 -2
|
@@ -0,0 +1,1400 @@
|
|
|
1
|
+
# PSBT (Partially Signed Bitcoin Transaction) - BIP 174
|
|
2
|
+
|
|
3
|
+
PSBT is a standardized binary format for unsigned and partially signed Bitcoin transactions. It allows multiple participants (wallets, hardware signers, coordinators) to collaboratively construct, sign, and finalize a transaction without requiring direct access to each other's private keys. This library implements BIP 174 with extensions for BIP 371 (Taproot).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
| Property | Value |
|
|
8
|
+
|----------|-------|
|
|
9
|
+
| BIP | 174 (base), 371 (Taproot extensions) |
|
|
10
|
+
| Format | Binary with base64/hex serialization |
|
|
11
|
+
| Module entry | `src/psbt.ts` |
|
|
12
|
+
| Internal modules | `PsbtCache`, `PsbtSigner`, `PsbtFinalizer`, `PsbtTransaction` |
|
|
13
|
+
| Supported script types | P2PKH, P2WPKH, P2SH, P2WSH, P2SH-P2WSH, P2TR, P2MR, P2MS, P2PK, P2OP, P2A |
|
|
14
|
+
|
|
15
|
+
### BIP 174 Roles
|
|
16
|
+
|
|
17
|
+
The PSBT specification defines six roles that the `Psbt` class fulfills:
|
|
18
|
+
|
|
19
|
+
| Role | Description | Methods |
|
|
20
|
+
|------|-------------|---------|
|
|
21
|
+
| **Creator** | Creates a new empty PSBT | `new Psbt()` |
|
|
22
|
+
| **Updater** | Adds inputs, outputs, and metadata | `addInput()`, `addOutput()`, `updateInput()`, `updateOutput()`, `updateGlobal()` |
|
|
23
|
+
| **Signer** | Signs inputs with private keys | `signInput()`, `signAllInputs()`, `signInputAsync()`, `signAllInputsAsync()` |
|
|
24
|
+
| **Combiner** | Merges multiple PSBTs for the same transaction | `combine()` |
|
|
25
|
+
| **Input Finalizer** | Constructs final scriptSigs and witnesses | `finalizeInput()`, `finalizeAllInputs()` |
|
|
26
|
+
| **Transaction Extractor** | Produces the final signed transaction | `extractTransaction()` |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import {
|
|
34
|
+
Psbt,
|
|
35
|
+
// Types
|
|
36
|
+
type PsbtOpts,
|
|
37
|
+
type PsbtOptsOptional,
|
|
38
|
+
type PsbtInputExtended,
|
|
39
|
+
type PsbtOutputExtended,
|
|
40
|
+
type Signer,
|
|
41
|
+
type SignerAsync,
|
|
42
|
+
type HDSigner,
|
|
43
|
+
type HDSignerAsync,
|
|
44
|
+
type ValidateSigFunction,
|
|
45
|
+
type FinalScriptsFunc,
|
|
46
|
+
type FinalTaprootScriptsFunc,
|
|
47
|
+
type AllScriptType,
|
|
48
|
+
type PsbtTxInput,
|
|
49
|
+
type PsbtTxOutput,
|
|
50
|
+
// Utilities
|
|
51
|
+
getFinalScripts,
|
|
52
|
+
prepareFinalScripts,
|
|
53
|
+
PsbtCache,
|
|
54
|
+
PsbtSigner,
|
|
55
|
+
PsbtFinalizer,
|
|
56
|
+
PsbtTransaction,
|
|
57
|
+
transactionFromBuffer,
|
|
58
|
+
} from '@btc-vision/bitcoin';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Class: Psbt
|
|
64
|
+
|
|
65
|
+
### Constructor
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
class Psbt {
|
|
69
|
+
constructor(opts?: PsbtOptsOptional, data?: PsbtBaseExtended);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Parameters:**
|
|
74
|
+
|
|
75
|
+
| Parameter | Type | Default | Description |
|
|
76
|
+
|-----------|------|---------|-------------|
|
|
77
|
+
| `opts` | `PsbtOptsOptional` | `{}` | Configuration options (network, maximumFeeRate, version) |
|
|
78
|
+
| `data` | `PsbtBaseExtended` | `new PsbtBase(new PsbtTransaction())` | Pre-existing PSBT data (used internally by deserialization) |
|
|
79
|
+
|
|
80
|
+
If `opts.version` is `3`, the transaction version is set to TRUC (Topologically Restricted Until Confirmation). Otherwise, if no inputs exist, the version defaults to `2`.
|
|
81
|
+
|
|
82
|
+
### Properties
|
|
83
|
+
|
|
84
|
+
| Property | Type | Description |
|
|
85
|
+
|----------|------|-------------|
|
|
86
|
+
| `inputCount` | `number` | Number of inputs in the PSBT |
|
|
87
|
+
| `version` | `number` | Transaction version (get/set) |
|
|
88
|
+
| `locktime` | `number` | Transaction locktime (get/set) |
|
|
89
|
+
| `txInputs` | `PsbtTxInput[]` | Read-only cloned array of transaction inputs |
|
|
90
|
+
| `txOutputs` | `PsbtTxOutput[]` | Read-only cloned array of transaction outputs with decoded addresses |
|
|
91
|
+
| `maximumFeeRate` | `number` | Current maximum fee rate in satoshis per byte |
|
|
92
|
+
| `data` | `PsbtBaseExtended` | The underlying BIP 174 data structure |
|
|
93
|
+
|
|
94
|
+
### Static Deserialization Methods
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// From base64 string
|
|
98
|
+
static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
|
|
99
|
+
|
|
100
|
+
// From hex string
|
|
101
|
+
static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
|
|
102
|
+
|
|
103
|
+
// From binary buffer
|
|
104
|
+
static fromBuffer(buffer: Uint8Array, opts?: PsbtOptsOptional): Psbt;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
All deserialization methods check for duplicate inputs and detect existing signatures.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Creating a PSBT
|
|
112
|
+
|
|
113
|
+
### addInput
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
addInput(inputData: PsbtInputExtended, checkPartialSigs?: boolean): this;
|
|
117
|
+
addInputs(inputDatas: PsbtInputExtended[], checkPartialSigs?: boolean): this;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Adds one or more inputs to the PSBT. Each input requires at minimum a `hash` (the TXID of the previous transaction) and an `index` (the output index being spent).
|
|
121
|
+
|
|
122
|
+
**Parameters:**
|
|
123
|
+
|
|
124
|
+
| Parameter | Type | Default | Description |
|
|
125
|
+
|-----------|------|---------|-------------|
|
|
126
|
+
| `inputData` | `PsbtInputExtended` | (required) | Input data with at least `hash` and `index` |
|
|
127
|
+
| `checkPartialSigs` | `boolean` | `true` | Whether to check if existing signatures prevent modification |
|
|
128
|
+
|
|
129
|
+
**PsbtInputExtended fields:**
|
|
130
|
+
|
|
131
|
+
| Field | Type | Required | Description |
|
|
132
|
+
|-------|------|----------|-------------|
|
|
133
|
+
| `hash` | `string \| Bytes32` | Yes | Previous transaction hash (TXID) |
|
|
134
|
+
| `index` | `number` | Yes | Output index in the previous transaction |
|
|
135
|
+
| `sequence` | `number` | No | Input sequence number |
|
|
136
|
+
| `witnessUtxo` | `{ script: Script; value: bigint }` | Conditional | UTXO data for segwit inputs |
|
|
137
|
+
| `nonWitnessUtxo` | `Uint8Array` | Conditional | Full previous transaction for non-segwit inputs |
|
|
138
|
+
| `redeemScript` | `Uint8Array` | No | Redeem script for P2SH inputs |
|
|
139
|
+
| `witnessScript` | `Uint8Array` | No | Witness script for P2WSH inputs |
|
|
140
|
+
| `bip32Derivation` | `Bip32Derivation[]` | No | BIP 32 derivation paths for HD signing |
|
|
141
|
+
| `tapInternalKey` | `Uint8Array` | No | Taproot internal key (32 bytes, x-only) |
|
|
142
|
+
| `tapMerkleRoot` | `Uint8Array` | No | Taproot Merkle root |
|
|
143
|
+
| `tapLeafScript` | `TapLeafScript[]` | No | Taproot leaf scripts with control blocks |
|
|
144
|
+
| `tapBip32Derivation` | `TapBip32Derivation[]` | No | Taproot BIP 32 derivation paths |
|
|
145
|
+
| `sighashType` | `number` | No | Sighash type for this input |
|
|
146
|
+
| `isPayToAnchor` | `boolean` | No | Whether this is a Pay-to-Anchor input |
|
|
147
|
+
|
|
148
|
+
**Example:**
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const psbt = new Psbt();
|
|
152
|
+
|
|
153
|
+
// Add a segwit input
|
|
154
|
+
psbt.addInput({
|
|
155
|
+
hash: 'abc123...', // TXID as hex string
|
|
156
|
+
index: 0,
|
|
157
|
+
witnessUtxo: {
|
|
158
|
+
script: Buffer.from('0014...', 'hex'), // scriptPubKey
|
|
159
|
+
value: 100000n, // in satoshis (bigint)
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Add a non-segwit input
|
|
164
|
+
psbt.addInput({
|
|
165
|
+
hash: txidBuffer, // TXID as Uint8Array (reversed)
|
|
166
|
+
index: 1,
|
|
167
|
+
nonWitnessUtxo: fullPreviousTxBuffer,
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### addOutput
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
addOutput(outputData: PsbtOutputExtended, checkPartialSigs?: boolean): this;
|
|
175
|
+
addOutputs(outputDatas: PsbtOutputExtended[], checkPartialSigs?: boolean): this;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Adds one or more outputs. Each output requires a `value` and either an `address` or a `script`.
|
|
179
|
+
|
|
180
|
+
**PsbtOutputExtended** is a union of two forms:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// Address-based output
|
|
184
|
+
interface PsbtOutputExtendedAddress {
|
|
185
|
+
readonly address: string;
|
|
186
|
+
readonly value: Satoshi; // bigint
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Script-based output
|
|
190
|
+
interface PsbtOutputExtendedScript {
|
|
191
|
+
readonly script: Script; // Uint8Array
|
|
192
|
+
readonly value: Satoshi; // bigint
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Example:**
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// Output by address
|
|
200
|
+
psbt.addOutput({
|
|
201
|
+
address: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
|
|
202
|
+
value: 50000n,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Output by script
|
|
206
|
+
psbt.addOutput({
|
|
207
|
+
script: outputScript,
|
|
208
|
+
value: 50000n,
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Signing
|
|
215
|
+
|
|
216
|
+
The PSBT class provides both synchronous and asynchronous signing methods. Async methods are useful for hardware wallets and remote signing services.
|
|
217
|
+
|
|
218
|
+
### signInput / signInputAsync
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
signInput(inputIndex: number, keyPair: Signer | HDSigner, sighashTypes?: number[]): this;
|
|
222
|
+
|
|
223
|
+
signInputAsync(
|
|
224
|
+
inputIndex: number,
|
|
225
|
+
keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
|
|
226
|
+
sighashTypes?: number[],
|
|
227
|
+
): Promise<void>;
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Signs a specific input. The method automatically detects whether the input is a Taproot input and routes to the appropriate signing logic.
|
|
231
|
+
|
|
232
|
+
**Parameters:**
|
|
233
|
+
|
|
234
|
+
| Parameter | Type | Default | Description |
|
|
235
|
+
|-----------|------|---------|-------------|
|
|
236
|
+
| `inputIndex` | `number` | (required) | Index of the input to sign |
|
|
237
|
+
| `keyPair` | `Signer \| HDSigner` | (required) | Key pair with signing capability |
|
|
238
|
+
| `sighashTypes` | `number[]` | `[SIGHASH_ALL]` | Allowed sighash types |
|
|
239
|
+
|
|
240
|
+
### signAllInputs / signAllInputsAsync
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
signAllInputs(keyPair: Signer | HDSigner, sighashTypes?: number[]): this;
|
|
244
|
+
|
|
245
|
+
signAllInputsAsync(
|
|
246
|
+
keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
|
|
247
|
+
sighashTypes?: number[],
|
|
248
|
+
): Promise<void>;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Attempts to sign every input with the provided key pair. Silently skips inputs that do not match the key. Throws only if no inputs were signed at all.
|
|
252
|
+
|
|
253
|
+
### signTaprootInput / signTaprootInputAsync
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
signTaprootInput(
|
|
257
|
+
inputIndex: number,
|
|
258
|
+
keyPair: Signer | HDSigner,
|
|
259
|
+
tapLeafHashToSign?: Uint8Array,
|
|
260
|
+
sighashTypes?: number[],
|
|
261
|
+
): this;
|
|
262
|
+
|
|
263
|
+
signTaprootInputAsync(
|
|
264
|
+
inputIndex: number,
|
|
265
|
+
keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
|
|
266
|
+
tapLeafHash?: Uint8Array,
|
|
267
|
+
sighashTypes?: number[],
|
|
268
|
+
): Promise<void>;
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Explicitly signs a Taproot input. Throws if the input is not a Taproot input. The optional `tapLeafHashToSign` restricts signing to a specific tap leaf.
|
|
272
|
+
|
|
273
|
+
**Default sighash types:**
|
|
274
|
+
- Non-Taproot: `[Transaction.SIGHASH_ALL]`
|
|
275
|
+
- Taproot: `[Transaction.SIGHASH_DEFAULT]`
|
|
276
|
+
|
|
277
|
+
### HD Signing
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this;
|
|
281
|
+
signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
|
|
282
|
+
|
|
283
|
+
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
|
|
284
|
+
signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Signs using an HD key pair. The method derives child keys from `bip32Derivation` data in each input and signs with the derived keys.
|
|
288
|
+
|
|
289
|
+
**Example:**
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { ECPairSigner, createNobleBackend } from '@btc-vision/ecpair';
|
|
293
|
+
import { networks } from '@btc-vision/bitcoin';
|
|
294
|
+
|
|
295
|
+
const backend = createNobleBackend();
|
|
296
|
+
|
|
297
|
+
// Synchronous signing
|
|
298
|
+
const keyPair = ECPairSigner.fromWIF(backend, 'L1...', networks.bitcoin);
|
|
299
|
+
psbt.signAllInputs(keyPair);
|
|
300
|
+
|
|
301
|
+
// Async signing (e.g., hardware wallet)
|
|
302
|
+
await psbt.signAllInputsAsync(hardwareWalletSigner);
|
|
303
|
+
|
|
304
|
+
// HD signing
|
|
305
|
+
const hdRoot = bip32.fromSeed(seed);
|
|
306
|
+
psbt.signAllInputsHD(hdRoot);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Finalizing
|
|
312
|
+
|
|
313
|
+
Finalization constructs the final `scriptSig` and/or `witness` for each input from the collected partial signatures. After finalization, PSBT metadata no longer needed is cleared.
|
|
314
|
+
|
|
315
|
+
### finalizeAllInputs
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
finalizeAllInputs(): this;
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Finalizes every input. Throws if any input cannot be finalized.
|
|
322
|
+
|
|
323
|
+
### finalizeInput
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
finalizeInput(
|
|
327
|
+
inputIndex: number,
|
|
328
|
+
finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc,
|
|
329
|
+
canRunChecks?: boolean,
|
|
330
|
+
): this;
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Finalizes a specific input. Automatically detects Taproot inputs and routes to the appropriate finalizer.
|
|
334
|
+
|
|
335
|
+
**Parameters:**
|
|
336
|
+
|
|
337
|
+
| Parameter | Type | Default | Description |
|
|
338
|
+
|-----------|------|---------|-------------|
|
|
339
|
+
| `inputIndex` | `number` | (required) | Input index to finalize |
|
|
340
|
+
| `finalScriptsFunc` | `FinalScriptsFunc \| FinalTaprootScriptsFunc` | `getFinalScripts` or `tapScriptFinalizer` | Custom finalization function |
|
|
341
|
+
| `canRunChecks` | `boolean` | `true` | Whether to verify signatures can produce a valid final script |
|
|
342
|
+
|
|
343
|
+
### finalizeTaprootInput
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
finalizeTaprootInput(
|
|
347
|
+
inputIndex: number,
|
|
348
|
+
tapLeafHashToFinalize?: Bytes32,
|
|
349
|
+
finalScriptsFunc?: FinalTaprootScriptsFunc,
|
|
350
|
+
): this;
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Explicitly finalizes a Taproot input. If a `tapKeySig` is present, it creates a key-path spend witness. Otherwise, it uses the `finalScriptsFunc` to construct a script-path spend.
|
|
354
|
+
|
|
355
|
+
**Example:**
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// Finalize all inputs
|
|
359
|
+
psbt.finalizeAllInputs();
|
|
360
|
+
|
|
361
|
+
// Finalize a specific input with custom logic
|
|
362
|
+
psbt.finalizeInput(0, myCustomFinalizer);
|
|
363
|
+
|
|
364
|
+
// Finalize a Taproot input for a specific leaf
|
|
365
|
+
psbt.finalizeTaprootInput(0, leafHash);
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## getFinalScripts and prepareFinalScripts
|
|
371
|
+
|
|
372
|
+
These standalone functions handle the construction of final scripts during finalization.
|
|
373
|
+
|
|
374
|
+
### getFinalScripts
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
function getFinalScripts(
|
|
378
|
+
inputIndex: number,
|
|
379
|
+
input: PsbtInput,
|
|
380
|
+
script: Script,
|
|
381
|
+
isSegwit: boolean,
|
|
382
|
+
isP2SH: boolean,
|
|
383
|
+
isP2WSH: boolean,
|
|
384
|
+
canRunChecks?: boolean,
|
|
385
|
+
solution?: Uint8Array[],
|
|
386
|
+
): {
|
|
387
|
+
finalScriptSig: Script | undefined;
|
|
388
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
389
|
+
};
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
The default finalization function. It classifies the script, checks that enough signatures are present (via `canFinalize`), and delegates to `prepareFinalScripts`.
|
|
393
|
+
|
|
394
|
+
### prepareFinalScripts
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
function prepareFinalScripts(
|
|
398
|
+
script: Uint8Array,
|
|
399
|
+
scriptType: string,
|
|
400
|
+
partialSig: PartialSig[],
|
|
401
|
+
isSegwit: boolean,
|
|
402
|
+
isP2SH: boolean,
|
|
403
|
+
isP2WSH: boolean,
|
|
404
|
+
solution?: Uint8Array[],
|
|
405
|
+
): {
|
|
406
|
+
finalScriptSig: Script | undefined;
|
|
407
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
408
|
+
};
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Constructs the final `scriptSig` and/or `witness` from partial signatures. Handles P2PKH, P2PK, P2WPKH, P2MS (multisig), and nonstandard scripts. Wraps the result in P2SH or P2WSH as needed.
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Extracting
|
|
416
|
+
|
|
417
|
+
### extractTransaction
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
extractTransaction(disableFeeCheck?: boolean, disableOutputChecks?: boolean): Transaction;
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Extracts the final signed `Transaction` object from the PSBT.
|
|
424
|
+
|
|
425
|
+
**Parameters:**
|
|
426
|
+
|
|
427
|
+
| Parameter | Type | Default | Description |
|
|
428
|
+
|-----------|------|---------|-------------|
|
|
429
|
+
| `disableFeeCheck` | `boolean` | `false` | Skip fee rate validation |
|
|
430
|
+
| `disableOutputChecks` | `boolean` | `false` | Skip output amount validation; filters out inputs with partial sigs |
|
|
431
|
+
|
|
432
|
+
**Validation:**
|
|
433
|
+
- All inputs must be finalized (have `finalScriptSig` or `finalScriptWitness`)
|
|
434
|
+
- Unless `disableFeeCheck` is `true`, the fee rate must be below `maximumFeeRate`
|
|
435
|
+
- Unless `disableOutputChecks` is `true`, output amounts must not exceed input amounts
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
const tx = psbt.extractTransaction();
|
|
439
|
+
const txHex = tx.toHex(); // Ready to broadcast
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### getFeeRate / getFee
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
getFeeRate(disableOutputChecks?: boolean): number; // satoshis per vbyte
|
|
446
|
+
getFee(disableOutputChecks?: boolean): number; // total fee in satoshis
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Compute the fee rate and total fee of the finalized PSBT. Both require all inputs to be finalized.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Serialization
|
|
454
|
+
|
|
455
|
+
### toBuffer / toHex / toBase64
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
toBuffer(): Uint8Array;
|
|
459
|
+
toHex(): string;
|
|
460
|
+
toBase64(): string;
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Serializes the PSBT to binary, hex, or base64 format. Throws if the cache is in an unsafe state (e.g., `unsafeSignNonSegwit` was used).
|
|
464
|
+
|
|
465
|
+
### clone
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
clone(): Psbt;
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Creates a deep copy of the PSBT by serializing to buffer and deserializing.
|
|
472
|
+
|
|
473
|
+
### combine
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
combine(...those: Psbt[]): this;
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Merges other PSBTs into this one. All PSBTs must share the same unsigned transaction. The calling PSBT takes precedence on conflicts.
|
|
480
|
+
|
|
481
|
+
**Example:**
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// Serialize for transport
|
|
485
|
+
const base64 = psbt.toBase64();
|
|
486
|
+
|
|
487
|
+
// Deserialize
|
|
488
|
+
const restored = Psbt.fromBase64(base64);
|
|
489
|
+
|
|
490
|
+
// Clone
|
|
491
|
+
const copy = psbt.clone();
|
|
492
|
+
|
|
493
|
+
// Combine partial signatures from multiple signers
|
|
494
|
+
const combined = psbt1.combine(psbt2, psbt3);
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Validation
|
|
500
|
+
|
|
501
|
+
### validateSignaturesOfAllInputs
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean;
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Validates signatures for every input. Returns `true` only if all inputs pass validation.
|
|
508
|
+
|
|
509
|
+
### validateSignaturesOfInput
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
validateSignaturesOfInput(
|
|
513
|
+
inputIndex: number,
|
|
514
|
+
validator: ValidateSigFunction,
|
|
515
|
+
pubkey?: PublicKey,
|
|
516
|
+
): boolean;
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Validates signatures for a specific input. Optionally filters to signatures for a specific pubkey. Automatically handles both standard and Taproot inputs.
|
|
520
|
+
|
|
521
|
+
**ValidateSigFunction:**
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
type ValidateSigFunction = (
|
|
525
|
+
pubkey: PublicKey,
|
|
526
|
+
msghash: MessageHash,
|
|
527
|
+
signature: Uint8Array,
|
|
528
|
+
) => boolean;
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
The `msghash` is a 32-byte hash of the signing preimage. The `signature` is a 64-byte compact signature (r, s, 32 bytes each).
|
|
532
|
+
|
|
533
|
+
**Example:**
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
import * as ecc from 'tiny-secp256k1';
|
|
537
|
+
|
|
538
|
+
const validator = (pubkey, msghash, signature) => {
|
|
539
|
+
return ecc.verify(msghash, pubkey, signature);
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const isValid = psbt.validateSignaturesOfAllInputs(validator);
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### inputHasPubkey / outputHasPubkey
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
inputHasPubkey(inputIndex: number, pubkey: PublicKey): boolean;
|
|
549
|
+
outputHasPubkey(outputIndex: number, pubkey: PublicKey): boolean;
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Checks if a public key is referenced in the script of a specific input or output.
|
|
553
|
+
|
|
554
|
+
### inputHasHDKey / outputHasHDKey
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
inputHasHDKey(inputIndex: number, root: HDSigner): boolean;
|
|
558
|
+
outputHasHDKey(outputIndex: number, root: HDSigner): boolean;
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Checks if an HD key pair's derivation path is present in the BIP 32 derivation data of a specific input or output.
|
|
562
|
+
|
|
563
|
+
### getInputType
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
getInputType(inputIndex: number): AllScriptType;
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Returns the script type of a specific input as a compound string such as `'witnesspubkeyhash'`, `'p2sh-witnesspubkeyhash'`, `'p2wsh-multisig'`, `'p2sh-p2wsh-pubkeyhash'`, etc.
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## Updating
|
|
574
|
+
|
|
575
|
+
### updateInput / updateOutput / updateGlobal
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
updateInput(inputIndex: number, updateData: PsbtInputUpdate): this;
|
|
579
|
+
updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this;
|
|
580
|
+
updateGlobal(updateData: PsbtGlobalUpdate): this;
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Updates metadata on existing inputs, outputs, or global fields. Validates Taproot fields to prevent mixing Taproot and non-Taproot data.
|
|
584
|
+
|
|
585
|
+
### setVersion / setLocktime / setInputSequence
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
setVersion(version: number): this;
|
|
589
|
+
setVersionTRUC(): this; // Sets version to TRUC (v3)
|
|
590
|
+
setLocktime(locktime: number): this;
|
|
591
|
+
setInputSequence(inputIndex: number, sequence: number): this;
|
|
592
|
+
setMaximumFeeRate(satoshiPerByte: number): void;
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
All setters validate that the value is a 32-bit unsigned integer and that existing signatures permit the modification (based on sighash flags).
|
|
596
|
+
|
|
597
|
+
### addUnknownKeyVal
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
addUnknownKeyValToGlobal(keyVal: KeyValue): this;
|
|
601
|
+
addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this;
|
|
602
|
+
addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this;
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Adds proprietary or unknown key-value pairs for custom extensions.
|
|
606
|
+
|
|
607
|
+
### clearFinalizedInput
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
clearFinalizedInput(inputIndex: number): this;
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Clears finalization data from a specific input, allowing re-finalization.
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Signer Interfaces
|
|
618
|
+
|
|
619
|
+
All signer interfaces are re-exported from `@btc-vision/ecpair`.
|
|
620
|
+
|
|
621
|
+
### Signer
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
interface Signer {
|
|
625
|
+
readonly publicKey: PublicKey; // SEC1-encoded (33 or 65 bytes)
|
|
626
|
+
readonly network?: Network | undefined;
|
|
627
|
+
sign(hash: MessageHash, lowR?: boolean): Signature;
|
|
628
|
+
signSchnorr?(hash: MessageHash): SchnorrSignature; // Required for Taproot
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### SignerAsync
|
|
633
|
+
|
|
634
|
+
```typescript
|
|
635
|
+
interface SignerAsync {
|
|
636
|
+
readonly publicKey: PublicKey;
|
|
637
|
+
readonly network?: Network | undefined;
|
|
638
|
+
sign(hash: MessageHash, lowR?: boolean): Promise<Signature>;
|
|
639
|
+
signSchnorr?(hash: MessageHash): Promise<SchnorrSignature>;
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### HDSigner
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
interface HDSigner {
|
|
647
|
+
readonly publicKey: PublicKey;
|
|
648
|
+
readonly fingerprint: Uint8Array; // First 4 bytes of hash160(publicKey)
|
|
649
|
+
derivePath(path: string): HDSigner;
|
|
650
|
+
sign(hash: MessageHash): Uint8Array;
|
|
651
|
+
}
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### HDSignerAsync
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
interface HDSignerAsync {
|
|
658
|
+
readonly publicKey: PublicKey;
|
|
659
|
+
readonly fingerprint: Uint8Array;
|
|
660
|
+
derivePath(path: string): HDSignerAsync;
|
|
661
|
+
sign(hash: MessageHash): Promise<Uint8Array>;
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### TaprootHashCheckSigner
|
|
666
|
+
|
|
667
|
+
A minimal signer interface used by `checkTaprootHashesForSig`. It only requires `publicKey` and optionally `signSchnorr`.
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
interface TaprootHashCheckSigner {
|
|
671
|
+
readonly publicKey: Uint8Array;
|
|
672
|
+
signSchnorr?(hash: Uint8Array): Uint8Array | Promise<Uint8Array>;
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## Options and Configuration
|
|
679
|
+
|
|
680
|
+
### PsbtOpts
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
interface PsbtOpts {
|
|
684
|
+
readonly network: Network;
|
|
685
|
+
maximumFeeRate: number; // Satoshis per byte; default 5000
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### PsbtOptsOptional
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
interface PsbtOptsOptional {
|
|
693
|
+
readonly network?: Network | undefined;
|
|
694
|
+
readonly maximumFeeRate?: number | undefined;
|
|
695
|
+
readonly version?: 1 | 2 | 3 | undefined; // 3 = TRUC version
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**Default options:**
|
|
700
|
+
|
|
701
|
+
```typescript
|
|
702
|
+
const DEFAULT_OPTS: PsbtOpts = {
|
|
703
|
+
network: bitcoin, // mainnet
|
|
704
|
+
maximumFeeRate: 5000, // 5000 sat/vB
|
|
705
|
+
};
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Script Type Detection
|
|
711
|
+
|
|
712
|
+
The `psbtutils` module provides factory functions for detecting script types. Each returns a boolean.
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
import {
|
|
716
|
+
isP2MS, // Pay-to-Multisig
|
|
717
|
+
isP2PK, // Pay-to-PubKey
|
|
718
|
+
isP2PKH, // Pay-to-PubKeyHash
|
|
719
|
+
isP2WPKH, // Pay-to-Witness-PubKeyHash
|
|
720
|
+
isP2WSHScript,// Pay-to-Witness-ScriptHash
|
|
721
|
+
isP2SHScript, // Pay-to-ScriptHash
|
|
722
|
+
isP2TR, // Pay-to-Taproot (BIP 341)
|
|
723
|
+
isP2MR, // Pay-to-Merkle-Root (BIP 360)
|
|
724
|
+
isP2OP, // Pay-to-OP_NET
|
|
725
|
+
isP2A, // Pay-to-Anchor
|
|
726
|
+
} from '@btc-vision/bitcoin';
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### isP2A (Pay-to-Anchor)
|
|
730
|
+
|
|
731
|
+
P2A is detected by a fixed 4-byte pattern:
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
OP_1 (0x51) PUSH2 (0x02) 0x4e 0x73
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### classifyScript
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
function classifyScript(script: Uint8Array): ScriptType;
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Returns one of: `'witnesspubkeyhash'`, `'pubkeyhash'`, `'multisig'`, `'pubkey'`, `'nonstandard'`.
|
|
744
|
+
|
|
745
|
+
### AllScriptType
|
|
746
|
+
|
|
747
|
+
The complete union of compound script types returned by `getInputType()`:
|
|
748
|
+
|
|
749
|
+
```typescript
|
|
750
|
+
type AllScriptType =
|
|
751
|
+
| 'witnesspubkeyhash'
|
|
752
|
+
| 'pubkeyhash'
|
|
753
|
+
| 'multisig'
|
|
754
|
+
| 'pubkey'
|
|
755
|
+
| 'nonstandard'
|
|
756
|
+
| 'p2sh-witnesspubkeyhash'
|
|
757
|
+
| 'p2sh-pubkeyhash'
|
|
758
|
+
| 'p2sh-multisig'
|
|
759
|
+
| 'p2sh-pubkey'
|
|
760
|
+
| 'p2sh-nonstandard'
|
|
761
|
+
| 'p2wsh-pubkeyhash'
|
|
762
|
+
| 'p2wsh-multisig'
|
|
763
|
+
| 'p2wsh-pubkey'
|
|
764
|
+
| 'p2wsh-nonstandard'
|
|
765
|
+
| 'p2sh-p2wsh-pubkeyhash'
|
|
766
|
+
| 'p2sh-p2wsh-multisig'
|
|
767
|
+
| 'p2sh-p2wsh-pubkey'
|
|
768
|
+
| 'p2sh-p2wsh-nonstandard';
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
## BIP 371 Taproot Extensions
|
|
774
|
+
|
|
775
|
+
The `bip371` module provides Taproot-specific utilities for PSBT construction and finalization.
|
|
776
|
+
|
|
777
|
+
### isTaprootInput
|
|
778
|
+
|
|
779
|
+
```typescript
|
|
780
|
+
function isTaprootInput(input: PsbtInput): boolean;
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
Returns `true` if the input has any Taproot-specific fields: `tapInternalKey`, `tapMerkleRoot`, `tapLeafScript`, `tapBip32Derivation`, or a P2TR or P2MR `witnessUtxo` script.
|
|
784
|
+
|
|
785
|
+
### isTaprootOutput
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
function isTaprootOutput(output: PsbtOutput, script?: Uint8Array): boolean;
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
Returns `true` if the output has Taproot-specific fields: `tapInternalKey`, `tapTree`, `tapBip32Derivation`, or a P2TR or P2MR script.
|
|
792
|
+
|
|
793
|
+
### isP2MRInput
|
|
794
|
+
|
|
795
|
+
```typescript
|
|
796
|
+
function isP2MRInput(input: PsbtInput): boolean;
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
Returns `true` if the input specifically has a P2MR (Pay-to-Merkle-Root) `witnessUtxo` script. Unlike `isTaprootInput` which matches both P2TR and P2MR, this function only matches P2MR inputs.
|
|
800
|
+
|
|
801
|
+
### checkTaprootInputForSigs
|
|
802
|
+
|
|
803
|
+
```typescript
|
|
804
|
+
function checkTaprootInputForSigs(input: PsbtInput, action: string): boolean;
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
Checks whether a Taproot input has existing signatures (from `tapKeySig`, `tapScriptSig`, or `finalScriptWitness`) that would block the given action. Extracts all Taproot signatures from the input and checks each against the action using Schnorr signature decoding.
|
|
808
|
+
|
|
809
|
+
### checkTaprootHashesForSig (Psbt method)
|
|
810
|
+
|
|
811
|
+
```typescript
|
|
812
|
+
checkTaprootHashesForSig(
|
|
813
|
+
inputIndex: number,
|
|
814
|
+
input: PsbtInput,
|
|
815
|
+
keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync | TaprootHashCheckSigner,
|
|
816
|
+
tapLeafHashToSign?: Uint8Array,
|
|
817
|
+
allowedSighashTypes?: number[],
|
|
818
|
+
): { hash: MessageHash; leafHash?: Bytes32 }[];
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
A public method on the `Psbt` class. Validates that the key pair has a `signSchnorr` method and retrieves all Taproot hashes that need to be signed for the given input and key pair. Throws if no hashes are found for the provided key. Used internally by Taproot signing methods and can be called directly for advanced use cases.
|
|
822
|
+
|
|
823
|
+
### checkTaprootInputFields
|
|
824
|
+
|
|
825
|
+
```typescript
|
|
826
|
+
function checkTaprootInputFields(
|
|
827
|
+
inputData: PsbtInput,
|
|
828
|
+
newInputData: PsbtInput,
|
|
829
|
+
action: string,
|
|
830
|
+
): void;
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
Validates that Taproot and non-Taproot fields are not mixed in the same input. Also verifies that any `tapLeafScript` entries belong to the tap tree (if a `tapMerkleRoot` is provided). Throws on validation failure.
|
|
834
|
+
|
|
835
|
+
### checkTaprootOutputFields
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
function checkTaprootOutputFields(
|
|
839
|
+
outputData: PsbtOutput,
|
|
840
|
+
newOutputData: PsbtOutput,
|
|
841
|
+
action: string,
|
|
842
|
+
): void;
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
Validates that Taproot and non-Taproot fields are not mixed in the same output. Also verifies that the `tapInternalKey` and `tapTree` produce a script pubkey matching the output script.
|
|
846
|
+
|
|
847
|
+
### serializeTaprootSignature
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
function serializeTaprootSignature(sig: Uint8Array, sighashType?: number): Uint8Array;
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
Serializes a Schnorr signature with an optional sighash type byte appended. If `sighashType` is `SIGHASH_DEFAULT` (0) or undefined, no extra byte is appended (the signature is 64 bytes). Otherwise the sighash byte is appended (65 bytes).
|
|
854
|
+
|
|
855
|
+
### tapScriptFinalizer
|
|
856
|
+
|
|
857
|
+
```typescript
|
|
858
|
+
function tapScriptFinalizer(
|
|
859
|
+
inputIndex: number,
|
|
860
|
+
input: PsbtInput,
|
|
861
|
+
tapLeafHashToFinalize?: Uint8Array,
|
|
862
|
+
): { finalScriptWitness: Uint8Array | undefined };
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
The default Taproot script-path finalizer. It:
|
|
866
|
+
|
|
867
|
+
1. Searches for a tap leaf matching `tapLeafHashToFinalize` (if provided)
|
|
868
|
+
2. Otherwise selects the signed tap leaf with the shortest control block (shortest Merkle proof path)
|
|
869
|
+
3. Sorts signatures by their public key position in the leaf script (reverse order for OP_CHECKSIGADD compatibility)
|
|
870
|
+
4. Constructs the witness: `[...signatures, script, controlBlock]`
|
|
871
|
+
|
|
872
|
+
### tweakInternalPubKey
|
|
873
|
+
|
|
874
|
+
```typescript
|
|
875
|
+
function tweakInternalPubKey(inputIndex: number, input: PsbtInput): Uint8Array;
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Tweaks the `tapInternalKey` with the `tapMerkleRoot` to produce the output key. Used internally during Taproot signing.
|
|
879
|
+
|
|
880
|
+
### tapTreeToList / tapTreeFromList
|
|
881
|
+
|
|
882
|
+
```typescript
|
|
883
|
+
function tapTreeToList(tree: Taptree): TapLeaf[];
|
|
884
|
+
function tapTreeFromList(leaves?: TapLeaf[]): Taptree;
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
Converts between the binary `Taptree` structure and the flat `TapLeaf[]` list format (BIP 371). The list format uses depth-first search ordering for tree reconstruction.
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
## FinalScriptsFunc and FinalTaprootScriptsFunc
|
|
892
|
+
|
|
893
|
+
These function types allow custom finalization logic to be injected.
|
|
894
|
+
|
|
895
|
+
### FinalScriptsFunc
|
|
896
|
+
|
|
897
|
+
```typescript
|
|
898
|
+
type FinalScriptsFunc = (
|
|
899
|
+
inputIndex: number,
|
|
900
|
+
input: PsbtInput,
|
|
901
|
+
script: Script,
|
|
902
|
+
isSegwit: boolean,
|
|
903
|
+
isP2SH: boolean,
|
|
904
|
+
isP2WSH: boolean,
|
|
905
|
+
canRunChecks: boolean,
|
|
906
|
+
) => {
|
|
907
|
+
finalScriptSig: Script | undefined;
|
|
908
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
909
|
+
};
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
Used for non-Taproot inputs. Receives the classified script and wrapping flags. Must return either `finalScriptSig`, `finalScriptWitness`, or both.
|
|
913
|
+
|
|
914
|
+
### FinalTaprootScriptsFunc
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
type FinalTaprootScriptsFunc = (
|
|
918
|
+
inputIndex: number,
|
|
919
|
+
input: PsbtInput,
|
|
920
|
+
tapLeafHashToFinalize?: Bytes32,
|
|
921
|
+
) => {
|
|
922
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
923
|
+
};
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
Used for Taproot inputs. Must construct and return the `finalScriptWitness`.
|
|
927
|
+
|
|
928
|
+
---
|
|
929
|
+
|
|
930
|
+
## witnessStackToScriptWitness
|
|
931
|
+
|
|
932
|
+
```typescript
|
|
933
|
+
function witnessStackToScriptWitness(witness: Uint8Array[]): Uint8Array;
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
Converts a witness stack (array of `Uint8Array` items) into the serialized script witness format used in `finalScriptWitness`. The format is:
|
|
937
|
+
|
|
938
|
+
```
|
|
939
|
+
[varint:numItems] [varint:item1Len] [item1] [varint:item2Len] [item2] ...
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
The inverse operation is `scriptWitnessToWitnessStack`:
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
function scriptWitnessToWitnessStack(buffer: Uint8Array): Uint8Array[];
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
---
|
|
949
|
+
|
|
950
|
+
## Internal Architecture
|
|
951
|
+
|
|
952
|
+
### PsbtCache
|
|
953
|
+
|
|
954
|
+
The `PsbtCache` class manages all computed and cached values for a PSBT instance.
|
|
955
|
+
|
|
956
|
+
```typescript
|
|
957
|
+
class PsbtCache {
|
|
958
|
+
readonly nonWitnessUtxoTxCache: Transaction[]; // Cached decoded non-witness transactions
|
|
959
|
+
readonly nonWitnessUtxoBufCache: Uint8Array[]; // Cached raw non-witness transaction buffers
|
|
960
|
+
readonly txInCache: Record<string, number>; // Cached transaction input lookups
|
|
961
|
+
readonly tx: Transaction;
|
|
962
|
+
unsafeSignNonSegwit: boolean; // Flag for unsafe non-segwit signing
|
|
963
|
+
hasSignatures: boolean;
|
|
964
|
+
fee: number | undefined;
|
|
965
|
+
feeRate: number | undefined;
|
|
966
|
+
extractedTx: Transaction | undefined;
|
|
967
|
+
prevOuts: readonly PrevOut[] | undefined; // Cached for Taproot signing
|
|
968
|
+
signingScripts: readonly Script[] | undefined; // Cached for Taproot signing
|
|
969
|
+
values: readonly Satoshi[] | undefined; // Cached for Taproot signing
|
|
970
|
+
taprootHashCache: TaprootHashCache | undefined; // Cached intermediate hashes
|
|
971
|
+
|
|
972
|
+
constructor(tx: Transaction);
|
|
973
|
+
invalidate(scope: 'full' | 'outputs'): void;
|
|
974
|
+
addNonWitnessTxCache(input, inputIndex, txFromBuffer): void;
|
|
975
|
+
getNonWitnessUtxoTx(input, inputIndex, txFromBuffer): Transaction;
|
|
976
|
+
getScriptFromUtxo(inputIndex, input, txFromBuffer): Script;
|
|
977
|
+
getScriptAndAmountFromUtxo(inputIndex, input, txFromBuffer): { script, value };
|
|
978
|
+
computeFee(inputs, disableOutputChecks?, txFromBuffer?): number;
|
|
979
|
+
computeFeeRate(inputs, disableOutputChecks?, txFromBuffer?): number;
|
|
980
|
+
checkFees(opts): void;
|
|
981
|
+
pubkeyInInput(pubkey, input, inputIndex, txFromBuffer): boolean;
|
|
982
|
+
pubkeyInOutput(pubkey, output, outputIndex): boolean;
|
|
983
|
+
redeemFromFinalScriptSig(finalScript): Uint8Array | undefined;
|
|
984
|
+
redeemFromFinalWitnessScript(finalScript): Uint8Array | undefined;
|
|
985
|
+
finalizeAndComputeAmounts(inputs, tx, mustFinalize, disableOutputChecks?, txFromBuffer?): { fee, feeRate };
|
|
986
|
+
getScriptFromInput(inputIndex, input, txFromBuffer): GetScriptReturn;
|
|
987
|
+
getPrevoutTaprootKey(inputIndex, input, txFromBuffer): XOnlyPublicKey | null;
|
|
988
|
+
}
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
**Cache invalidation scopes:**
|
|
992
|
+
- `'full'`: Clears everything including `prevOuts`, `signingScripts`, and `values` (used when inputs change)
|
|
993
|
+
- `'outputs'`: Clears `fee`, `feeRate`, `extractedTx`, and `taprootHashCache` (used when outputs or version/locktime change)
|
|
994
|
+
|
|
995
|
+
### PsbtSigner
|
|
996
|
+
|
|
997
|
+
The `PsbtSigner` class wraps all signing-related logic.
|
|
998
|
+
|
|
999
|
+
```typescript
|
|
1000
|
+
class PsbtSigner {
|
|
1001
|
+
getHashAndSighashType(inputs, inputIndex, pubkey, sighashTypes): { hash, sighashType };
|
|
1002
|
+
getHashForSig(inputIndex, input, forValidate, sighashTypes?): { script, hash, sighashType };
|
|
1003
|
+
getTaprootHashesForSig(inputIndex, input, inputs, pubkey, tapLeafHashToSign?, allowedSighashTypes?): HashForSig[];
|
|
1004
|
+
getAllTaprootHashesForSig(inputIndex, input, inputs): HashForSig[];
|
|
1005
|
+
trimTaprootSig(signature): Uint8Array; // Trims 65-byte sig to 64 bytes
|
|
1006
|
+
getSignersFromHD(inputIndex, inputs, hdKeyPair): HDSigner[];
|
|
1007
|
+
bip32DerivationIsMine(root): (d: Bip32Derivation) => boolean;
|
|
1008
|
+
}
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
### PsbtFinalizer
|
|
1012
|
+
|
|
1013
|
+
The `PsbtFinalizer` class wraps finalization logic.
|
|
1014
|
+
|
|
1015
|
+
```typescript
|
|
1016
|
+
class PsbtFinalizer {
|
|
1017
|
+
getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH, canRunChecks?, solution?): FinalScriptsResult;
|
|
1018
|
+
getScriptFromInput(inputIndex, input): GetScriptReturn;
|
|
1019
|
+
}
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
### PsbtTransaction
|
|
1023
|
+
|
|
1024
|
+
Implements the `ITransaction` interface required by the `bip174` library. Wraps a `Transaction` object.
|
|
1025
|
+
|
|
1026
|
+
```typescript
|
|
1027
|
+
class PsbtTransaction {
|
|
1028
|
+
tx: Transaction;
|
|
1029
|
+
constructor(buffer?: Uint8Array);
|
|
1030
|
+
getInputOutputCounts(): { inputCount: number; outputCount: number };
|
|
1031
|
+
addInput(input: TransactionInput): void;
|
|
1032
|
+
addOutput(output: PsbtTxOutput): void;
|
|
1033
|
+
toBuffer(): Uint8Array;
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
When no buffer is provided, a default empty version-2 transaction is used: `[02 00 00 00] [00] [00] [00 00 00 00]`.
|
|
1038
|
+
|
|
1039
|
+
---
|
|
1040
|
+
|
|
1041
|
+
## Validation Utilities
|
|
1042
|
+
|
|
1043
|
+
### check32Bit
|
|
1044
|
+
|
|
1045
|
+
```typescript
|
|
1046
|
+
function check32Bit(num: number): void;
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
Validates that a number is a non-negative 32-bit integer. Throws on invalid values.
|
|
1050
|
+
|
|
1051
|
+
### isFinalized
|
|
1052
|
+
|
|
1053
|
+
```typescript
|
|
1054
|
+
function isFinalized(input: PsbtInput): boolean;
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
Returns `true` if the input has either `finalScriptSig` or `finalScriptWitness`.
|
|
1058
|
+
|
|
1059
|
+
### checkInputsForPartialSig
|
|
1060
|
+
|
|
1061
|
+
```typescript
|
|
1062
|
+
function checkInputsForPartialSig(
|
|
1063
|
+
inputs: PsbtInput[],
|
|
1064
|
+
action: string,
|
|
1065
|
+
hasSignaturesCache?: boolean,
|
|
1066
|
+
): void;
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
Checks if existing signatures would be invalidated by the specified action (e.g., `'addInput'`, `'addOutput'`, `'setVersion'`). Uses the `hasSignatures` cache flag for O(1) fast-path when no signatures exist.
|
|
1070
|
+
|
|
1071
|
+
Sighash-based logic:
|
|
1072
|
+
- `SIGHASH_ANYONECANPAY`: allows `addInput`
|
|
1073
|
+
- `SIGHASH_NONE` / `SIGHASH_SINGLE`: allows `addOutput` and `setInputSequence`
|
|
1074
|
+
- `SIGHASH_ALL`: does not allow any modification
|
|
1075
|
+
|
|
1076
|
+
### checkScriptForPubkey
|
|
1077
|
+
|
|
1078
|
+
```typescript
|
|
1079
|
+
function checkScriptForPubkey(pubkey: PublicKey, script: Script, action: string): void;
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
Throws if the public key is not found in the script. Used during signing to prevent signing with the wrong key.
|
|
1083
|
+
|
|
1084
|
+
### checkPartialSigSighashes
|
|
1085
|
+
|
|
1086
|
+
```typescript
|
|
1087
|
+
function checkPartialSigSighashes(input: PsbtInput): void;
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
Validates that all partial signatures in an input have sighash types matching the input's `sighashType` field.
|
|
1091
|
+
|
|
1092
|
+
### checkTxForDupeIns
|
|
1093
|
+
|
|
1094
|
+
```typescript
|
|
1095
|
+
function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void;
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
Checks all transaction inputs for duplicates (same TXID and output index).
|
|
1099
|
+
|
|
1100
|
+
### checkRedeemScript / checkWitnessScript
|
|
1101
|
+
|
|
1102
|
+
```typescript
|
|
1103
|
+
const checkRedeemScript: (idx, scriptPubKey, redeemScript, ioType) => void;
|
|
1104
|
+
const checkWitnessScript: (idx, scriptPubKey, witnessScript, ioType) => void;
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
Validate that a redeem or witness script produces the expected scriptPubKey when wrapped in P2SH or P2WSH.
|
|
1108
|
+
|
|
1109
|
+
---
|
|
1110
|
+
|
|
1111
|
+
## Utility Functions
|
|
1112
|
+
|
|
1113
|
+
### pubkeyPositionInScript
|
|
1114
|
+
|
|
1115
|
+
```typescript
|
|
1116
|
+
function pubkeyPositionInScript(pubkey: Uint8Array, script: Uint8Array): number;
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
Finds the position of a public key in a script. Checks against the raw pubkey, its x-only form, its hash160, uncompressed form, and hybrid form. Returns `-1` if not found.
|
|
1120
|
+
|
|
1121
|
+
### pubkeyInScript
|
|
1122
|
+
|
|
1123
|
+
```typescript
|
|
1124
|
+
function pubkeyInScript(pubkey: Uint8Array, script: Uint8Array): boolean;
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
Returns `true` if the public key is found in the script (delegates to `pubkeyPositionInScript`).
|
|
1128
|
+
|
|
1129
|
+
### checkInputForSig
|
|
1130
|
+
|
|
1131
|
+
```typescript
|
|
1132
|
+
function checkInputForSig(input: PsbtInput, action: string): boolean;
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
Checks whether an input already has a signature that would block the given action.
|
|
1136
|
+
|
|
1137
|
+
### getMeaningfulScript
|
|
1138
|
+
|
|
1139
|
+
```typescript
|
|
1140
|
+
function getMeaningfulScript(
|
|
1141
|
+
script: Uint8Array,
|
|
1142
|
+
index: number,
|
|
1143
|
+
ioType: 'input' | 'output',
|
|
1144
|
+
redeemScript?: Uint8Array,
|
|
1145
|
+
witnessScript?: Uint8Array,
|
|
1146
|
+
): {
|
|
1147
|
+
meaningfulScript: Uint8Array;
|
|
1148
|
+
type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw';
|
|
1149
|
+
};
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
Unwraps P2SH, P2WSH, and P2SH-P2WSH scripts to reveal the underlying meaningful script. Validates that redeem and witness scripts match the scriptPubKey.
|
|
1153
|
+
|
|
1154
|
+
### sighashTypeToString
|
|
1155
|
+
|
|
1156
|
+
```typescript
|
|
1157
|
+
function sighashTypeToString(sighashType: number): string;
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
Converts a sighash type integer to a human-readable string like `'SIGHASH_ALL'`, `'SIGHASH_ANYONECANPAY | SIGHASH_SINGLE'`, etc.
|
|
1161
|
+
|
|
1162
|
+
### compressPubkey
|
|
1163
|
+
|
|
1164
|
+
```typescript
|
|
1165
|
+
function compressPubkey(pubkey: Uint8Array): Uint8Array;
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
Compresses a 65-byte uncompressed public key to 33 bytes. Returns a copy if already compressed.
|
|
1169
|
+
|
|
1170
|
+
### range
|
|
1171
|
+
|
|
1172
|
+
```typescript
|
|
1173
|
+
function range(n: number): number[];
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
Creates an array `[0, 1, 2, ..., n-1]`.
|
|
1177
|
+
|
|
1178
|
+
---
|
|
1179
|
+
|
|
1180
|
+
## Complete Workflow Examples
|
|
1181
|
+
|
|
1182
|
+
### Standard P2WPKH Transaction
|
|
1183
|
+
|
|
1184
|
+
```typescript
|
|
1185
|
+
import { Psbt, networks } from '@btc-vision/bitcoin';
|
|
1186
|
+
import { ECPairSigner, createNobleBackend } from '@btc-vision/ecpair';
|
|
1187
|
+
|
|
1188
|
+
const backend = createNobleBackend();
|
|
1189
|
+
|
|
1190
|
+
// 1. Create
|
|
1191
|
+
const psbt = new Psbt({ network: networks.bitcoin });
|
|
1192
|
+
|
|
1193
|
+
// 2. Add inputs
|
|
1194
|
+
psbt.addInput({
|
|
1195
|
+
hash: 'previousTxId...', // TXID hex string
|
|
1196
|
+
index: 0,
|
|
1197
|
+
witnessUtxo: {
|
|
1198
|
+
script: Buffer.from('0014ab68025513c3dbd2f7b92a94e0581f5d50f654e7', 'hex'),
|
|
1199
|
+
value: 100000n,
|
|
1200
|
+
},
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// 3. Add outputs
|
|
1204
|
+
psbt.addOutput({
|
|
1205
|
+
address: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
|
|
1206
|
+
value: 90000n,
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
// 4. Sign
|
|
1210
|
+
const keyPair = ECPairSigner.fromWIF(backend, 'L1...', networks.bitcoin);
|
|
1211
|
+
psbt.signAllInputs(keyPair);
|
|
1212
|
+
|
|
1213
|
+
// 5. Validate (recommended before finalizing)
|
|
1214
|
+
const validator = (pubkey, msghash, signature) => {
|
|
1215
|
+
return keyPair.verify(msghash, signature);
|
|
1216
|
+
};
|
|
1217
|
+
psbt.validateSignaturesOfAllInputs(validator);
|
|
1218
|
+
|
|
1219
|
+
// 6. Finalize
|
|
1220
|
+
psbt.finalizeAllInputs();
|
|
1221
|
+
|
|
1222
|
+
// 7. Extract
|
|
1223
|
+
const tx = psbt.extractTransaction();
|
|
1224
|
+
const rawTx = tx.toHex(); // Ready to broadcast
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
### Taproot Key-Path Spend
|
|
1228
|
+
|
|
1229
|
+
```typescript
|
|
1230
|
+
import { Psbt } from '@btc-vision/bitcoin';
|
|
1231
|
+
|
|
1232
|
+
const psbt = new Psbt();
|
|
1233
|
+
|
|
1234
|
+
psbt.addInput({
|
|
1235
|
+
hash: 'previousTxId...',
|
|
1236
|
+
index: 0,
|
|
1237
|
+
witnessUtxo: {
|
|
1238
|
+
script: taprootOutputScript, // OP_1 <32-byte x-only pubkey>
|
|
1239
|
+
value: 50000n,
|
|
1240
|
+
},
|
|
1241
|
+
tapInternalKey: internalPubkeyXOnly, // 32-byte x-only public key
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
psbt.addOutput({
|
|
1245
|
+
address: 'bc1p...',
|
|
1246
|
+
value: 40000n,
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
// Sign with Schnorr-capable signer
|
|
1250
|
+
psbt.signInput(0, taprootKeyPair); // Uses SIGHASH_DEFAULT by default
|
|
1251
|
+
psbt.finalizeInput(0);
|
|
1252
|
+
|
|
1253
|
+
const tx = psbt.extractTransaction();
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
### Taproot Script-Path Spend
|
|
1257
|
+
|
|
1258
|
+
```typescript
|
|
1259
|
+
import { Psbt } from '@btc-vision/bitcoin';
|
|
1260
|
+
|
|
1261
|
+
const psbt = new Psbt();
|
|
1262
|
+
|
|
1263
|
+
psbt.addInput({
|
|
1264
|
+
hash: 'previousTxId...',
|
|
1265
|
+
index: 0,
|
|
1266
|
+
witnessUtxo: {
|
|
1267
|
+
script: taprootOutputScript,
|
|
1268
|
+
value: 50000n,
|
|
1269
|
+
},
|
|
1270
|
+
tapInternalKey: internalPubkeyXOnly,
|
|
1271
|
+
tapMerkleRoot: merkleRoot,
|
|
1272
|
+
tapLeafScript: [
|
|
1273
|
+
{
|
|
1274
|
+
controlBlock: controlBlockBuffer,
|
|
1275
|
+
script: leafScriptBuffer,
|
|
1276
|
+
leafVersion: 0xc0, // LEAF_VERSION_TAPSCRIPT
|
|
1277
|
+
},
|
|
1278
|
+
],
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
psbt.addOutput({
|
|
1282
|
+
address: 'bc1q...',
|
|
1283
|
+
value: 40000n,
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
// Sign the specific tap leaf
|
|
1287
|
+
psbt.signTaprootInput(0, taprootKeyPair, leafHash);
|
|
1288
|
+
|
|
1289
|
+
// Finalize using the default tap script finalizer
|
|
1290
|
+
psbt.finalizeTaprootInput(0);
|
|
1291
|
+
|
|
1292
|
+
const tx = psbt.extractTransaction();
|
|
1293
|
+
```
|
|
1294
|
+
|
|
1295
|
+
### Multi-Party Signing (PSBT Combiner Pattern)
|
|
1296
|
+
|
|
1297
|
+
```typescript
|
|
1298
|
+
import { Psbt } from '@btc-vision/bitcoin';
|
|
1299
|
+
|
|
1300
|
+
// Coordinator creates the PSBT
|
|
1301
|
+
const psbt = new Psbt();
|
|
1302
|
+
psbt.addInput({ hash: '...', index: 0, witnessUtxo: { script: p2wshScript, value: 100000n } });
|
|
1303
|
+
psbt.addOutput({ address: 'bc1q...', value: 90000n });
|
|
1304
|
+
|
|
1305
|
+
// Serialize and send to signers
|
|
1306
|
+
const psbtBase64 = psbt.toBase64();
|
|
1307
|
+
|
|
1308
|
+
// Signer A
|
|
1309
|
+
const psbtA = Psbt.fromBase64(psbtBase64);
|
|
1310
|
+
psbtA.signInput(0, signerA);
|
|
1311
|
+
|
|
1312
|
+
// Signer B
|
|
1313
|
+
const psbtB = Psbt.fromBase64(psbtBase64);
|
|
1314
|
+
psbtB.signInput(0, signerB);
|
|
1315
|
+
|
|
1316
|
+
// Coordinator combines
|
|
1317
|
+
const combined = Psbt.fromBase64(psbtBase64);
|
|
1318
|
+
combined.combine(psbtA, psbtB);
|
|
1319
|
+
|
|
1320
|
+
// Finalize and extract
|
|
1321
|
+
combined.finalizeAllInputs();
|
|
1322
|
+
const tx = combined.extractTransaction();
|
|
1323
|
+
```
|
|
1324
|
+
|
|
1325
|
+
### Async Signing with Hardware Wallet
|
|
1326
|
+
|
|
1327
|
+
```typescript
|
|
1328
|
+
import { Psbt } from '@btc-vision/bitcoin';
|
|
1329
|
+
|
|
1330
|
+
const psbt = new Psbt();
|
|
1331
|
+
// ... add inputs and outputs ...
|
|
1332
|
+
|
|
1333
|
+
// Hardware wallet implements SignerAsync
|
|
1334
|
+
const hardwareSigner: SignerAsync = {
|
|
1335
|
+
publicKey: hwPublicKey,
|
|
1336
|
+
async sign(hash) {
|
|
1337
|
+
// Communicate with hardware device
|
|
1338
|
+
return await hardwareDevice.signECDSA(hash);
|
|
1339
|
+
},
|
|
1340
|
+
async signSchnorr(hash) {
|
|
1341
|
+
return await hardwareDevice.signSchnorr(hash);
|
|
1342
|
+
},
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
await psbt.signAllInputsAsync(hardwareSigner);
|
|
1346
|
+
psbt.finalizeAllInputs();
|
|
1347
|
+
const tx = psbt.extractTransaction();
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
### Custom Finalization Function
|
|
1351
|
+
|
|
1352
|
+
```typescript
|
|
1353
|
+
import { Psbt, type FinalScriptsFunc } from '@btc-vision/bitcoin';
|
|
1354
|
+
|
|
1355
|
+
const customFinalizer: FinalScriptsFunc = (
|
|
1356
|
+
inputIndex, input, script, isSegwit, isP2SH, isP2WSH, canRunChecks
|
|
1357
|
+
) => {
|
|
1358
|
+
// Custom logic to construct final scripts
|
|
1359
|
+
const witness = [input.partialSig[0].signature, input.partialSig[0].pubkey];
|
|
1360
|
+
return {
|
|
1361
|
+
finalScriptSig: undefined,
|
|
1362
|
+
finalScriptWitness: witnessStackToScriptWitness(witness),
|
|
1363
|
+
};
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1366
|
+
psbt.finalizeInput(0, customFinalizer);
|
|
1367
|
+
```
|
|
1368
|
+
|
|
1369
|
+
---
|
|
1370
|
+
|
|
1371
|
+
## Error Handling
|
|
1372
|
+
|
|
1373
|
+
Common errors thrown by the PSBT module:
|
|
1374
|
+
|
|
1375
|
+
| Error | Cause |
|
|
1376
|
+
|-------|-------|
|
|
1377
|
+
| `"Invalid arguments for Psbt.addInput"` | Missing `hash` or `index` |
|
|
1378
|
+
| `"Invalid arguments for Psbt.addOutput"` | Missing `value` or both `address` and `script` |
|
|
1379
|
+
| `"Can not modify transaction, signatures exist."` | Attempting to add input/output after signing with `SIGHASH_ALL` |
|
|
1380
|
+
| `"Need Signer to sign input"` | Null or missing key pair |
|
|
1381
|
+
| `"Need Schnorr Signer to sign taproot input"` | Signer lacks `signSchnorr` method |
|
|
1382
|
+
| `"No inputs were signed"` | Key pair did not match any input |
|
|
1383
|
+
| `"Not finalized"` | Calling `extractTransaction` before finalizing |
|
|
1384
|
+
| `"Can not finalize input #N"` | Missing required signatures for the script type |
|
|
1385
|
+
| `"Duplicate input detected."` | Same TXID:vout added twice |
|
|
1386
|
+
| `"Warning: You are paying around X in fees..."` | Fee rate exceeds `maximumFeeRate` |
|
|
1387
|
+
| `"Not BIP174 compliant, can not export"` | Attempting to serialize after `unsafeSignNonSegwit` |
|
|
1388
|
+
| `"Cannot use both taproot and non-taproot fields."` | Mixing Taproot and legacy fields in the same input/output |
|
|
1389
|
+
| `"Sighash type is not allowed."` | Input sighash type not in the allowed list |
|
|
1390
|
+
| `"P2WPKH or P2SH can not be contained within P2WSH"` | Invalid witness script wrapping |
|
|
1391
|
+
|
|
1392
|
+
---
|
|
1393
|
+
|
|
1394
|
+
## Security Considerations
|
|
1395
|
+
|
|
1396
|
+
- **Always validate signatures** before finalizing with `validateSignaturesOfAllInputs()`. The finalizer constructs scripts from partial data and does not re-validate cryptographic correctness.
|
|
1397
|
+
- **Fee rate protection**: The default `maximumFeeRate` of 5000 sat/vB prevents accidental overpayment. Adjust with `setMaximumFeeRate()` if higher rates are intentional.
|
|
1398
|
+
- **Non-segwit signing risk**: Signing non-segwit inputs with only `witnessUtxo` (no `nonWitnessUtxo`) is unsafe because a malicious node could lie about the input amount. The library blocks this by default; the `unsafeSignNonSegwit` flag bypasses this check but prevents BIP 174 serialization.
|
|
1399
|
+
- **Sighash type whitelisting**: Pass explicit `sighashTypes` arrays to signing methods to prevent unintended sighash usage.
|
|
1400
|
+
- **Duplicate input detection**: The library automatically checks for duplicate inputs (same TXID and output index) to prevent double-spend construction errors.
|