@ledgerhq/hw-app-btc 6.2.0 → 6.9.1-6.9.1-taproot.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Btc.d.ts +7 -3
- package/lib/Btc.d.ts.map +1 -1
- package/lib/Btc.js +99 -30
- package/lib/Btc.js.map +1 -1
- package/lib/BtcNew.d.ts +70 -0
- package/lib/BtcNew.d.ts.map +1 -0
- package/lib/BtcNew.js +372 -0
- package/lib/BtcNew.js.map +1 -0
- package/lib/BtcOld.d.ts +114 -0
- package/lib/BtcOld.d.ts.map +1 -0
- package/lib/BtcOld.js +138 -0
- package/lib/BtcOld.js.map +1 -0
- package/lib/bip32.d.ts +8 -0
- package/lib/bip32.d.ts.map +1 -1
- package/lib/bip32.js +32 -3
- package/lib/bip32.js.map +1 -1
- package/lib/buffertools.d.ts +28 -0
- package/lib/buffertools.d.ts.map +1 -0
- package/lib/buffertools.js +100 -0
- package/lib/buffertools.js.map +1 -0
- package/lib/createTransaction.d.ts.map +1 -1
- package/lib/createTransaction.js +16 -16
- package/lib/createTransaction.js.map +1 -1
- package/lib/finalizeInput.js +1 -1
- package/lib/finalizeInput.js.map +1 -1
- package/lib/getAppAndVersion.js +1 -1
- package/lib/getAppAndVersion.js.map +1 -1
- package/lib/getTrustedInput.js +6 -6
- package/lib/getTrustedInput.js.map +1 -1
- package/lib/getTrustedInputBIP143.js +2 -2
- package/lib/getTrustedInputBIP143.js.map +1 -1
- package/lib/getWalletPublicKey.d.ts +1 -1
- package/lib/getWalletPublicKey.d.ts.map +1 -1
- package/lib/getWalletPublicKey.js +1 -1
- package/lib/getWalletPublicKey.js.map +1 -1
- package/lib/hashPublicKey.d.ts +1 -1
- package/lib/hashPublicKey.d.ts.map +1 -1
- package/lib/hashPublicKey.js +1 -1
- package/lib/hashPublicKey.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -0
- package/lib/newops/appClient.d.ts +14 -0
- package/lib/newops/appClient.d.ts.map +1 -0
- package/lib/newops/appClient.js +242 -0
- package/lib/newops/appClient.js.map +1 -0
- package/lib/newops/clientCommands.d.ts +61 -0
- package/lib/newops/clientCommands.d.ts.map +1 -0
- package/lib/newops/clientCommands.js +331 -0
- package/lib/newops/clientCommands.js.map +1 -0
- package/lib/newops/merkelizedPsbt.d.ts +15 -0
- package/lib/newops/merkelizedPsbt.d.ts.map +1 -0
- package/lib/newops/merkelizedPsbt.js +91 -0
- package/lib/newops/merkelizedPsbt.js.map +1 -0
- package/lib/newops/merkle.d.ts +29 -0
- package/lib/newops/merkle.d.ts.map +1 -0
- package/lib/newops/merkle.js +133 -0
- package/lib/newops/merkle.js.map +1 -0
- package/lib/newops/merkleMap.d.ts +15 -0
- package/lib/newops/merkleMap.d.ts.map +1 -0
- package/lib/newops/merkleMap.js +37 -0
- package/lib/newops/merkleMap.js.map +1 -0
- package/lib/newops/policy.d.ts +14 -0
- package/lib/newops/policy.d.ts.map +1 -0
- package/lib/newops/policy.js +40 -0
- package/lib/newops/policy.js.map +1 -0
- package/lib/newops/psbtExtractor.d.ts +4 -0
- package/lib/newops/psbtExtractor.d.ts.map +1 -0
- package/lib/newops/psbtExtractor.js +36 -0
- package/lib/newops/psbtExtractor.js.map +1 -0
- package/lib/newops/psbtFinalizer.d.ts +7 -0
- package/lib/newops/psbtFinalizer.d.ts.map +1 -0
- package/lib/newops/psbtFinalizer.js +111 -0
- package/lib/newops/psbtFinalizer.js.map +1 -0
- package/lib/newops/psbtv2.d.ts +129 -0
- package/lib/newops/psbtv2.d.ts.map +1 -0
- package/lib/newops/psbtv2.js +478 -0
- package/lib/newops/psbtv2.js.map +1 -0
- package/lib/serializeTransaction.js +4 -4
- package/lib/serializeTransaction.js.map +1 -1
- package/lib/signP2SHTransaction.js +5 -5
- package/lib/signP2SHTransaction.js.map +1 -1
- package/lib/signTransaction.js +1 -1
- package/lib/signTransaction.js.map +1 -1
- package/lib/splitTransaction.js +7 -7
- package/lib/splitTransaction.js.map +1 -1
- package/lib/startUntrustedHashTransactionInput.js +2 -2
- package/lib/startUntrustedHashTransactionInput.js.map +1 -1
- package/lib/varint.d.ts.map +1 -1
- package/lib/varint.js +1 -0
- package/lib/varint.js.map +1 -1
- package/lib-es/Btc.d.ts +7 -3
- package/lib-es/Btc.d.ts.map +1 -1
- package/lib-es/Btc.js +92 -26
- package/lib-es/Btc.js.map +1 -1
- package/lib-es/BtcNew.d.ts +70 -0
- package/lib-es/BtcNew.d.ts.map +1 -0
- package/lib-es/BtcNew.js +370 -0
- package/lib-es/BtcNew.js.map +1 -0
- package/lib-es/BtcOld.d.ts +114 -0
- package/lib-es/BtcOld.d.ts.map +1 -0
- package/lib-es/BtcOld.js +136 -0
- package/lib-es/BtcOld.js.map +1 -0
- package/lib-es/bip32.d.ts +8 -0
- package/lib-es/bip32.d.ts.map +1 -1
- package/lib-es/bip32.js +26 -2
- package/lib-es/bip32.js.map +1 -1
- package/lib-es/buffertools.d.ts +28 -0
- package/lib-es/buffertools.d.ts.map +1 -0
- package/lib-es/buffertools.js +94 -0
- package/lib-es/buffertools.js.map +1 -0
- package/lib-es/createTransaction.d.ts.map +1 -1
- package/lib-es/getWalletPublicKey.d.ts +1 -1
- package/lib-es/getWalletPublicKey.d.ts.map +1 -1
- package/lib-es/getWalletPublicKey.js.map +1 -1
- package/lib-es/hashPublicKey.d.ts +1 -1
- package/lib-es/hashPublicKey.d.ts.map +1 -1
- package/lib-es/index.d.ts +3 -0
- package/lib-es/index.d.ts.map +1 -0
- package/lib-es/index.js +3 -0
- package/lib-es/index.js.map +1 -0
- package/lib-es/newops/appClient.d.ts +14 -0
- package/lib-es/newops/appClient.d.ts.map +1 -0
- package/lib-es/newops/appClient.js +239 -0
- package/lib-es/newops/appClient.js.map +1 -0
- package/lib-es/newops/clientCommands.d.ts +61 -0
- package/lib-es/newops/clientCommands.d.ts.map +1 -0
- package/lib-es/newops/clientCommands.js +328 -0
- package/lib-es/newops/clientCommands.js.map +1 -0
- package/lib-es/newops/merkelizedPsbt.d.ts +15 -0
- package/lib-es/newops/merkelizedPsbt.d.ts.map +1 -0
- package/lib-es/newops/merkelizedPsbt.js +88 -0
- package/lib-es/newops/merkelizedPsbt.js.map +1 -0
- package/lib-es/newops/merkle.d.ts +29 -0
- package/lib-es/newops/merkle.d.ts.map +1 -0
- package/lib-es/newops/merkle.js +129 -0
- package/lib-es/newops/merkle.js.map +1 -0
- package/lib-es/newops/merkleMap.d.ts +15 -0
- package/lib-es/newops/merkleMap.d.ts.map +1 -0
- package/lib-es/newops/merkleMap.js +34 -0
- package/lib-es/newops/merkleMap.js.map +1 -0
- package/lib-es/newops/policy.d.ts +14 -0
- package/lib-es/newops/policy.d.ts.map +1 -0
- package/lib-es/newops/policy.js +36 -0
- package/lib-es/newops/policy.js.map +1 -0
- package/lib-es/newops/psbtExtractor.d.ts +4 -0
- package/lib-es/newops/psbtExtractor.d.ts.map +1 -0
- package/lib-es/newops/psbtExtractor.js +32 -0
- package/lib-es/newops/psbtExtractor.js.map +1 -0
- package/lib-es/newops/psbtFinalizer.d.ts +7 -0
- package/lib-es/newops/psbtFinalizer.d.ts.map +1 -0
- package/lib-es/newops/psbtFinalizer.js +107 -0
- package/lib-es/newops/psbtFinalizer.js.map +1 -0
- package/lib-es/newops/psbtv2.d.ts +129 -0
- package/lib-es/newops/psbtv2.d.ts.map +1 -0
- package/lib-es/newops/psbtv2.js +475 -0
- package/lib-es/newops/psbtv2.js.map +1 -0
- package/lib-es/varint.d.ts.map +1 -1
- package/lib-es/varint.js +1 -0
- package/lib-es/varint.js.map +1 -1
- package/package.json +7 -4
- package/src/Btc.ts +42 -25
- package/src/BtcNew.ts +326 -0
- package/src/BtcOld.ts +156 -0
- package/src/bip32.ts +34 -2
- package/src/buffertools.ts +102 -0
- package/src/createTransaction.ts +2 -2
- package/src/getWalletPublicKey.ts +6 -1
- package/src/hashPublicKey.ts +1 -1
- package/src/index.ts +2 -0
- package/src/newops/appClient.ts +178 -0
- package/src/newops/clientCommands.ts +312 -0
- package/src/newops/merkelizedPsbt.ts +55 -0
- package/src/newops/merkle.ts +123 -0
- package/src/newops/merkleMap.ts +39 -0
- package/src/newops/policy.ts +52 -0
- package/src/newops/psbtExtractor.ts +33 -0
- package/src/newops/psbtFinalizer.ts +110 -0
- package/src/newops/psbtv2.ts +548 -0
- package/src/varint.ts +2 -0
- package/tests/Btc.integration.test.ts +89 -0
- package/tests/Btc.test.ts +6 -0
- package/tests/newops/BtcNew.test.ts +646 -0
- package/tests/newops/common.ts +25 -0
- package/tests/newops/merkle.test.ts +97 -0
- package/tests/trustedInputs.test.ts +4 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { crypto } from "bitcoinjs-lib";
|
|
2
|
+
|
|
3
|
+
export class Merkle {
|
|
4
|
+
private leaves: Buffer[];
|
|
5
|
+
private rootNode: Node;
|
|
6
|
+
private leafNodes: Node[];
|
|
7
|
+
private h: (buf: Buffer) => Buffer;
|
|
8
|
+
constructor(
|
|
9
|
+
leaves: Buffer[],
|
|
10
|
+
hasher: (buf: Buffer) => Buffer = crypto.sha256
|
|
11
|
+
) {
|
|
12
|
+
this.leaves = leaves;
|
|
13
|
+
this.h = hasher;
|
|
14
|
+
const nodes = this.calculateRoot(leaves);
|
|
15
|
+
this.rootNode = nodes.root;
|
|
16
|
+
this.leafNodes = nodes.leaves;
|
|
17
|
+
}
|
|
18
|
+
getRoot(): Buffer {
|
|
19
|
+
return this.rootNode.hash;
|
|
20
|
+
}
|
|
21
|
+
size(): number {
|
|
22
|
+
return this.leaves.length;
|
|
23
|
+
}
|
|
24
|
+
getLeaves(): Buffer[] {
|
|
25
|
+
return this.leaves;
|
|
26
|
+
}
|
|
27
|
+
getLeafHash(index: number): Buffer {
|
|
28
|
+
return this.leafNodes[index].hash;
|
|
29
|
+
}
|
|
30
|
+
getProof(index: number): Buffer[] {
|
|
31
|
+
if (index >= this.leaves.length) throw Error("Index out of bounds");
|
|
32
|
+
return proveNode(this.leafNodes[index]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
calculateRoot(leaves: Buffer[]): { root: Node; leaves: Node[] } {
|
|
36
|
+
const n = leaves.length;
|
|
37
|
+
if (n == 0) {
|
|
38
|
+
return {
|
|
39
|
+
root: new Node(undefined, undefined, Buffer.alloc(32, 0)),
|
|
40
|
+
leaves: [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (n == 1) {
|
|
44
|
+
const newNode = new Node(undefined, undefined, leaves[0]);
|
|
45
|
+
return { root: newNode, leaves: [newNode] };
|
|
46
|
+
}
|
|
47
|
+
const leftCount = highestPowerOf2LessThan(n);
|
|
48
|
+
const leftBranch = this.calculateRoot(leaves.slice(0, leftCount));
|
|
49
|
+
const rightBranch = this.calculateRoot(leaves.slice(leftCount));
|
|
50
|
+
const leftChild = leftBranch.root;
|
|
51
|
+
const rightChild = rightBranch.root;
|
|
52
|
+
const hash = this.hashNode(leftChild.hash, rightChild.hash);
|
|
53
|
+
const node = new Node(leftChild, rightChild, hash);
|
|
54
|
+
leftChild.parent = node;
|
|
55
|
+
rightChild.parent = node;
|
|
56
|
+
return { root: node, leaves: leftBranch.leaves.concat(rightBranch.leaves) };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
hashNode(left: Buffer, right: Buffer): Buffer {
|
|
60
|
+
return this.h(Buffer.concat([Buffer.of(1), left, right]));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function hashLeaf(
|
|
65
|
+
buf: Buffer,
|
|
66
|
+
hashFunction: (buf: Buffer) => Buffer = crypto.sha256
|
|
67
|
+
): Buffer {
|
|
68
|
+
return hashConcat(Buffer.of(0), buf, hashFunction);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hashConcat(
|
|
72
|
+
bufA: Buffer,
|
|
73
|
+
bufB: Buffer,
|
|
74
|
+
hashFunction: (buf: Buffer) => Buffer
|
|
75
|
+
): Buffer {
|
|
76
|
+
return hashFunction(Buffer.concat([bufA, bufB]));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class Node {
|
|
80
|
+
leftChild?: Node;
|
|
81
|
+
rightChild?: Node;
|
|
82
|
+
parent?: Node;
|
|
83
|
+
hash: Buffer;
|
|
84
|
+
constructor(left: Node | undefined, right: Node | undefined, hash: Buffer) {
|
|
85
|
+
this.leftChild = left;
|
|
86
|
+
this.rightChild = right;
|
|
87
|
+
this.hash = hash;
|
|
88
|
+
}
|
|
89
|
+
isLeaf(): boolean {
|
|
90
|
+
return this.leftChild == undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function proveNode(node: Node): Buffer[] {
|
|
95
|
+
if (!node.parent) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
if (node.parent.leftChild == node) {
|
|
99
|
+
if (!node.parent.rightChild) {
|
|
100
|
+
throw new Error("Expected right child to exist");
|
|
101
|
+
}
|
|
102
|
+
return [node.parent.rightChild.hash, ...proveNode(node.parent)];
|
|
103
|
+
} else {
|
|
104
|
+
if (!node.parent.leftChild) {
|
|
105
|
+
throw new Error("Expected left child to exist");
|
|
106
|
+
}
|
|
107
|
+
return [node.parent.leftChild.hash, ...proveNode(node.parent)];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function highestPowerOf2LessThan(n: number) {
|
|
112
|
+
if (n < 2) {
|
|
113
|
+
throw Error("Expected n >= 2");
|
|
114
|
+
}
|
|
115
|
+
if (isPowerOf2(n)) {
|
|
116
|
+
return n / 2;
|
|
117
|
+
}
|
|
118
|
+
return 1 << Math.floor(Math.log2(n));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isPowerOf2(n: number): boolean {
|
|
122
|
+
return (n & (n - 1)) == 0;
|
|
123
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createVarint } from "../varint";
|
|
2
|
+
import { hashLeaf, Merkle } from "./merkle";
|
|
3
|
+
|
|
4
|
+
export class MerkleMap {
|
|
5
|
+
keys: Buffer[];
|
|
6
|
+
keysTree: Merkle;
|
|
7
|
+
values: Buffer[];
|
|
8
|
+
valuesTree: Merkle;
|
|
9
|
+
/**
|
|
10
|
+
* @param keys Sorted list of (unhashed) keys
|
|
11
|
+
* @param values values, in corresponding order as the keys, and of equal length
|
|
12
|
+
*/
|
|
13
|
+
constructor(keys: Buffer[], values: Buffer[]) {
|
|
14
|
+
if (keys.length != values.length) {
|
|
15
|
+
throw new Error("keys and values should have the same length");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Sanity check: verify that keys are actually sorted and with no duplicates
|
|
19
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
20
|
+
if (keys[i].toString("hex") >= keys[i + 1].toString("hex")) {
|
|
21
|
+
throw new Error("keys must be in strictly increasing order");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.keys = keys;
|
|
26
|
+
this.keysTree = new Merkle(keys.map((k) => hashLeaf(k)));
|
|
27
|
+
this.values = values;
|
|
28
|
+
this.valuesTree = new Merkle(values.map((v) => hashLeaf(v)));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
commitment(): Buffer {
|
|
32
|
+
// returns a buffer between 65 and 73 (included) bytes long
|
|
33
|
+
return Buffer.concat([
|
|
34
|
+
createVarint(this.keys.length),
|
|
35
|
+
this.keysTree.getRoot(),
|
|
36
|
+
this.valuesTree.getRoot(),
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { pathArrayToString } from "../bip32";
|
|
2
|
+
import { BufferWriter } from "../buffertools";
|
|
3
|
+
import { crypto } from "bitcoinjs-lib";
|
|
4
|
+
import { Merkle, hashLeaf } from "./merkle";
|
|
5
|
+
|
|
6
|
+
export type DefaultDescriptorTemplate =
|
|
7
|
+
| "pkh(@0)"
|
|
8
|
+
| "sh(wpkh(@0))"
|
|
9
|
+
| "wpkh(@0)"
|
|
10
|
+
| "tr(@0)";
|
|
11
|
+
|
|
12
|
+
export class WalletPolicy {
|
|
13
|
+
descriptorTemplate: string;
|
|
14
|
+
keys: string[];
|
|
15
|
+
/**
|
|
16
|
+
* For now, we only support default descriptor templates.
|
|
17
|
+
*/
|
|
18
|
+
constructor(descriptorTemplate: DefaultDescriptorTemplate, key: string) {
|
|
19
|
+
this.descriptorTemplate = descriptorTemplate;
|
|
20
|
+
this.keys = [key];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getWalletId(): Buffer {
|
|
24
|
+
// wallet_id (sha256 of the wallet serialization),
|
|
25
|
+
return crypto.sha256(this.serialize());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
serialize(): Buffer {
|
|
29
|
+
const keyBuffers = this.keys.map((k) => {
|
|
30
|
+
return Buffer.from(k, "ascii");
|
|
31
|
+
});
|
|
32
|
+
const m = new Merkle(keyBuffers.map((k) => hashLeaf(k)));
|
|
33
|
+
|
|
34
|
+
const buf = new BufferWriter();
|
|
35
|
+
buf.writeUInt8(0x01); // wallet type (policy map)
|
|
36
|
+
buf.writeUInt8(0); // length of wallet name (empty string for default wallets)
|
|
37
|
+
buf.writeVarSlice(Buffer.from(this.descriptorTemplate, "ascii"));
|
|
38
|
+
buf.writeVarInt(this.keys.length), buf.writeSlice(m.getRoot());
|
|
39
|
+
return buf.buffer();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createKey(
|
|
44
|
+
masterFingerprint: Buffer,
|
|
45
|
+
path: number[],
|
|
46
|
+
xpub: string
|
|
47
|
+
): string {
|
|
48
|
+
const accountPath = pathArrayToString(path);
|
|
49
|
+
return `[${masterFingerprint.toString("hex")}${accountPath.substring(
|
|
50
|
+
1
|
|
51
|
+
)}]${xpub}/**`;
|
|
52
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BufferWriter } from "../buffertools";
|
|
2
|
+
import { PsbtV2 } from "./psbtv2";
|
|
3
|
+
|
|
4
|
+
export function extract(psbt: PsbtV2): Buffer {
|
|
5
|
+
const tx = new BufferWriter();
|
|
6
|
+
tx.writeUInt32(psbt.getGlobalTxVersion());
|
|
7
|
+
|
|
8
|
+
const isSegwit = !!psbt.getInputWitnessUtxo(0);
|
|
9
|
+
if (isSegwit) {
|
|
10
|
+
tx.writeSlice(Buffer.of(0, 1));
|
|
11
|
+
}
|
|
12
|
+
const inputCount = psbt.getGlobalInputCount();
|
|
13
|
+
tx.writeVarInt(inputCount);
|
|
14
|
+
const witnessWriter = new BufferWriter();
|
|
15
|
+
for (let i = 0; i < inputCount; i++) {
|
|
16
|
+
tx.writeSlice(psbt.getInputPreviousTxid(i));
|
|
17
|
+
tx.writeUInt32(psbt.getInputOutputIndex(i));
|
|
18
|
+
tx.writeVarSlice(psbt.getInputFinalScriptsig(i) ?? Buffer.of());
|
|
19
|
+
tx.writeUInt32(psbt.getInputSequence(i));
|
|
20
|
+
if (isSegwit) {
|
|
21
|
+
witnessWriter.writeSlice(psbt.getInputFinalScriptwitness(i));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const outputCount = psbt.getGlobalOutputCount();
|
|
25
|
+
tx.writeVarInt(outputCount);
|
|
26
|
+
for (let i = 0; i < outputCount; i++) {
|
|
27
|
+
tx.writeUInt64(BigInt(psbt.getOutputAmount(i)));
|
|
28
|
+
tx.writeVarSlice(psbt.getOutputScript(i));
|
|
29
|
+
}
|
|
30
|
+
tx.writeSlice(witnessWriter.buffer());
|
|
31
|
+
tx.writeUInt32(psbt.getGlobalFallbackLocktime() ?? 0);
|
|
32
|
+
return tx.buffer();
|
|
33
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BufferWriter } from "../buffertools";
|
|
2
|
+
import { psbtIn, PsbtV2 } from "./psbtv2";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param psbt The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
|
|
7
|
+
*/
|
|
8
|
+
export function finalize(psbt: PsbtV2): void {
|
|
9
|
+
// First check that each input has a signature
|
|
10
|
+
const inputCount = psbt.getGlobalInputCount();
|
|
11
|
+
for (let i = 0; i < inputCount; i++) {
|
|
12
|
+
const legacyPubkeys = psbt.getInputKeyDatas(i, psbtIn.PARTIAL_SIG);
|
|
13
|
+
const taprootSig = psbt.getInputTapKeySig(i);
|
|
14
|
+
if (legacyPubkeys.length == 0 && !taprootSig) {
|
|
15
|
+
throw Error(`No signature for input ${i} present`);
|
|
16
|
+
}
|
|
17
|
+
if (legacyPubkeys.length > 0) {
|
|
18
|
+
if (legacyPubkeys.length > 1) {
|
|
19
|
+
throw Error(
|
|
20
|
+
`Expected exactly one signature, got ${legacyPubkeys.length}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
if (taprootSig) {
|
|
24
|
+
throw Error("Both taproot and non-taproot signatures present.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const isSegwitV0 = !!psbt.getInputWitnessUtxo(i);
|
|
28
|
+
const redeemScript = psbt.getInputRedeemScript(i);
|
|
29
|
+
const isWrappedSegwit = !!redeemScript;
|
|
30
|
+
const signature = psbt.getInputPartialSig(i, legacyPubkeys[0]);
|
|
31
|
+
if (!signature)
|
|
32
|
+
throw new Error("Expected partial signature for input " + i);
|
|
33
|
+
if (isSegwitV0) {
|
|
34
|
+
const witnessBuf = new BufferWriter();
|
|
35
|
+
witnessBuf.writeVarInt(2);
|
|
36
|
+
witnessBuf.writeVarInt(signature.length);
|
|
37
|
+
witnessBuf.writeSlice(signature);
|
|
38
|
+
witnessBuf.writeVarInt(legacyPubkeys[0].length);
|
|
39
|
+
witnessBuf.writeSlice(legacyPubkeys[0]);
|
|
40
|
+
psbt.setInputFinalScriptwitness(i, witnessBuf.buffer());
|
|
41
|
+
if (isWrappedSegwit) {
|
|
42
|
+
if (!redeemScript || redeemScript.length == 0) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"Expected non-empty redeemscript. Can't finalize intput " + i
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const scriptSigBuf = new BufferWriter();
|
|
48
|
+
// Push redeemScript length
|
|
49
|
+
scriptSigBuf.writeUInt8(redeemScript.length);
|
|
50
|
+
scriptSigBuf.writeSlice(redeemScript);
|
|
51
|
+
psbt.setInputFinalScriptsig(i, scriptSigBuf.buffer());
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// Legacy input
|
|
55
|
+
const scriptSig = new BufferWriter();
|
|
56
|
+
writePush(scriptSig, signature);
|
|
57
|
+
writePush(scriptSig, legacyPubkeys[0]);
|
|
58
|
+
psbt.setInputFinalScriptsig(i, scriptSig.buffer());
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Taproot input
|
|
62
|
+
const signature = psbt.getInputTapKeySig(i);
|
|
63
|
+
if (!signature) {
|
|
64
|
+
throw Error("No taproot signature found");
|
|
65
|
+
}
|
|
66
|
+
if (signature.length != 64) {
|
|
67
|
+
throw Error("Unexpected length of schnorr signature.");
|
|
68
|
+
}
|
|
69
|
+
const witnessBuf = new BufferWriter();
|
|
70
|
+
witnessBuf.writeVarInt(1);
|
|
71
|
+
witnessBuf.writeVarInt(64);
|
|
72
|
+
witnessBuf.writeSlice(signature);
|
|
73
|
+
psbt.setInputFinalScriptwitness(i, witnessBuf.buffer());
|
|
74
|
+
}
|
|
75
|
+
clearFinalizedInput(psbt, i);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function clearFinalizedInput(psbt: PsbtV2, inputIndex: number) {
|
|
80
|
+
const keyTypes = [
|
|
81
|
+
psbtIn.BIP32_DERIVATION,
|
|
82
|
+
psbtIn.PARTIAL_SIG,
|
|
83
|
+
psbtIn.TAP_BIP32_DERIVATION,
|
|
84
|
+
psbtIn.TAP_KEY_SIG,
|
|
85
|
+
];
|
|
86
|
+
const witnessUtxoAvailable = !!psbt.getInputWitnessUtxo(inputIndex);
|
|
87
|
+
const nonWitnessUtxoAvailable = !!psbt.getInputNonWitnessUtxo(inputIndex);
|
|
88
|
+
if (witnessUtxoAvailable && nonWitnessUtxoAvailable) {
|
|
89
|
+
// Remove NON_WITNESS_UTXO for segwit v0 as it's only needed while signing.
|
|
90
|
+
// Segwit v1 doesn't have NON_WITNESS_UTXO set.
|
|
91
|
+
// See https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#cite_note-7
|
|
92
|
+
keyTypes.push(psbtIn.NON_WITNESS_UTXO);
|
|
93
|
+
}
|
|
94
|
+
psbt.deleteInputEntries(inputIndex, keyTypes);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function writePush(buf: BufferWriter, data: Buffer) {
|
|
98
|
+
if (data.length <= 75) {
|
|
99
|
+
buf.writeUInt8(data.length);
|
|
100
|
+
} else if (data.length <= 256) {
|
|
101
|
+
buf.writeUInt8(76);
|
|
102
|
+
buf.writeUInt8(data.length);
|
|
103
|
+
} else if (data.length <= 256 * 256) {
|
|
104
|
+
buf.writeUInt8(77);
|
|
105
|
+
const b = Buffer.alloc(2);
|
|
106
|
+
b.writeUInt16LE(data.length, 0);
|
|
107
|
+
buf.writeSlice(b);
|
|
108
|
+
}
|
|
109
|
+
buf.writeSlice(data);
|
|
110
|
+
}
|