@ledgerhq/hw-app-btc 6.20.0 → 6.25.1-alpha.3
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 +4 -4
- package/lib/newops/clientCommands.js +4 -4
- package/lib/newops/clientCommands.js.map +1 -1
- package/lib-es/Btc.js.flow +280 -0
- package/lib-es/bip32.js.flow +13 -0
- package/lib-es/compressPublicKey.js.flow +8 -0
- package/lib-es/constants.js.flow +13 -0
- package/lib-es/createTransaction.js.flow +419 -0
- package/lib-es/debug.js.flow +41 -0
- package/lib-es/finalizeInput.js.flow +38 -0
- package/lib-es/getAppAndVersion.js.flow +19 -0
- package/lib-es/getTrustedInput.js.flow +163 -0
- package/lib-es/getTrustedInputBIP143.js.flow +37 -0
- package/lib-es/getWalletPublicKey.js.flow +51 -0
- package/lib-es/hashPublicKey.js.flow +8 -0
- package/lib-es/newops/clientCommands.js +4 -4
- package/lib-es/newops/clientCommands.js.map +1 -1
- package/lib-es/serializeTransaction.js.flow +77 -0
- package/lib-es/shouldUseTrustedInputForSegwit.js.flow +15 -0
- package/lib-es/signMessage.js.flow +67 -0
- package/lib-es/signP2SHTransaction.js.flow +170 -0
- package/lib-es/signTransaction.js.flow +40 -0
- package/lib-es/splitTransaction.js.flow +145 -0
- package/lib-es/startUntrustedHashTransactionInput.js.flow +136 -0
- package/lib-es/types.js.flow +31 -0
- package/lib-es/varint.js.flow +43 -0
- package/package.json +3 -3
- package/src/newops/clientCommands.ts +4 -4
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { log } from "@ledgerhq/logs";
|
|
3
|
+
import type { Transaction } from "./types";
|
|
4
|
+
import { getVarint } from "./varint";
|
|
5
|
+
import { formatTransactionDebug } from "./debug";
|
|
6
|
+
|
|
7
|
+
export function splitTransaction(
|
|
8
|
+
transactionHex: string,
|
|
9
|
+
isSegwitSupported: ?boolean = false,
|
|
10
|
+
hasTimestamp?: boolean = false,
|
|
11
|
+
hasExtraData?: boolean = false,
|
|
12
|
+
additionals: Array<string> = []
|
|
13
|
+
): Transaction {
|
|
14
|
+
const inputs = [];
|
|
15
|
+
const outputs = [];
|
|
16
|
+
var witness = false;
|
|
17
|
+
let offset = 0;
|
|
18
|
+
let timestamp = Buffer.alloc(0);
|
|
19
|
+
let nExpiryHeight = Buffer.alloc(0);
|
|
20
|
+
let nVersionGroupId = Buffer.alloc(0);
|
|
21
|
+
let extraData = Buffer.alloc(0);
|
|
22
|
+
const isDecred = additionals.includes("decred");
|
|
23
|
+
const transaction = Buffer.from(transactionHex, "hex");
|
|
24
|
+
const version = transaction.slice(offset, offset + 4);
|
|
25
|
+
const overwinter =
|
|
26
|
+
version.equals(Buffer.from([0x03, 0x00, 0x00, 0x80])) ||
|
|
27
|
+
version.equals(Buffer.from([0x04, 0x00, 0x00, 0x80]));
|
|
28
|
+
offset += 4;
|
|
29
|
+
if (
|
|
30
|
+
!hasTimestamp &&
|
|
31
|
+
isSegwitSupported &&
|
|
32
|
+
transaction[offset] === 0 &&
|
|
33
|
+
transaction[offset + 1] !== 0
|
|
34
|
+
) {
|
|
35
|
+
offset += 2;
|
|
36
|
+
witness = true;
|
|
37
|
+
}
|
|
38
|
+
if (hasTimestamp) {
|
|
39
|
+
timestamp = transaction.slice(offset, 4 + offset);
|
|
40
|
+
offset += 4;
|
|
41
|
+
}
|
|
42
|
+
if (overwinter) {
|
|
43
|
+
nVersionGroupId = transaction.slice(offset, 4 + offset);
|
|
44
|
+
offset += 4;
|
|
45
|
+
}
|
|
46
|
+
let varint = getVarint(transaction, offset);
|
|
47
|
+
const numberInputs = varint[0];
|
|
48
|
+
offset += varint[1];
|
|
49
|
+
for (let i = 0; i < numberInputs; i++) {
|
|
50
|
+
const prevout = transaction.slice(offset, offset + 36);
|
|
51
|
+
offset += 36;
|
|
52
|
+
let script = Buffer.alloc(0);
|
|
53
|
+
let tree = Buffer.alloc(0);
|
|
54
|
+
//No script for decred, it has a witness
|
|
55
|
+
if (!isDecred) {
|
|
56
|
+
varint = getVarint(transaction, offset);
|
|
57
|
+
offset += varint[1];
|
|
58
|
+
script = transaction.slice(offset, offset + varint[0]);
|
|
59
|
+
offset += varint[0];
|
|
60
|
+
} else {
|
|
61
|
+
//Tree field
|
|
62
|
+
tree = transaction.slice(offset, offset + 1);
|
|
63
|
+
offset += 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const sequence = transaction.slice(offset, offset + 4);
|
|
67
|
+
offset += 4;
|
|
68
|
+
inputs.push({ prevout, script, sequence, tree });
|
|
69
|
+
}
|
|
70
|
+
varint = getVarint(transaction, offset);
|
|
71
|
+
const numberOutputs = varint[0];
|
|
72
|
+
offset += varint[1];
|
|
73
|
+
for (let i = 0; i < numberOutputs; i++) {
|
|
74
|
+
const amount = transaction.slice(offset, offset + 8);
|
|
75
|
+
offset += 8;
|
|
76
|
+
|
|
77
|
+
if (isDecred) {
|
|
78
|
+
//Script version
|
|
79
|
+
offset += 2;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
varint = getVarint(transaction, offset);
|
|
83
|
+
offset += varint[1];
|
|
84
|
+
const script = transaction.slice(offset, offset + varint[0]);
|
|
85
|
+
offset += varint[0];
|
|
86
|
+
outputs.push({ amount, script });
|
|
87
|
+
}
|
|
88
|
+
let witnessScript, locktime;
|
|
89
|
+
if (witness) {
|
|
90
|
+
witnessScript = transaction.slice(offset, -4);
|
|
91
|
+
locktime = transaction.slice(transaction.length - 4);
|
|
92
|
+
} else {
|
|
93
|
+
locktime = transaction.slice(offset, offset + 4);
|
|
94
|
+
}
|
|
95
|
+
offset += 4;
|
|
96
|
+
if (overwinter || isDecred) {
|
|
97
|
+
nExpiryHeight = transaction.slice(offset, offset + 4);
|
|
98
|
+
offset += 4;
|
|
99
|
+
}
|
|
100
|
+
if (hasExtraData) {
|
|
101
|
+
extraData = transaction.slice(offset);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//Get witnesses for Decred
|
|
105
|
+
if (isDecred) {
|
|
106
|
+
varint = getVarint(transaction, offset);
|
|
107
|
+
offset += varint[1];
|
|
108
|
+
if (varint[0] !== numberInputs) {
|
|
109
|
+
throw new Error("splitTransaction: incoherent number of witnesses");
|
|
110
|
+
}
|
|
111
|
+
for (let i = 0; i < numberInputs; i++) {
|
|
112
|
+
//amount
|
|
113
|
+
offset += 8;
|
|
114
|
+
//block height
|
|
115
|
+
offset += 4;
|
|
116
|
+
//block index
|
|
117
|
+
offset += 4;
|
|
118
|
+
//Script size
|
|
119
|
+
varint = getVarint(transaction, offset);
|
|
120
|
+
offset += varint[1];
|
|
121
|
+
const script = transaction.slice(offset, offset + varint[0]);
|
|
122
|
+
offset += varint[0];
|
|
123
|
+
inputs[i].script = script;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const t: Transaction = {
|
|
128
|
+
version,
|
|
129
|
+
inputs,
|
|
130
|
+
outputs,
|
|
131
|
+
locktime,
|
|
132
|
+
witness: witnessScript,
|
|
133
|
+
timestamp,
|
|
134
|
+
nVersionGroupId,
|
|
135
|
+
nExpiryHeight,
|
|
136
|
+
extraData,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
log(
|
|
140
|
+
"btc",
|
|
141
|
+
`splitTransaction ${transactionHex}:\n${formatTransactionDebug(t)}`
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return t;
|
|
145
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type Transport from "@ledgerhq/hw-transport";
|
|
3
|
+
import type { Transaction } from "./types";
|
|
4
|
+
import { createVarint } from "./varint";
|
|
5
|
+
import { MAX_SCRIPT_BLOCK } from "./constants";
|
|
6
|
+
|
|
7
|
+
export function startUntrustedHashTransactionInputRaw(
|
|
8
|
+
transport: Transport<*>,
|
|
9
|
+
newTransaction: boolean,
|
|
10
|
+
firstRound: boolean,
|
|
11
|
+
transactionData: Buffer,
|
|
12
|
+
bip143?: boolean = false,
|
|
13
|
+
overwinter?: boolean = false,
|
|
14
|
+
additionals: Array<string> = []
|
|
15
|
+
) {
|
|
16
|
+
const p2 = additionals.includes("cashaddr")
|
|
17
|
+
? 0x03
|
|
18
|
+
: bip143
|
|
19
|
+
? additionals.includes("sapling")
|
|
20
|
+
? 0x05
|
|
21
|
+
: overwinter
|
|
22
|
+
? 0x04
|
|
23
|
+
: 0x02
|
|
24
|
+
: 0x00;
|
|
25
|
+
return transport.send(
|
|
26
|
+
0xe0,
|
|
27
|
+
0x44,
|
|
28
|
+
firstRound ? 0x00 : 0x80,
|
|
29
|
+
newTransaction ? p2 : 0x80,
|
|
30
|
+
transactionData
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function startUntrustedHashTransactionInput(
|
|
35
|
+
transport: Transport<*>,
|
|
36
|
+
newTransaction: boolean,
|
|
37
|
+
transaction: Transaction,
|
|
38
|
+
inputs: Array<{ trustedInput: boolean, value: Buffer }>,
|
|
39
|
+
bip143?: boolean = false,
|
|
40
|
+
overwinter?: boolean = false,
|
|
41
|
+
additionals: Array<string> = [],
|
|
42
|
+
useTrustedInputForSegwit?: boolean = false
|
|
43
|
+
) {
|
|
44
|
+
let data = Buffer.concat([
|
|
45
|
+
transaction.version,
|
|
46
|
+
transaction.timestamp || Buffer.alloc(0),
|
|
47
|
+
transaction.nVersionGroupId || Buffer.alloc(0),
|
|
48
|
+
createVarint(transaction.inputs.length),
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
await startUntrustedHashTransactionInputRaw(
|
|
52
|
+
transport,
|
|
53
|
+
newTransaction,
|
|
54
|
+
true,
|
|
55
|
+
data,
|
|
56
|
+
bip143,
|
|
57
|
+
overwinter,
|
|
58
|
+
additionals
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
let i = 0;
|
|
62
|
+
const isDecred = additionals.includes("decred");
|
|
63
|
+
|
|
64
|
+
for (let input of transaction.inputs) {
|
|
65
|
+
let prefix;
|
|
66
|
+
let inputValue = inputs[i].value;
|
|
67
|
+
if (bip143) {
|
|
68
|
+
if (useTrustedInputForSegwit && inputs[i].trustedInput) {
|
|
69
|
+
prefix = Buffer.from([0x01, inputValue.length]);
|
|
70
|
+
} else {
|
|
71
|
+
prefix = Buffer.from([0x02]);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
if (inputs[i].trustedInput) {
|
|
75
|
+
prefix = Buffer.from([0x01, inputs[i].value.length]);
|
|
76
|
+
} else {
|
|
77
|
+
prefix = Buffer.from([0x00]);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
data = Buffer.concat([
|
|
81
|
+
prefix,
|
|
82
|
+
inputValue,
|
|
83
|
+
isDecred ? Buffer.from([0x00]) : Buffer.alloc(0),
|
|
84
|
+
createVarint(input.script.length),
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
await startUntrustedHashTransactionInputRaw(
|
|
88
|
+
transport,
|
|
89
|
+
newTransaction,
|
|
90
|
+
false,
|
|
91
|
+
data,
|
|
92
|
+
bip143,
|
|
93
|
+
overwinter,
|
|
94
|
+
additionals
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
let scriptBlocks = [];
|
|
98
|
+
let offset = 0;
|
|
99
|
+
|
|
100
|
+
if (input.script.length === 0) {
|
|
101
|
+
scriptBlocks.push(input.sequence);
|
|
102
|
+
} else {
|
|
103
|
+
while (offset !== input.script.length) {
|
|
104
|
+
let blockSize =
|
|
105
|
+
input.script.length - offset > MAX_SCRIPT_BLOCK
|
|
106
|
+
? MAX_SCRIPT_BLOCK
|
|
107
|
+
: input.script.length - offset;
|
|
108
|
+
if (offset + blockSize !== input.script.length) {
|
|
109
|
+
scriptBlocks.push(input.script.slice(offset, offset + blockSize));
|
|
110
|
+
} else {
|
|
111
|
+
scriptBlocks.push(
|
|
112
|
+
Buffer.concat([
|
|
113
|
+
input.script.slice(offset, offset + blockSize),
|
|
114
|
+
input.sequence,
|
|
115
|
+
])
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
offset += blockSize;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (let scriptBlock of scriptBlocks) {
|
|
123
|
+
await startUntrustedHashTransactionInputRaw(
|
|
124
|
+
transport,
|
|
125
|
+
newTransaction,
|
|
126
|
+
false,
|
|
127
|
+
scriptBlock,
|
|
128
|
+
bip143,
|
|
129
|
+
overwinter,
|
|
130
|
+
additionals
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*/
|
|
5
|
+
export type TransactionInput = {
|
|
6
|
+
prevout: Buffer,
|
|
7
|
+
script: Buffer,
|
|
8
|
+
sequence: Buffer,
|
|
9
|
+
tree?: Buffer,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*/
|
|
14
|
+
export type TransactionOutput = {
|
|
15
|
+
amount: Buffer,
|
|
16
|
+
script: Buffer,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*/
|
|
21
|
+
export type Transaction = {
|
|
22
|
+
version: Buffer,
|
|
23
|
+
inputs: TransactionInput[],
|
|
24
|
+
outputs?: TransactionOutput[],
|
|
25
|
+
locktime?: Buffer,
|
|
26
|
+
witness?: Buffer,
|
|
27
|
+
timestamp?: Buffer,
|
|
28
|
+
nVersionGroupId?: Buffer,
|
|
29
|
+
nExpiryHeight?: Buffer,
|
|
30
|
+
extraData?: Buffer,
|
|
31
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export function getVarint(data: Buffer, offset: number): [number, number] {
|
|
4
|
+
if (data[offset] < 0xfd) {
|
|
5
|
+
return [data[offset], 1];
|
|
6
|
+
}
|
|
7
|
+
if (data[offset] === 0xfd) {
|
|
8
|
+
return [(data[offset + 2] << 8) + data[offset + 1], 3];
|
|
9
|
+
}
|
|
10
|
+
if (data[offset] === 0xfe) {
|
|
11
|
+
return [
|
|
12
|
+
(data[offset + 4] << 24) +
|
|
13
|
+
(data[offset + 3] << 16) +
|
|
14
|
+
(data[offset + 2] << 8) +
|
|
15
|
+
data[offset + 1],
|
|
16
|
+
5,
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw new Error("getVarint called with unexpected parameters");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createVarint(value: number): Buffer {
|
|
24
|
+
if (value < 0xfd) {
|
|
25
|
+
const buffer = Buffer.alloc(1);
|
|
26
|
+
buffer[0] = value;
|
|
27
|
+
return buffer;
|
|
28
|
+
}
|
|
29
|
+
if (value <= 0xffff) {
|
|
30
|
+
const buffer = Buffer.alloc(3);
|
|
31
|
+
buffer[0] = 0xfd;
|
|
32
|
+
buffer[1] = value & 0xff;
|
|
33
|
+
buffer[2] = (value >> 8) & 0xff;
|
|
34
|
+
return buffer;
|
|
35
|
+
}
|
|
36
|
+
const buffer = Buffer.alloc(5);
|
|
37
|
+
buffer[0] = 0xfe;
|
|
38
|
+
buffer[1] = value & 0xff;
|
|
39
|
+
buffer[2] = (value >> 8) & 0xff;
|
|
40
|
+
buffer[3] = (value >> 16) & 0xff;
|
|
41
|
+
buffer[4] = (value >> 24) & 0xff;
|
|
42
|
+
return buffer;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/hw-app-btc",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.25.1-alpha.3+eb669e17",
|
|
4
4
|
"description": "Ledger Hardware Wallet Bitcoin Application API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"types": "lib/Btc.d.ts",
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ledgerhq/hw-transport": "^6.
|
|
30
|
+
"@ledgerhq/hw-transport": "^6.25.1-alpha.3+eb669e17",
|
|
31
31
|
"@ledgerhq/logs": "^6.10.0",
|
|
32
32
|
"bip32-path": "^0.4.2",
|
|
33
33
|
"bitcoinjs-lib": "^5.2.0",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"watch": "bash ../../script/watch.sh",
|
|
46
46
|
"doc": "bash ../../script/doc.sh"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "eb669e17dd87d3ab568beab1f9a5ddb1a2536e83"
|
|
49
49
|
}
|
|
@@ -47,7 +47,7 @@ export class GetPreimageCommand extends ClientCommand {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
execute(request: Buffer): Buffer {
|
|
50
|
-
const req = request.subarray(1);
|
|
50
|
+
const req = Buffer.from(request.subarray(1));
|
|
51
51
|
|
|
52
52
|
// we expect no more data to read
|
|
53
53
|
if (req.length != 1 + 32) {
|
|
@@ -84,7 +84,7 @@ export class GetPreimageCommand extends ClientCommand {
|
|
|
84
84
|
return Buffer.concat([
|
|
85
85
|
preimage_len_varint,
|
|
86
86
|
Buffer.from([payload_size]),
|
|
87
|
-
known_preimage.subarray(0, payload_size),
|
|
87
|
+
Buffer.from(known_preimage.subarray(0, payload_size)),
|
|
88
88
|
]);
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -105,7 +105,7 @@ export class GetMerkleLeafProofCommand extends ClientCommand {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
execute(request: Buffer): Buffer {
|
|
108
|
-
const req = request.subarray(1);
|
|
108
|
+
const req = Buffer.from(request.subarray(1));
|
|
109
109
|
|
|
110
110
|
if (req.length < 32 + 1 + 1) {
|
|
111
111
|
throw new Error("Invalid request, expected at least 34 bytes");
|
|
@@ -174,7 +174,7 @@ export class GetMerkleLeafIndexCommand extends ClientCommand {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
execute(request: Buffer): Buffer {
|
|
177
|
-
const req = request.subarray(1);
|
|
177
|
+
const req = Buffer.from(request.subarray(1));
|
|
178
178
|
|
|
179
179
|
if (req.length != 32 + 32) {
|
|
180
180
|
throw new Error("Invalid request, unexpected trailing data");
|