@sidhujag/sysweb3-keyring 1.0.544 → 1.0.547
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/coverage/clover.xml +2875 -0
- package/coverage/coverage-final.json +29468 -0
- package/coverage/lcov-report/base.css +354 -0
- package/coverage/lcov-report/block-navigation.js +85 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +320 -0
- package/coverage/lcov-report/prettify.css +101 -0
- package/coverage/lcov-report/prettify.js +1008 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +191 -0
- package/coverage/lcov-report/src/index.html +276 -0
- package/coverage/lcov-report/src/index.ts.html +114 -0
- package/coverage/lcov-report/src/initial-state.ts.html +558 -0
- package/coverage/lcov-report/src/keyring-manager.ts.html +6279 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +178 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +144 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +1560 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +276 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +495 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +1138 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +363 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +289 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +486 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +240 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +342 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +2388 -0
- package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +453 -0
- package/coverage/lcov-report/src/ledger/consts.ts.html +177 -0
- package/coverage/lcov-report/src/ledger/index.html +216 -0
- package/coverage/lcov-report/src/ledger/index.ts.html +1371 -0
- package/coverage/lcov-report/src/ledger/utils.ts.html +102 -0
- package/coverage/lcov-report/src/signers.ts.html +591 -0
- package/coverage/lcov-report/src/storage.ts.html +198 -0
- package/coverage/lcov-report/src/transactions/ethereum.ts.html +5826 -0
- package/coverage/lcov-report/src/transactions/index.html +216 -0
- package/coverage/lcov-report/src/transactions/index.ts.html +93 -0
- package/coverage/lcov-report/src/transactions/syscoin.ts.html +1521 -0
- package/coverage/lcov-report/src/trezor/index.html +176 -0
- package/coverage/lcov-report/src/trezor/index.ts.html +2655 -0
- package/coverage/lcov-report/src/types.ts.html +1443 -0
- package/coverage/lcov-report/src/utils/derivation-paths.ts.html +486 -0
- package/coverage/lcov-report/src/utils/index.html +196 -0
- package/coverage/lcov-report/src/utils/psbt.ts.html +159 -0
- package/coverage/lcov-report/test/helpers/constants.ts.html +627 -0
- package/coverage/lcov-report/test/helpers/index.html +176 -0
- package/coverage/lcov.info +4832 -0
- package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/appClient.js +1 -124
- package/dist/cjs/ledger/bitcoin_client/lib/appClient.js.map +1 -0
- package/{cjs → dist/cjs}/transactions/ethereum.js +24 -11
- package/dist/cjs/transactions/ethereum.js.map +1 -0
- package/dist/package.json +50 -0
- package/{types → dist/types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -6
- package/examples/basic-usage.js +140 -0
- package/jest.config.js +32 -0
- package/package.json +31 -13
- package/readme.md +201 -0
- package/src/declare.d.ts +7 -0
- package/src/errorUtils.ts +83 -0
- package/src/hardware-wallet-manager.ts +655 -0
- package/src/index.ts +12 -0
- package/src/initial-state.ts +108 -0
- package/src/keyring-manager.ts +2698 -0
- package/src/ledger/bitcoin_client/index.ts +19 -0
- package/src/ledger/bitcoin_client/lib/appClient.ts +405 -0
- package/src/ledger/bitcoin_client/lib/bip32.ts +61 -0
- package/src/ledger/bitcoin_client/lib/buffertools.ts +134 -0
- package/src/ledger/bitcoin_client/lib/clientCommands.ts +356 -0
- package/src/ledger/bitcoin_client/lib/constants.ts +12 -0
- package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +65 -0
- package/src/ledger/bitcoin_client/lib/merkle.ts +136 -0
- package/src/ledger/bitcoin_client/lib/merkleMap.ts +49 -0
- package/src/ledger/bitcoin_client/lib/policy.ts +91 -0
- package/src/ledger/bitcoin_client/lib/psbtv2.ts +768 -0
- package/src/ledger/bitcoin_client/lib/varint.ts +120 -0
- package/src/ledger/consts.ts +3 -0
- package/src/ledger/index.ts +685 -0
- package/src/ledger/types.ts +74 -0
- package/src/network-utils.ts +99 -0
- package/src/providers.ts +345 -0
- package/src/signers.ts +158 -0
- package/src/storage.ts +63 -0
- package/src/transactions/__tests__/integration.test.ts +303 -0
- package/src/transactions/__tests__/syscoin.test.ts +409 -0
- package/src/transactions/ethereum.ts +2503 -0
- package/src/transactions/index.ts +2 -0
- package/src/transactions/syscoin.ts +542 -0
- package/src/trezor/index.ts +1050 -0
- package/src/types.ts +366 -0
- package/src/utils/derivation-paths.ts +133 -0
- package/src/utils/psbt.ts +24 -0
- package/src/utils.ts +191 -0
- package/test/README.md +158 -0
- package/test/__mocks__/ledger-mock.js +20 -0
- package/test/__mocks__/trezor-mock.js +75 -0
- package/test/cleanup-summary.md +167 -0
- package/test/helpers/README.md +78 -0
- package/test/helpers/constants.ts +79 -0
- package/test/helpers/setup.ts +714 -0
- package/test/integration/import-validation.spec.ts +588 -0
- package/test/unit/hardware/ledger.spec.ts +869 -0
- package/test/unit/hardware/trezor.spec.ts +828 -0
- package/test/unit/keyring-manager/account-management.spec.ts +970 -0
- package/test/unit/keyring-manager/import-watchonly.spec.ts +181 -0
- package/test/unit/keyring-manager/import-wif.spec.ts +126 -0
- package/test/unit/keyring-manager/initialization.spec.ts +782 -0
- package/test/unit/keyring-manager/key-derivation.spec.ts +996 -0
- package/test/unit/keyring-manager/security.spec.ts +505 -0
- package/test/unit/keyring-manager/state-management.spec.ts +375 -0
- package/test/unit/network/network-management.spec.ts +372 -0
- package/test/unit/transactions/ethereum-transactions.spec.ts +382 -0
- package/test/unit/transactions/syscoin-transactions.spec.ts +615 -0
- package/tsconfig.json +14 -0
- package/cjs/ledger/bitcoin_client/lib/appClient.js.map +0 -1
- package/cjs/transactions/ethereum.js.map +0 -1
- /package/{README.md → dist/README.md} +0 -0
- /package/{cjs → dist/cjs}/errorUtils.js +0 -0
- /package/{cjs → dist/cjs}/errorUtils.js.map +0 -0
- /package/{cjs → dist/cjs}/hardware-wallet-manager.js +0 -0
- /package/{cjs → dist/cjs}/hardware-wallet-manager.js.map +0 -0
- /package/{cjs → dist/cjs}/index.js +0 -0
- /package/{cjs → dist/cjs}/index.js.map +0 -0
- /package/{cjs → dist/cjs}/initial-state.js +0 -0
- /package/{cjs → dist/cjs}/initial-state.js.map +0 -0
- /package/{cjs → dist/cjs}/keyring-manager.js +0 -0
- /package/{cjs → dist/cjs}/keyring-manager.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
- /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/consts.js +0 -0
- /package/{cjs → dist/cjs}/ledger/consts.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/index.js +0 -0
- /package/{cjs → dist/cjs}/ledger/index.js.map +0 -0
- /package/{cjs → dist/cjs}/ledger/types.js +0 -0
- /package/{cjs → dist/cjs}/ledger/types.js.map +0 -0
- /package/{cjs → dist/cjs}/network-utils.js +0 -0
- /package/{cjs → dist/cjs}/network-utils.js.map +0 -0
- /package/{cjs → dist/cjs}/providers.js +0 -0
- /package/{cjs → dist/cjs}/providers.js.map +0 -0
- /package/{cjs → dist/cjs}/signers.js +0 -0
- /package/{cjs → dist/cjs}/signers.js.map +0 -0
- /package/{cjs → dist/cjs}/storage.js +0 -0
- /package/{cjs → dist/cjs}/storage.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js +0 -0
- /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/index.js +0 -0
- /package/{cjs → dist/cjs}/transactions/index.js.map +0 -0
- /package/{cjs → dist/cjs}/transactions/syscoin.js +0 -0
- /package/{cjs → dist/cjs}/transactions/syscoin.js.map +0 -0
- /package/{cjs → dist/cjs}/trezor/index.js +0 -0
- /package/{cjs → dist/cjs}/trezor/index.js.map +0 -0
- /package/{cjs → dist/cjs}/types.js +0 -0
- /package/{cjs → dist/cjs}/types.js.map +0 -0
- /package/{cjs → dist/cjs}/utils/derivation-paths.js +0 -0
- /package/{cjs → dist/cjs}/utils/derivation-paths.js.map +0 -0
- /package/{cjs → dist/cjs}/utils/psbt.js +0 -0
- /package/{cjs → dist/cjs}/utils/psbt.js.map +0 -0
- /package/{cjs → dist/cjs}/utils.js +0 -0
- /package/{cjs → dist/cjs}/utils.js.map +0 -0
- /package/{types → dist/types}/errorUtils.d.ts +0 -0
- /package/{types → dist/types}/hardware-wallet-manager.d.ts +0 -0
- /package/{types → dist/types}/index.d.ts +0 -0
- /package/{types → dist/types}/initial-state.d.ts +0 -0
- /package/{types → dist/types}/keyring-manager.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/index.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
- /package/{types → dist/types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
- /package/{types → dist/types}/ledger/consts.d.ts +0 -0
- /package/{types → dist/types}/ledger/index.d.ts +0 -0
- /package/{types → dist/types}/ledger/types.d.ts +0 -0
- /package/{types → dist/types}/network-utils.d.ts +0 -0
- /package/{types → dist/types}/providers.d.ts +0 -0
- /package/{types → dist/types}/signers.d.ts +0 -0
- /package/{types → dist/types}/storage.d.ts +0 -0
- /package/{types → dist/types}/transactions/__tests__/integration.test.d.ts +0 -0
- /package/{types → dist/types}/transactions/__tests__/syscoin.test.d.ts +0 -0
- /package/{types → dist/types}/transactions/ethereum.d.ts +0 -0
- /package/{types → dist/types}/transactions/index.d.ts +0 -0
- /package/{types → dist/types}/transactions/syscoin.d.ts +0 -0
- /package/{types → dist/types}/trezor/index.d.ts +0 -0
- /package/{types → dist/types}/types.d.ts +0 -0
- /package/{types → dist/types}/utils/derivation-paths.d.ts +0 -0
- /package/{types → dist/types}/utils/psbt.d.ts +0 -0
- /package/{types → dist/types}/utils.d.ts +0 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
/* eslint-disable no-confusing-arrow */
|
|
2
|
+
/* eslint-disable camelcase */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
4
|
+
|
|
5
|
+
import * as bjs from 'bitcoinjs-lib';
|
|
6
|
+
import { Transaction } from 'syscoinjs-lib';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
BufferReader,
|
|
10
|
+
BufferWriter,
|
|
11
|
+
unsafeFrom64bitLE,
|
|
12
|
+
unsafeTo64bitLE,
|
|
13
|
+
} from './buffertools';
|
|
14
|
+
import { sanitizeBigintToNumber } from './varint';
|
|
15
|
+
export enum psbtGlobal {
|
|
16
|
+
UNSIGNED_TX = 0x00,
|
|
17
|
+
XPUB = 0x01,
|
|
18
|
+
TX_VERSION = 0x02,
|
|
19
|
+
FALLBACK_LOCKTIME = 0x03,
|
|
20
|
+
INPUT_COUNT = 0x04,
|
|
21
|
+
OUTPUT_COUNT = 0x05,
|
|
22
|
+
TX_MODIFIABLE = 0x06,
|
|
23
|
+
VERSION = 0xfb,
|
|
24
|
+
}
|
|
25
|
+
export enum psbtIn {
|
|
26
|
+
NON_WITNESS_UTXO = 0x00,
|
|
27
|
+
WITNESS_UTXO = 0x01,
|
|
28
|
+
PARTIAL_SIG = 0x02,
|
|
29
|
+
SIGHASH_TYPE = 0x03,
|
|
30
|
+
REDEEM_SCRIPT = 0x04,
|
|
31
|
+
WITNESS_SCRIPT = 0x05,
|
|
32
|
+
BIP32_DERIVATION = 0x06,
|
|
33
|
+
FINAL_SCRIPTSIG = 0x07,
|
|
34
|
+
FINAL_SCRIPTWITNESS = 0x08,
|
|
35
|
+
PREVIOUS_TXID = 0x0e,
|
|
36
|
+
OUTPUT_INDEX = 0x0f,
|
|
37
|
+
SEQUENCE = 0x10,
|
|
38
|
+
TAP_KEY_SIG = 0x13,
|
|
39
|
+
TAP_BIP32_DERIVATION = 0x16,
|
|
40
|
+
}
|
|
41
|
+
export enum psbtOut {
|
|
42
|
+
REDEEM_SCRIPT = 0x00,
|
|
43
|
+
WITNESS_SCRIPT = 0x01,
|
|
44
|
+
BIP_32_DERIVATION = 0x02,
|
|
45
|
+
AMOUNT = 0x03,
|
|
46
|
+
SCRIPT = 0x04,
|
|
47
|
+
TAP_BIP32_DERIVATION = 0x07,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const PSBT_MAGIC_BYTES = Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]);
|
|
51
|
+
|
|
52
|
+
export class NoSuchEntry extends Error {}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
|
|
56
|
+
* documented at https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
|
|
57
|
+
* and https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
|
58
|
+
*
|
|
59
|
+
* A psbt is a data structure that can carry all relevant information about a
|
|
60
|
+
* transaction through all stages of the signing process. From constructing an
|
|
61
|
+
* unsigned transaction to extracting the final serialized transaction ready for
|
|
62
|
+
* broadcast.
|
|
63
|
+
*
|
|
64
|
+
* This implementation is limited to what's needed in ledgerjs to carry out its
|
|
65
|
+
* duties, which means that support for features like multisig or taproot script
|
|
66
|
+
* path spending are not implemented. Specifically, it supports p2pkh,
|
|
67
|
+
* p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
|
|
68
|
+
*
|
|
69
|
+
* This class is made purposefully dumb, so it's easy to add support for
|
|
70
|
+
* complemantary fields as needed in the future.
|
|
71
|
+
*/
|
|
72
|
+
export class PsbtV2 {
|
|
73
|
+
protected globalMap: Map<string, Buffer> = new Map();
|
|
74
|
+
protected inputMaps: Map<string, Buffer>[] = [];
|
|
75
|
+
protected outputMaps: Map<string, Buffer>[] = [];
|
|
76
|
+
|
|
77
|
+
setGlobalTxVersion(version: number) {
|
|
78
|
+
this.setGlobal(psbtGlobal.TX_VERSION, uint32LE(version));
|
|
79
|
+
}
|
|
80
|
+
getGlobalTxVersion(): number {
|
|
81
|
+
return this.getGlobal(psbtGlobal.TX_VERSION).readUInt32LE(0);
|
|
82
|
+
}
|
|
83
|
+
setGlobalFallbackLocktime(locktime: number) {
|
|
84
|
+
this.setGlobal(psbtGlobal.FALLBACK_LOCKTIME, uint32LE(locktime));
|
|
85
|
+
}
|
|
86
|
+
getGlobalFallbackLocktime(): number | undefined {
|
|
87
|
+
return this.getGlobalOptional(psbtGlobal.FALLBACK_LOCKTIME)?.readUInt32LE(
|
|
88
|
+
0
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
setGlobalInputCount(inputCount: number) {
|
|
92
|
+
this.setGlobal(psbtGlobal.INPUT_COUNT, varint(inputCount));
|
|
93
|
+
}
|
|
94
|
+
getGlobalInputCount(): number {
|
|
95
|
+
return fromVarint(this.getGlobal(psbtGlobal.INPUT_COUNT));
|
|
96
|
+
}
|
|
97
|
+
setGlobalOutputCount(outputCount: number) {
|
|
98
|
+
this.setGlobal(psbtGlobal.OUTPUT_COUNT, varint(outputCount));
|
|
99
|
+
}
|
|
100
|
+
getGlobalOutputCount(): number {
|
|
101
|
+
return fromVarint(this.getGlobal(psbtGlobal.OUTPUT_COUNT));
|
|
102
|
+
}
|
|
103
|
+
setGlobalTxModifiable(byte: Buffer) {
|
|
104
|
+
this.setGlobal(psbtGlobal.TX_MODIFIABLE, byte);
|
|
105
|
+
}
|
|
106
|
+
getGlobalTxModifiable(): Buffer | undefined {
|
|
107
|
+
return this.getGlobalOptional(psbtGlobal.TX_MODIFIABLE);
|
|
108
|
+
}
|
|
109
|
+
setGlobalPsbtVersion(psbtVersion: number) {
|
|
110
|
+
this.setGlobal(psbtGlobal.VERSION, uint32LE(psbtVersion));
|
|
111
|
+
}
|
|
112
|
+
getGlobalPsbtVersion(): number {
|
|
113
|
+
return this.getGlobal(psbtGlobal.VERSION).readUInt32LE(0);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
setInputNonWitnessUtxo(inputIndex: number, transaction: Buffer) {
|
|
117
|
+
this.setInput(inputIndex, psbtIn.NON_WITNESS_UTXO, b(), transaction);
|
|
118
|
+
}
|
|
119
|
+
getInputNonWitnessUtxo(inputIndex: number): Buffer | undefined {
|
|
120
|
+
return this.getInputOptional(inputIndex, psbtIn.NON_WITNESS_UTXO, b());
|
|
121
|
+
}
|
|
122
|
+
setInputWitnessUtxo(
|
|
123
|
+
inputIndex: number,
|
|
124
|
+
amount: number,
|
|
125
|
+
scriptPubKey: Buffer
|
|
126
|
+
) {
|
|
127
|
+
const buf = new BufferWriter();
|
|
128
|
+
buf.writeSlice(uint64LE(amount));
|
|
129
|
+
buf.writeVarSlice(scriptPubKey);
|
|
130
|
+
this.setInput(inputIndex, psbtIn.WITNESS_UTXO, b(), buf.buffer());
|
|
131
|
+
}
|
|
132
|
+
getInputWitnessUtxo(
|
|
133
|
+
inputIndex: number
|
|
134
|
+
): { readonly amount: number; readonly scriptPubKey: Buffer } | undefined {
|
|
135
|
+
const utxo = this.getInputOptional(inputIndex, psbtIn.WITNESS_UTXO, b());
|
|
136
|
+
if (!utxo) return undefined;
|
|
137
|
+
const buf = new BufferReader(utxo);
|
|
138
|
+
return {
|
|
139
|
+
amount: unsafeFrom64bitLE(buf.readSlice(8)),
|
|
140
|
+
scriptPubKey: buf.readVarSlice(),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
setInputPartialSig(inputIndex: number, pubkey: Buffer, signature: Buffer) {
|
|
144
|
+
this.setInput(inputIndex, psbtIn.PARTIAL_SIG, pubkey, signature);
|
|
145
|
+
}
|
|
146
|
+
getInputPartialSig(inputIndex: number, pubkey: Buffer): Buffer | undefined {
|
|
147
|
+
return this.getInputOptional(inputIndex, psbtIn.PARTIAL_SIG, pubkey);
|
|
148
|
+
}
|
|
149
|
+
setInputSighashType(inputIndex: number, sigHashtype: number) {
|
|
150
|
+
this.setInput(inputIndex, psbtIn.SIGHASH_TYPE, b(), uint32LE(sigHashtype));
|
|
151
|
+
}
|
|
152
|
+
getInputSighashType(inputIndex: number): number | undefined {
|
|
153
|
+
const result = this.getInputOptional(inputIndex, psbtIn.SIGHASH_TYPE, b());
|
|
154
|
+
if (!result) return undefined;
|
|
155
|
+
return result.readUInt32LE(0);
|
|
156
|
+
}
|
|
157
|
+
setInputRedeemScript(inputIndex: number, redeemScript: Buffer) {
|
|
158
|
+
this.setInput(inputIndex, psbtIn.REDEEM_SCRIPT, b(), redeemScript);
|
|
159
|
+
}
|
|
160
|
+
getInputRedeemScript(inputIndex: number): Buffer | undefined {
|
|
161
|
+
return this.getInputOptional(inputIndex, psbtIn.REDEEM_SCRIPT, b());
|
|
162
|
+
}
|
|
163
|
+
setInputWitnessScript(inputIndex: number, witnessScript: Buffer) {
|
|
164
|
+
this.setInput(inputIndex, psbtIn.WITNESS_SCRIPT, b(), witnessScript);
|
|
165
|
+
}
|
|
166
|
+
getInputWitnessScript(inputIndex: number): Buffer | undefined {
|
|
167
|
+
return this.getInputOptional(inputIndex, psbtIn.WITNESS_SCRIPT, b());
|
|
168
|
+
}
|
|
169
|
+
setInputBip32Derivation(
|
|
170
|
+
inputIndex: number,
|
|
171
|
+
pubkey: Buffer,
|
|
172
|
+
masterFingerprint: Buffer,
|
|
173
|
+
path: readonly number[]
|
|
174
|
+
) {
|
|
175
|
+
if (pubkey.length != 33)
|
|
176
|
+
throw new Error('Invalid pubkey length: ' + pubkey.length);
|
|
177
|
+
this.setInput(
|
|
178
|
+
inputIndex,
|
|
179
|
+
psbtIn.BIP32_DERIVATION,
|
|
180
|
+
pubkey,
|
|
181
|
+
this.encodeBip32Derivation(masterFingerprint, path)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
getInputBip32Derivation(
|
|
185
|
+
inputIndex: number,
|
|
186
|
+
pubkey: Buffer
|
|
187
|
+
):
|
|
188
|
+
| { readonly masterFingerprint: Buffer; readonly path: readonly number[] }
|
|
189
|
+
| undefined {
|
|
190
|
+
const buf = this.getInputOptional(
|
|
191
|
+
inputIndex,
|
|
192
|
+
psbtIn.BIP32_DERIVATION,
|
|
193
|
+
pubkey
|
|
194
|
+
);
|
|
195
|
+
if (!buf) return undefined;
|
|
196
|
+
return this.decodeBip32Derivation(buf);
|
|
197
|
+
}
|
|
198
|
+
setInputFinalScriptsig(inputIndex: number, scriptSig: Buffer) {
|
|
199
|
+
this.setInput(inputIndex, psbtIn.FINAL_SCRIPTSIG, b(), scriptSig);
|
|
200
|
+
}
|
|
201
|
+
getInputFinalScriptsig(inputIndex: number): Buffer | undefined {
|
|
202
|
+
return this.getInputOptional(inputIndex, psbtIn.FINAL_SCRIPTSIG, b());
|
|
203
|
+
}
|
|
204
|
+
setInputFinalScriptwitness(inputIndex: number, scriptWitness: Buffer) {
|
|
205
|
+
this.setInput(inputIndex, psbtIn.FINAL_SCRIPTWITNESS, b(), scriptWitness);
|
|
206
|
+
}
|
|
207
|
+
getInputFinalScriptwitness(inputIndex: number): Buffer {
|
|
208
|
+
return this.getInput(inputIndex, psbtIn.FINAL_SCRIPTWITNESS, b());
|
|
209
|
+
}
|
|
210
|
+
setInputPreviousTxId(inputIndex: number, txid: Buffer) {
|
|
211
|
+
this.setInput(inputIndex, psbtIn.PREVIOUS_TXID, b(), txid);
|
|
212
|
+
}
|
|
213
|
+
getInputPreviousTxid(inputIndex: number): Buffer {
|
|
214
|
+
return this.getInput(inputIndex, psbtIn.PREVIOUS_TXID, b());
|
|
215
|
+
}
|
|
216
|
+
setInputOutputIndex(inputIndex: number, outputIndex: number) {
|
|
217
|
+
this.setInput(inputIndex, psbtIn.OUTPUT_INDEX, b(), uint32LE(outputIndex));
|
|
218
|
+
}
|
|
219
|
+
getInputOutputIndex(inputIndex: number): number {
|
|
220
|
+
return this.getInput(inputIndex, psbtIn.OUTPUT_INDEX, b()).readUInt32LE(0);
|
|
221
|
+
}
|
|
222
|
+
setInputSequence(inputIndex: number, sequence: number) {
|
|
223
|
+
this.setInput(inputIndex, psbtIn.SEQUENCE, b(), uint32LE(sequence));
|
|
224
|
+
}
|
|
225
|
+
getInputSequence(inputIndex: number): number {
|
|
226
|
+
return (
|
|
227
|
+
this.getInputOptional(inputIndex, psbtIn.SEQUENCE, b())?.readUInt32LE(
|
|
228
|
+
0
|
|
229
|
+
) ?? 0xffffffff
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
setInputTapKeySig(inputIndex: number, sig: Buffer) {
|
|
233
|
+
this.setInput(inputIndex, psbtIn.TAP_KEY_SIG, b(), sig);
|
|
234
|
+
}
|
|
235
|
+
getInputTapKeySig(inputIndex: number): Buffer | undefined {
|
|
236
|
+
return this.getInputOptional(inputIndex, psbtIn.TAP_KEY_SIG, b());
|
|
237
|
+
}
|
|
238
|
+
setInputTapBip32Derivation(
|
|
239
|
+
inputIndex: number,
|
|
240
|
+
pubkey: Buffer,
|
|
241
|
+
hashes: readonly Buffer[],
|
|
242
|
+
masterFingerprint: Buffer,
|
|
243
|
+
path: readonly number[]
|
|
244
|
+
) {
|
|
245
|
+
if (pubkey.length != 32)
|
|
246
|
+
throw new Error('Invalid pubkey length: ' + pubkey.length);
|
|
247
|
+
const buf = this.encodeTapBip32Derivation(hashes, masterFingerprint, path);
|
|
248
|
+
this.setInput(inputIndex, psbtIn.TAP_BIP32_DERIVATION, pubkey, buf);
|
|
249
|
+
}
|
|
250
|
+
getInputTapBip32Derivation(
|
|
251
|
+
inputIndex: number,
|
|
252
|
+
pubkey: Buffer
|
|
253
|
+
): {
|
|
254
|
+
readonly hashes: readonly Buffer[];
|
|
255
|
+
readonly masterFingerprint: Buffer;
|
|
256
|
+
readonly path: readonly number[];
|
|
257
|
+
} {
|
|
258
|
+
const buf = this.getInput(inputIndex, psbtIn.TAP_BIP32_DERIVATION, pubkey);
|
|
259
|
+
return this.decodeTapBip32Derivation(buf);
|
|
260
|
+
}
|
|
261
|
+
getInputKeyDatas(inputIndex: number, keyType: KeyType): readonly Buffer[] {
|
|
262
|
+
return this.getKeyDatas(this.inputMaps[inputIndex], keyType);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
setOutputRedeemScript(outputIndex: number, redeemScript: Buffer) {
|
|
266
|
+
this.setOutput(outputIndex, psbtOut.REDEEM_SCRIPT, b(), redeemScript);
|
|
267
|
+
}
|
|
268
|
+
getOutputRedeemScript(outputIndex: number): Buffer {
|
|
269
|
+
return this.getOutput(outputIndex, psbtOut.REDEEM_SCRIPT, b());
|
|
270
|
+
}
|
|
271
|
+
setOutputBip32Derivation(
|
|
272
|
+
outputIndex: number,
|
|
273
|
+
pubkey: Buffer,
|
|
274
|
+
masterFingerprint: Buffer,
|
|
275
|
+
path: readonly number[]
|
|
276
|
+
) {
|
|
277
|
+
this.setOutput(
|
|
278
|
+
outputIndex,
|
|
279
|
+
psbtOut.BIP_32_DERIVATION,
|
|
280
|
+
pubkey,
|
|
281
|
+
this.encodeBip32Derivation(masterFingerprint, path)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
getOutputBip32Derivation(
|
|
285
|
+
outputIndex: number,
|
|
286
|
+
pubkey: Buffer
|
|
287
|
+
): { readonly masterFingerprint: Buffer; readonly path: readonly number[] } {
|
|
288
|
+
const buf = this.getOutput(outputIndex, psbtOut.BIP_32_DERIVATION, pubkey);
|
|
289
|
+
return this.decodeBip32Derivation(buf);
|
|
290
|
+
}
|
|
291
|
+
setOutputAmount(outputIndex: number, amount: number) {
|
|
292
|
+
this.setOutput(outputIndex, psbtOut.AMOUNT, b(), uint64LE(amount));
|
|
293
|
+
}
|
|
294
|
+
getOutputAmount(outputIndex: number): number {
|
|
295
|
+
const buf = this.getOutput(outputIndex, psbtOut.AMOUNT, b());
|
|
296
|
+
return unsafeFrom64bitLE(buf);
|
|
297
|
+
}
|
|
298
|
+
setOutputScript(outputIndex: number, scriptPubKey: Buffer) {
|
|
299
|
+
this.setOutput(outputIndex, psbtOut.SCRIPT, b(), scriptPubKey);
|
|
300
|
+
}
|
|
301
|
+
getOutputScript(outputIndex: number): Buffer {
|
|
302
|
+
return this.getOutput(outputIndex, psbtOut.SCRIPT, b());
|
|
303
|
+
}
|
|
304
|
+
setOutputTapBip32Derivation(
|
|
305
|
+
outputIndex: number,
|
|
306
|
+
pubkey: Buffer,
|
|
307
|
+
hashes: readonly Buffer[],
|
|
308
|
+
fingerprint: Buffer,
|
|
309
|
+
path: readonly number[]
|
|
310
|
+
) {
|
|
311
|
+
const buf = this.encodeTapBip32Derivation(hashes, fingerprint, path);
|
|
312
|
+
this.setOutput(outputIndex, psbtOut.TAP_BIP32_DERIVATION, pubkey, buf);
|
|
313
|
+
}
|
|
314
|
+
getOutputTapBip32Derivation(
|
|
315
|
+
outputIndex: number,
|
|
316
|
+
pubkey: Buffer
|
|
317
|
+
): {
|
|
318
|
+
readonly hashes: readonly Buffer[];
|
|
319
|
+
readonly masterFingerprint: Buffer;
|
|
320
|
+
readonly path: readonly number[];
|
|
321
|
+
} {
|
|
322
|
+
const buf = this.getOutput(
|
|
323
|
+
outputIndex,
|
|
324
|
+
psbtOut.TAP_BIP32_DERIVATION,
|
|
325
|
+
pubkey
|
|
326
|
+
);
|
|
327
|
+
return this.decodeTapBip32Derivation(buf);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
deleteInputEntries(inputIndex: number, keyTypes: readonly psbtIn[]) {
|
|
331
|
+
const map = this.inputMaps[inputIndex];
|
|
332
|
+
map.forEach((_v, k, m) => {
|
|
333
|
+
if (this.isKeyType(k, keyTypes)) {
|
|
334
|
+
m.delete(k);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
copy(to: PsbtV2) {
|
|
340
|
+
this.copyMap(this.globalMap, to.globalMap);
|
|
341
|
+
this.copyMaps(this.inputMaps, to.inputMaps);
|
|
342
|
+
this.copyMaps(this.outputMaps, to.outputMaps);
|
|
343
|
+
}
|
|
344
|
+
copyMaps(
|
|
345
|
+
from: readonly ReadonlyMap<string, Buffer>[],
|
|
346
|
+
to: Map<string, Buffer>[]
|
|
347
|
+
) {
|
|
348
|
+
from.forEach((m, index) => {
|
|
349
|
+
const to_index = new Map();
|
|
350
|
+
this.copyMap(m, to_index);
|
|
351
|
+
to[index] = to_index;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
copyMap(from: ReadonlyMap<string, Buffer>, to: Map<string, Buffer>) {
|
|
355
|
+
from.forEach((v, k) => to.set(k, Buffer.from(v)));
|
|
356
|
+
}
|
|
357
|
+
serialize(): Buffer {
|
|
358
|
+
const buf = new BufferWriter();
|
|
359
|
+
buf.writeSlice(Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]));
|
|
360
|
+
serializeMap(buf, this.globalMap);
|
|
361
|
+
this.inputMaps.forEach((map) => {
|
|
362
|
+
serializeMap(buf, map);
|
|
363
|
+
});
|
|
364
|
+
this.outputMaps.forEach((map) => {
|
|
365
|
+
serializeMap(buf, map);
|
|
366
|
+
});
|
|
367
|
+
return buf.buffer();
|
|
368
|
+
}
|
|
369
|
+
deserialize(psbt: Buffer) {
|
|
370
|
+
const buf = new BufferReader(psbt);
|
|
371
|
+
if (!buf.readSlice(5).equals(PSBT_MAGIC_BYTES)) {
|
|
372
|
+
throw new Error('Invalid magic bytes');
|
|
373
|
+
}
|
|
374
|
+
while (this.readKeyPair(this.globalMap, buf));
|
|
375
|
+
|
|
376
|
+
let psbtVersion: number;
|
|
377
|
+
try {
|
|
378
|
+
psbtVersion = this.getGlobalPsbtVersion();
|
|
379
|
+
} catch {
|
|
380
|
+
psbtVersion = 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (psbtVersion !== 0 && psbtVersion !== 2)
|
|
384
|
+
throw new Error('Only PSBTs of version 0 or 2 are supported');
|
|
385
|
+
|
|
386
|
+
let nInputs: number;
|
|
387
|
+
let nOutputs: number;
|
|
388
|
+
if (psbtVersion == 0) {
|
|
389
|
+
// if PSBTv0, we parse the PSBT_GLOBAL_UNSIGNED_TX field
|
|
390
|
+
const txRaw = this.getGlobal(psbtGlobal.UNSIGNED_TX);
|
|
391
|
+
const tx = Transaction.fromBuffer(txRaw);
|
|
392
|
+
nInputs = tx.ins.length;
|
|
393
|
+
nOutputs = tx.outs.length;
|
|
394
|
+
} else {
|
|
395
|
+
// if PSBTv2, we already have the counts
|
|
396
|
+
nInputs = this.getGlobalInputCount();
|
|
397
|
+
nOutputs = this.getGlobalOutputCount();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
for (let i = 0; i < nInputs; i++) {
|
|
401
|
+
this.inputMaps[i] = new Map();
|
|
402
|
+
while (this.readKeyPair(this.inputMaps[i], buf));
|
|
403
|
+
}
|
|
404
|
+
for (let i = 0; i < nOutputs; i++) {
|
|
405
|
+
this.outputMaps[i] = new Map();
|
|
406
|
+
while (this.readKeyPair(this.outputMaps[i], buf));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this.normalizeToV2();
|
|
410
|
+
}
|
|
411
|
+
normalizeToV2() {
|
|
412
|
+
// if the psbt is a PsbtV0, convert it to PsbtV2 instead.
|
|
413
|
+
// throw an error for any version other than 0 or 2,
|
|
414
|
+
const psbtVersion = this.getGlobalOptional(psbtGlobal.VERSION)?.readInt32LE(
|
|
415
|
+
0
|
|
416
|
+
);
|
|
417
|
+
if (psbtVersion === 2) return;
|
|
418
|
+
else if (psbtVersion !== undefined) {
|
|
419
|
+
throw new Error('Invalid or unsupported value for PSBT_GLOBAL_VERSION');
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Convert PsbtV0 to PsbtV2 by parsing the PSBT_GLOBAL_UNSIGNED_TX field
|
|
423
|
+
// and filling in the corresponding fields.
|
|
424
|
+
const txRaw = this.getGlobal(psbtGlobal.UNSIGNED_TX);
|
|
425
|
+
const tx = Transaction.fromBuffer(txRaw);
|
|
426
|
+
|
|
427
|
+
this.setGlobalPsbtVersion(2);
|
|
428
|
+
this.setGlobalTxVersion(tx.version);
|
|
429
|
+
this.setGlobalFallbackLocktime(tx.locktime);
|
|
430
|
+
this.setGlobalInputCount(tx.ins.length);
|
|
431
|
+
this.setGlobalOutputCount(tx.outs.length);
|
|
432
|
+
|
|
433
|
+
for (let i = 0; i < tx.ins.length; i++) {
|
|
434
|
+
this.setInputPreviousTxId(i, Buffer.from(tx.ins[i].hash));
|
|
435
|
+
this.setInputOutputIndex(i, tx.ins[i].index);
|
|
436
|
+
this.setInputSequence(i, tx.ins[i].sequence);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
for (let i = 0; i < tx.outs.length; i++) {
|
|
440
|
+
this.setOutputAmount(i, Number(tx.outs[i].value));
|
|
441
|
+
this.setOutputScript(i, Buffer.from(tx.outs[i].script));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// PSBT_GLOBAL_UNSIGNED_TX must be removed in a valid PSBTv2
|
|
445
|
+
this.globalMap.delete(psbtGlobal.UNSIGNED_TX.toString(16).padStart(2, '0'));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Imports a BitcoinJS (bitcoinjs-lib) Psbt object.
|
|
449
|
+
* https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/psbt.ts
|
|
450
|
+
*
|
|
451
|
+
* Prepares the fields required for signing a Psbt on a Ledger
|
|
452
|
+
* device. It should be used exclusively before calling
|
|
453
|
+
* `appClient.signPsbt()` and not as a general Psbt conversion method.
|
|
454
|
+
*
|
|
455
|
+
* Note: This method supports all the policies that the Ledger is able to
|
|
456
|
+
* sign, with the exception of taproot: tr(@0).
|
|
457
|
+
*/
|
|
458
|
+
fromBitcoinJS(psbtBJS: bjs.Psbt): PsbtV2 {
|
|
459
|
+
function isTaprootInput(input: any): boolean {
|
|
460
|
+
let isP2TR;
|
|
461
|
+
try {
|
|
462
|
+
bjs.payments.p2tr({ output: input.witnessUtxo.script });
|
|
463
|
+
isP2TR = true;
|
|
464
|
+
} catch (err) {
|
|
465
|
+
isP2TR = false;
|
|
466
|
+
}
|
|
467
|
+
return (
|
|
468
|
+
input &&
|
|
469
|
+
!!(
|
|
470
|
+
input.tapInternalKey ||
|
|
471
|
+
input.tapMerkleRoot ||
|
|
472
|
+
(input.tapLeafScript && input.tapLeafScript.length) ||
|
|
473
|
+
(input.tapBip32Derivation && input.tapBip32Derivation.length) ||
|
|
474
|
+
isP2TR
|
|
475
|
+
)
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
this.setGlobalPsbtVersion(2);
|
|
479
|
+
this.setGlobalTxVersion(psbtBJS.version);
|
|
480
|
+
this.setGlobalInputCount(psbtBJS.data.inputs.length);
|
|
481
|
+
this.setGlobalOutputCount(psbtBJS.txOutputs.length);
|
|
482
|
+
if (psbtBJS.locktime !== undefined)
|
|
483
|
+
this.setGlobalFallbackLocktime(psbtBJS.locktime);
|
|
484
|
+
psbtBJS.data.inputs.forEach((input, index) => {
|
|
485
|
+
if (isTaprootInput(input))
|
|
486
|
+
throw new Error(`Taproot inputs not supported`);
|
|
487
|
+
this.setInputPreviousTxId(
|
|
488
|
+
index,
|
|
489
|
+
Buffer.from(psbtBJS.txInputs[index].hash)
|
|
490
|
+
);
|
|
491
|
+
if (psbtBJS.txInputs[index].sequence !== undefined)
|
|
492
|
+
// @ts-ignore
|
|
493
|
+
this.setInputSequence(index, psbtBJS.txInputs[index].sequence);
|
|
494
|
+
this.setInputOutputIndex(index, psbtBJS.txInputs[index].index);
|
|
495
|
+
if (input.sighashType !== undefined)
|
|
496
|
+
this.setInputSighashType(index, input.sighashType);
|
|
497
|
+
if (input.nonWitnessUtxo)
|
|
498
|
+
this.setInputNonWitnessUtxo(index, Buffer.from(input.nonWitnessUtxo));
|
|
499
|
+
if (input.witnessUtxo) {
|
|
500
|
+
this.setInputWitnessUtxo(
|
|
501
|
+
index,
|
|
502
|
+
Number(input.witnessUtxo.value),
|
|
503
|
+
Buffer.from(input.witnessUtxo.script)
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
if (input.witnessScript)
|
|
507
|
+
this.setInputWitnessScript(index, Buffer.from(input.witnessScript));
|
|
508
|
+
if (input.redeemScript)
|
|
509
|
+
this.setInputRedeemScript(index, Buffer.from(input.redeemScript));
|
|
510
|
+
// @ts-ignore
|
|
511
|
+
psbtBJS.data.inputs[index].bip32Derivation.forEach((derivation) => {
|
|
512
|
+
if (!/^m\//i.test(derivation.path))
|
|
513
|
+
throw new Error(`Invalid input bip32 derivation`);
|
|
514
|
+
const pathArray = derivation.path
|
|
515
|
+
.replace(/m\//i, '')
|
|
516
|
+
.split('/')
|
|
517
|
+
.map((level) =>
|
|
518
|
+
level.match(/['h]/i) ? parseInt(level) + 0x80000000 : Number(level)
|
|
519
|
+
);
|
|
520
|
+
this.setInputBip32Derivation(
|
|
521
|
+
index,
|
|
522
|
+
Buffer.from(derivation.pubkey),
|
|
523
|
+
Buffer.from(derivation.masterFingerprint),
|
|
524
|
+
pathArray
|
|
525
|
+
);
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
psbtBJS.txOutputs.forEach((output, index) => {
|
|
529
|
+
this.setOutputAmount(index, Number(output.value));
|
|
530
|
+
this.setOutputScript(index, Buffer.from(output.script));
|
|
531
|
+
});
|
|
532
|
+
return this;
|
|
533
|
+
}
|
|
534
|
+
private readKeyPair(map: Map<string, Buffer>, buf: BufferReader): boolean {
|
|
535
|
+
const keyLen = sanitizeBigintToNumber(buf.readVarInt());
|
|
536
|
+
if (keyLen == 0) {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
const keyType = buf.readUInt8();
|
|
540
|
+
const keyData = buf.readSlice(keyLen - 1);
|
|
541
|
+
const value = buf.readVarSlice();
|
|
542
|
+
set(map, keyType, keyData, value);
|
|
543
|
+
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
private getKeyDatas(
|
|
547
|
+
map: ReadonlyMap<string, Buffer>,
|
|
548
|
+
keyType: KeyType
|
|
549
|
+
): readonly Buffer[] {
|
|
550
|
+
const result: Buffer[] = [];
|
|
551
|
+
map.forEach((_v, k) => {
|
|
552
|
+
if (this.isKeyType(k, [keyType])) {
|
|
553
|
+
result.push(Buffer.from(k.substring(2), 'hex'));
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
return result;
|
|
557
|
+
}
|
|
558
|
+
private isKeyType(hexKey: string, keyTypes: readonly KeyType[]): boolean {
|
|
559
|
+
const keyType = Buffer.from(hexKey.substring(0, 2), 'hex').readUInt8(0);
|
|
560
|
+
return keyTypes.some((k) => k == keyType);
|
|
561
|
+
}
|
|
562
|
+
private setGlobal(keyType: KeyType, value: Buffer) {
|
|
563
|
+
const key = new Key(keyType, Buffer.from([]));
|
|
564
|
+
this.globalMap.set(key.toString(), value);
|
|
565
|
+
}
|
|
566
|
+
private getGlobal(keyType: KeyType): Buffer {
|
|
567
|
+
return get(this.globalMap, keyType, b(), false)!;
|
|
568
|
+
}
|
|
569
|
+
private getGlobalOptional(keyType: KeyType): Buffer | undefined {
|
|
570
|
+
return get(this.globalMap, keyType, b(), true);
|
|
571
|
+
}
|
|
572
|
+
private setInput(
|
|
573
|
+
index: number,
|
|
574
|
+
keyType: KeyType,
|
|
575
|
+
keyData: Buffer,
|
|
576
|
+
value: Buffer
|
|
577
|
+
) {
|
|
578
|
+
set(this.getMap(index, this.inputMaps), keyType, keyData, value);
|
|
579
|
+
}
|
|
580
|
+
private getInput(index: number, keyType: KeyType, keyData: Buffer): Buffer {
|
|
581
|
+
return get(this.inputMaps[index], keyType, keyData, false)!;
|
|
582
|
+
}
|
|
583
|
+
private getInputOptional(
|
|
584
|
+
index: number,
|
|
585
|
+
keyType: KeyType,
|
|
586
|
+
keyData: Buffer
|
|
587
|
+
): Buffer | undefined {
|
|
588
|
+
return get(this.inputMaps[index], keyType, keyData, true);
|
|
589
|
+
}
|
|
590
|
+
private setOutput(
|
|
591
|
+
index: number,
|
|
592
|
+
keyType: KeyType,
|
|
593
|
+
keyData: Buffer,
|
|
594
|
+
value: Buffer
|
|
595
|
+
) {
|
|
596
|
+
set(this.getMap(index, this.outputMaps), keyType, keyData, value);
|
|
597
|
+
}
|
|
598
|
+
private getOutput(index: number, keyType: KeyType, keyData: Buffer): Buffer {
|
|
599
|
+
return get(this.outputMaps[index], keyType, keyData, false)!;
|
|
600
|
+
}
|
|
601
|
+
private getMap(
|
|
602
|
+
index: number,
|
|
603
|
+
maps: Map<string, Buffer>[]
|
|
604
|
+
): Map<string, Buffer> {
|
|
605
|
+
if (maps[index]) {
|
|
606
|
+
return maps[index];
|
|
607
|
+
}
|
|
608
|
+
return (maps[index] = new Map());
|
|
609
|
+
}
|
|
610
|
+
private encodeBip32Derivation(
|
|
611
|
+
masterFingerprint: Buffer,
|
|
612
|
+
path: readonly number[]
|
|
613
|
+
) {
|
|
614
|
+
const buf = new BufferWriter();
|
|
615
|
+
this.writeBip32Derivation(buf, masterFingerprint, path);
|
|
616
|
+
return buf.buffer();
|
|
617
|
+
}
|
|
618
|
+
private decodeBip32Derivation(buffer: Buffer): {
|
|
619
|
+
readonly masterFingerprint: Buffer;
|
|
620
|
+
readonly path: readonly number[];
|
|
621
|
+
} {
|
|
622
|
+
const buf = new BufferReader(buffer);
|
|
623
|
+
return this.readBip32Derivation(buf);
|
|
624
|
+
}
|
|
625
|
+
private writeBip32Derivation(
|
|
626
|
+
buf: BufferWriter,
|
|
627
|
+
masterFingerprint: Buffer,
|
|
628
|
+
path: readonly number[]
|
|
629
|
+
) {
|
|
630
|
+
buf.writeSlice(masterFingerprint);
|
|
631
|
+
path.forEach((element) => {
|
|
632
|
+
buf.writeUInt32(element);
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
private readBip32Derivation(buf: BufferReader): {
|
|
636
|
+
readonly masterFingerprint: Buffer;
|
|
637
|
+
readonly path: readonly number[];
|
|
638
|
+
} {
|
|
639
|
+
const masterFingerprint = buf.readSlice(4);
|
|
640
|
+
const path: number[] = [];
|
|
641
|
+
while (buf.offset < buf.buffer.length) {
|
|
642
|
+
path.push(buf.readUInt32());
|
|
643
|
+
}
|
|
644
|
+
return { masterFingerprint, path };
|
|
645
|
+
}
|
|
646
|
+
private encodeTapBip32Derivation(
|
|
647
|
+
hashes: readonly Buffer[],
|
|
648
|
+
masterFingerprint: Buffer,
|
|
649
|
+
path: readonly number[]
|
|
650
|
+
): Buffer {
|
|
651
|
+
const buf = new BufferWriter();
|
|
652
|
+
buf.writeVarInt(hashes.length);
|
|
653
|
+
hashes.forEach((h) => {
|
|
654
|
+
buf.writeSlice(h);
|
|
655
|
+
});
|
|
656
|
+
this.writeBip32Derivation(buf, masterFingerprint, path);
|
|
657
|
+
return buf.buffer();
|
|
658
|
+
}
|
|
659
|
+
private decodeTapBip32Derivation(buffer: Buffer): {
|
|
660
|
+
readonly hashes: readonly Buffer[];
|
|
661
|
+
readonly masterFingerprint: Buffer;
|
|
662
|
+
readonly path: readonly number[];
|
|
663
|
+
} {
|
|
664
|
+
const buf = new BufferReader(buffer);
|
|
665
|
+
const hashCount = sanitizeBigintToNumber(buf.readVarInt());
|
|
666
|
+
const hashes: Buffer[] = [];
|
|
667
|
+
for (let i = 0; i < hashCount; i++) {
|
|
668
|
+
hashes.push(buf.readSlice(32));
|
|
669
|
+
}
|
|
670
|
+
const deriv = this.readBip32Derivation(buf);
|
|
671
|
+
return { hashes, ...deriv };
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function get(
|
|
675
|
+
map: ReadonlyMap<string, Buffer>,
|
|
676
|
+
keyType: KeyType,
|
|
677
|
+
keyData: Buffer,
|
|
678
|
+
acceptUndefined: boolean
|
|
679
|
+
): Buffer | undefined {
|
|
680
|
+
if (!map) throw Error('No such map');
|
|
681
|
+
const key = new Key(keyType, keyData);
|
|
682
|
+
const value = map.get(key.toString());
|
|
683
|
+
if (!value) {
|
|
684
|
+
if (acceptUndefined) {
|
|
685
|
+
return undefined;
|
|
686
|
+
}
|
|
687
|
+
throw new NoSuchEntry(key.toString());
|
|
688
|
+
}
|
|
689
|
+
// Make sure to return a copy, to protect the underlying data.
|
|
690
|
+
return Buffer.from(value);
|
|
691
|
+
}
|
|
692
|
+
type KeyType = number;
|
|
693
|
+
|
|
694
|
+
class Key {
|
|
695
|
+
readonly keyType: KeyType;
|
|
696
|
+
readonly keyData: Buffer;
|
|
697
|
+
constructor(keyType: KeyType, keyData: Buffer) {
|
|
698
|
+
this.keyType = keyType;
|
|
699
|
+
this.keyData = keyData;
|
|
700
|
+
}
|
|
701
|
+
toString(): string {
|
|
702
|
+
const buf = new BufferWriter();
|
|
703
|
+
this.toBuffer(buf);
|
|
704
|
+
return buf.buffer().toString('hex');
|
|
705
|
+
}
|
|
706
|
+
serialize(buf: BufferWriter) {
|
|
707
|
+
buf.writeVarInt(1 + this.keyData.length);
|
|
708
|
+
this.toBuffer(buf);
|
|
709
|
+
}
|
|
710
|
+
private toBuffer(buf: BufferWriter) {
|
|
711
|
+
buf.writeUInt8(this.keyType);
|
|
712
|
+
buf.writeSlice(this.keyData);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
class KeyPair {
|
|
716
|
+
readonly key: Key;
|
|
717
|
+
readonly value: Buffer;
|
|
718
|
+
constructor(key: Key, value: Buffer) {
|
|
719
|
+
this.key = key;
|
|
720
|
+
this.value = value;
|
|
721
|
+
}
|
|
722
|
+
serialize(buf: BufferWriter) {
|
|
723
|
+
this.key.serialize(buf);
|
|
724
|
+
buf.writeVarSlice(this.value);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
function createKey(buf: Buffer): Key {
|
|
728
|
+
return new Key(buf.readUInt8(0), buf.slice(1));
|
|
729
|
+
}
|
|
730
|
+
function serializeMap(buf: BufferWriter, map: ReadonlyMap<string, Buffer>) {
|
|
731
|
+
// serialize in lexicographical order of keys
|
|
732
|
+
for (const [key, value] of [...map].sort(([k1], [k2]) =>
|
|
733
|
+
k1.localeCompare(k2)
|
|
734
|
+
)) {
|
|
735
|
+
const keyPair = new KeyPair(createKey(Buffer.from(key, 'hex')), value);
|
|
736
|
+
keyPair.serialize(buf);
|
|
737
|
+
}
|
|
738
|
+
buf.writeUInt8(0);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function b(): Buffer {
|
|
742
|
+
return Buffer.from([]);
|
|
743
|
+
}
|
|
744
|
+
function set(
|
|
745
|
+
map: Map<string, Buffer>,
|
|
746
|
+
keyType: KeyType,
|
|
747
|
+
keyData: Buffer,
|
|
748
|
+
value: Buffer
|
|
749
|
+
) {
|
|
750
|
+
const key = new Key(keyType, keyData);
|
|
751
|
+
map.set(key.toString(), value);
|
|
752
|
+
}
|
|
753
|
+
function uint32LE(n: number): Buffer {
|
|
754
|
+
const buf = Buffer.alloc(4);
|
|
755
|
+
buf.writeUInt32LE(n, 0);
|
|
756
|
+
return buf;
|
|
757
|
+
}
|
|
758
|
+
function uint64LE(n: number): Buffer {
|
|
759
|
+
return unsafeTo64bitLE(n);
|
|
760
|
+
}
|
|
761
|
+
function varint(n: number): Buffer {
|
|
762
|
+
const buf = new BufferWriter();
|
|
763
|
+
buf.writeVarInt(n);
|
|
764
|
+
return buf.buffer();
|
|
765
|
+
}
|
|
766
|
+
function fromVarint(buf: Buffer): number {
|
|
767
|
+
return sanitizeBigintToNumber(new BufferReader(buf).readVarInt());
|
|
768
|
+
}
|