-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpsbt.mjs
124 lines (111 loc) · 2.9 KB
/
psbt.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import {
packTx,
packUint64,
packVarInt,
} from "./bitcoin/protocol/messages.create.mjs";
import { joinBuffers } from "./bitcoin/utils/joinBuffers.mjs";
/**
* @typedef {{
* keyType: number;
* keyData?: Uint8Array;
* valueData: Uint8Array | Uint8Array[]
* }} KeyPair
*/
/**
* @param {KeyPair} keyPair
*/
function packKeyPair(keyPair) {
const keyType = packVarInt(keyPair.keyType);
const keyLen = packVarInt(
keyType.length + (keyPair.keyData ? keyPair.keyData.length : 0)
);
/**
* @type {Uint8Array[]}
*/
const out = [];
out.push(keyLen, keyType);
if (keyPair.keyData) {
out.push(keyPair.keyData);
}
const valueLen = packVarInt(
Array.isArray(keyPair.valueData)
? keyPair.valueData.reduce((acc, cur) => acc + cur.length, 0)
: keyPair.valueData.length
);
out.push(valueLen);
if (Array.isArray(keyPair.valueData)) {
for (const valueDataItem of keyPair.valueData) {
out.push(valueDataItem);
}
} else {
out.push(keyPair.valueData);
}
return out;
}
/**
* @param {KeyPair[]} keyPairs
*/
function packMap(keyPairs) {
/**
* @type {Uint8Array[]}
*/
const out = [];
for (const keyPair of keyPairs) {
out.push(...packKeyPair(keyPair));
}
out.push(new Uint8Array([0]));
return out;
}
const PSBT_GLOBAL_UNSIGNED_TX = 0x00;
const PSBT_IN_WITNESS_UTXO = 0x01;
/**
* @param {import("./bitcoin/protocol/types.js").BitcoinTransaction} tx
* @param {import("./bitcoin/protocol/messages.types.js").PkScript[]} spendingPkScripts
* @param {bigint[]} spendingValues
*/
export function packTxToPSBT(tx, spendingPkScripts, spendingValues) {
if (tx.txIn.length !== spendingPkScripts.length) {
throw new Error(`Mismatch spendingPkScripts length`);
}
if (tx.txIn.length !== spendingValues.length) {
throw new Error(`Mismatch spendingValues length`);
}
const packedTx = packTx({
...tx,
txIn: tx.txIn.map((txIn) => ({
...txIn,
script:
/** @type {import("./bitcoin/protocol/messages.types.js").SignatureScript} */ (
new ArrayBuffer(0)
),
witness: undefined,
})),
isWitness: false,
});
const globalMap = packMap([
{
keyType: PSBT_GLOBAL_UNSIGNED_TX,
valueData: new Uint8Array(packedTx),
},
]);
const inputMaps = tx.txIn
.map((txIn, index) =>
packMap([
{
keyType: PSBT_IN_WITNESS_UTXO,
valueData: [
new Uint8Array(packUint64(spendingValues[index])),
packVarInt(spendingPkScripts.length),
new Uint8Array(spendingPkScripts[index]),
],
},
])
)
.reduce((acc, cur) => [...acc, ...cur], []);
const outputMaps = tx.txOut
.map((txOut) => packMap([]))
.reduce((acc, cur) => [...acc, ...cur], []);
const magic = new Uint8Array([0x70, 0x73, 0x62, 0x74, 0xff]);
const out = joinBuffers(magic, ...globalMap, ...inputMaps, ...outputMaps);
return out;
}