@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.
@@ -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.20.0",
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.20.0",
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": "814218180de877f1c4128af1b9e2dd0fb7dfb0c7"
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");