@ledgerhq/hw-app-btc 10.9.4-nightly.0 → 10.10.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -2
- package/README.md +29 -0
- package/lib/constants.d.ts +3 -0
- package/lib/constants.d.ts.map +1 -1
- package/lib/constants.js +4 -1
- package/lib/constants.js.map +1 -1
- package/lib/getTrustedInput.d.ts +3 -1
- package/lib/getTrustedInput.d.ts.map +1 -1
- package/lib/getTrustedInput.js +89 -1
- package/lib/getTrustedInput.js.map +1 -1
- package/lib/splitTransaction.d.ts.map +1 -1
- package/lib/splitTransaction.js +175 -1
- package/lib/splitTransaction.js.map +1 -1
- package/lib/types.d.ts +45 -0
- package/lib/types.d.ts.map +1 -1
- package/lib-es/constants.d.ts +3 -0
- package/lib-es/constants.d.ts.map +1 -1
- package/lib-es/constants.js +3 -0
- package/lib-es/constants.js.map +1 -1
- package/lib-es/getTrustedInput.d.ts +3 -1
- package/lib-es/getTrustedInput.d.ts.map +1 -1
- package/lib-es/getTrustedInput.js +87 -1
- package/lib-es/getTrustedInput.js.map +1 -1
- package/lib-es/splitTransaction.d.ts.map +1 -1
- package/lib-es/splitTransaction.js +175 -1
- package/lib-es/splitTransaction.js.map +1 -1
- package/lib-es/types.d.ts +45 -0
- package/lib-es/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/constants.ts +4 -0
- package/src/getTrustedInput.ts +102 -3
- package/src/splitTransaction.ts +235 -2
- package/src/types.ts +51 -0
- package/tests/createTransaction.test.ts +300 -0
- package/tests/splitTransaction.test.ts +182 -6
package/src/splitTransaction.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import { log } from "@ledgerhq/logs";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
Transaction,
|
|
4
|
+
TransactionInput,
|
|
5
|
+
TransactionOutput,
|
|
6
|
+
SaplingData,
|
|
7
|
+
SaplingSpendDescriptionV5,
|
|
8
|
+
SaplingOutputDescriptionV5,
|
|
9
|
+
OrchardAction,
|
|
10
|
+
OrchardData,
|
|
11
|
+
} from "./types";
|
|
3
12
|
import { getVarint } from "./varint";
|
|
4
13
|
import { formatTransactionDebug } from "./debug";
|
|
14
|
+
import {
|
|
15
|
+
zCashOutCiphertextSize,
|
|
16
|
+
zCashEncCiphertextSize,
|
|
17
|
+
zCashProofsSaplingSize,
|
|
18
|
+
} from "./constants";
|
|
5
19
|
|
|
6
20
|
export function splitTransaction(
|
|
7
21
|
transactionHex: string,
|
|
@@ -101,6 +115,18 @@ export function splitTransaction(
|
|
|
101
115
|
});
|
|
102
116
|
}
|
|
103
117
|
|
|
118
|
+
let sapling: SaplingData | undefined;
|
|
119
|
+
let orchard: OrchardData | undefined;
|
|
120
|
+
if (hasExtraData) {
|
|
121
|
+
if (isZcashv5) {
|
|
122
|
+
({ sapling, offset } = splitSaplingPart(transaction, offset));
|
|
123
|
+
|
|
124
|
+
({ orchard, offset } = splitOrchardPart(transaction, offset));
|
|
125
|
+
|
|
126
|
+
extraData = transaction.subarray(offset);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
104
130
|
if (witness) {
|
|
105
131
|
witnessScript = transaction.slice(offset, -4);
|
|
106
132
|
locktime = transaction.slice(transaction.length - 4);
|
|
@@ -116,7 +142,9 @@ export function splitTransaction(
|
|
|
116
142
|
}
|
|
117
143
|
|
|
118
144
|
if (hasExtraData) {
|
|
119
|
-
|
|
145
|
+
if (!isZcashv5) {
|
|
146
|
+
extraData = transaction.slice(offset);
|
|
147
|
+
}
|
|
120
148
|
}
|
|
121
149
|
|
|
122
150
|
//Get witnesses for Decred
|
|
@@ -154,7 +182,212 @@ export function splitTransaction(
|
|
|
154
182
|
nVersionGroupId,
|
|
155
183
|
nExpiryHeight,
|
|
156
184
|
extraData,
|
|
185
|
+
sapling,
|
|
186
|
+
orchard,
|
|
157
187
|
};
|
|
158
188
|
log("btc", `splitTransaction ${transactionHex}:\n${formatTransactionDebug(t)}`);
|
|
159
189
|
return t;
|
|
160
190
|
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Splits the Sapling part of a Zcash v5 transaction buffer according to https://zips.z.cash/zip-0225
|
|
194
|
+
*/
|
|
195
|
+
function splitSaplingPart(
|
|
196
|
+
transaction: Buffer,
|
|
197
|
+
offset: number,
|
|
198
|
+
): { sapling?: SaplingData; offset: number } {
|
|
199
|
+
let varint = getVarint(transaction, offset);
|
|
200
|
+
const nSpendsSapling = varint[0];
|
|
201
|
+
offset += varint[1];
|
|
202
|
+
|
|
203
|
+
const vSpendsSapling: SaplingSpendDescriptionV5[] = [];
|
|
204
|
+
for (let i = 0; i < nSpendsSapling; i++) {
|
|
205
|
+
const cv = transaction.slice(offset, offset + 32);
|
|
206
|
+
offset += 32;
|
|
207
|
+
const nullifier = transaction.slice(offset, offset + 32);
|
|
208
|
+
offset += 32;
|
|
209
|
+
const rk = transaction.slice(offset, offset + 32);
|
|
210
|
+
offset += 32;
|
|
211
|
+
|
|
212
|
+
vSpendsSapling.push({
|
|
213
|
+
cv,
|
|
214
|
+
nullifier,
|
|
215
|
+
rk,
|
|
216
|
+
} as SaplingSpendDescriptionV5);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
varint = getVarint(transaction, offset);
|
|
220
|
+
const nOutputsSapling = varint[0];
|
|
221
|
+
offset += varint[1];
|
|
222
|
+
const vOutputSapling: SaplingOutputDescriptionV5[] = [];
|
|
223
|
+
|
|
224
|
+
for (let i = 0; i < nOutputsSapling; i++) {
|
|
225
|
+
const cv = transaction.slice(offset, offset + 32);
|
|
226
|
+
|
|
227
|
+
offset += 32;
|
|
228
|
+
const cmu = transaction.slice(offset, offset + 32);
|
|
229
|
+
|
|
230
|
+
offset += 32;
|
|
231
|
+
const ephemeralKey = transaction.slice(offset, offset + 32);
|
|
232
|
+
offset += 32;
|
|
233
|
+
|
|
234
|
+
const encCiphertext = transaction.slice(offset, offset + zCashEncCiphertextSize);
|
|
235
|
+
offset += zCashEncCiphertextSize;
|
|
236
|
+
|
|
237
|
+
const outCiphertext = transaction.slice(offset, offset + zCashOutCiphertextSize);
|
|
238
|
+
offset += zCashOutCiphertextSize;
|
|
239
|
+
|
|
240
|
+
vOutputSapling.push({
|
|
241
|
+
cv,
|
|
242
|
+
cmu,
|
|
243
|
+
ephemeralKey,
|
|
244
|
+
encCiphertext,
|
|
245
|
+
outCiphertext,
|
|
246
|
+
} as SaplingOutputDescriptionV5);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let valueBalanceSapling = Buffer.alloc(0);
|
|
250
|
+
if (nSpendsSapling + nOutputsSapling > 0) {
|
|
251
|
+
valueBalanceSapling = transaction.slice(offset, offset + 8);
|
|
252
|
+
offset += 8;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let anchorSapling = Buffer.alloc(0);
|
|
256
|
+
if (nSpendsSapling > 0) {
|
|
257
|
+
anchorSapling = transaction.slice(offset, offset + 32);
|
|
258
|
+
offset += 32;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let vSpendProofsSapling = Buffer.alloc(0);
|
|
262
|
+
let vSpendAuthSigsSapling = Buffer.alloc(0);
|
|
263
|
+
if (nSpendsSapling > 0) {
|
|
264
|
+
vSpendProofsSapling = transaction.slice(
|
|
265
|
+
offset,
|
|
266
|
+
offset + zCashProofsSaplingSize * nSpendsSapling,
|
|
267
|
+
);
|
|
268
|
+
offset += zCashProofsSaplingSize * nSpendsSapling;
|
|
269
|
+
|
|
270
|
+
vSpendAuthSigsSapling = transaction.slice(offset, offset + 64 * nSpendsSapling);
|
|
271
|
+
offset += 64 * nSpendsSapling;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let vOutputProofsSapling = Buffer.alloc(0);
|
|
275
|
+
if (nOutputsSapling > 0) {
|
|
276
|
+
vOutputProofsSapling = transaction.slice(
|
|
277
|
+
offset,
|
|
278
|
+
offset + zCashProofsSaplingSize * nOutputsSapling,
|
|
279
|
+
);
|
|
280
|
+
offset += zCashProofsSaplingSize * nOutputsSapling;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let bindingSigSapling = Buffer.alloc(0);
|
|
284
|
+
if (nSpendsSapling + nOutputsSapling > 0) {
|
|
285
|
+
bindingSigSapling = transaction.slice(offset, offset + 64);
|
|
286
|
+
offset += 64;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let sapling: SaplingData | undefined;
|
|
290
|
+
if (nSpendsSapling + nOutputsSapling > 0) {
|
|
291
|
+
sapling = {
|
|
292
|
+
nSpendsSapling,
|
|
293
|
+
vSpendsSapling,
|
|
294
|
+
nOutputsSapling,
|
|
295
|
+
vOutputSapling,
|
|
296
|
+
valueBalanceSapling,
|
|
297
|
+
anchorSapling,
|
|
298
|
+
vSpendProofsSapling,
|
|
299
|
+
vSpendAuthSigsSapling,
|
|
300
|
+
vOutputProofsSapling,
|
|
301
|
+
bindingSigSapling,
|
|
302
|
+
} as SaplingData;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return { sapling, offset };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Splits the Orchard part of a Zcash v5 transaction buffer according to https://zips.z.cash/zip-0225
|
|
310
|
+
*/
|
|
311
|
+
function splitOrchardPart(
|
|
312
|
+
transaction: Buffer,
|
|
313
|
+
offset: number,
|
|
314
|
+
): { orchard?: OrchardData; offset: number } {
|
|
315
|
+
// orchard
|
|
316
|
+
let varint = getVarint(transaction, offset);
|
|
317
|
+
const nActionsOrchard = varint[0];
|
|
318
|
+
offset += varint[1];
|
|
319
|
+
|
|
320
|
+
let orchard: OrchardData | undefined;
|
|
321
|
+
if (nActionsOrchard > 0) {
|
|
322
|
+
const actionsOrchard: OrchardAction[] = [];
|
|
323
|
+
|
|
324
|
+
for (let i = 0; i < nActionsOrchard; i++) {
|
|
325
|
+
const cv = transaction.subarray(offset, offset + 32);
|
|
326
|
+
offset += 32;
|
|
327
|
+
const nullifier = transaction.subarray(offset, offset + 32);
|
|
328
|
+
offset += 32;
|
|
329
|
+
const rk = transaction.subarray(offset, offset + 32);
|
|
330
|
+
offset += 32;
|
|
331
|
+
const cmx = transaction.subarray(offset, offset + 32);
|
|
332
|
+
offset += 32;
|
|
333
|
+
const ephemeralKey = transaction.subarray(offset, offset + 32);
|
|
334
|
+
offset += 32;
|
|
335
|
+
const encCiphertext = transaction.subarray(offset, offset + zCashEncCiphertextSize);
|
|
336
|
+
offset += zCashEncCiphertextSize;
|
|
337
|
+
const outCiphertext = transaction.subarray(offset, offset + zCashOutCiphertextSize);
|
|
338
|
+
offset += zCashOutCiphertextSize;
|
|
339
|
+
|
|
340
|
+
const action: OrchardAction = {
|
|
341
|
+
cv,
|
|
342
|
+
nullifier,
|
|
343
|
+
rk,
|
|
344
|
+
cmx,
|
|
345
|
+
ephemeralKey,
|
|
346
|
+
encCiphertext,
|
|
347
|
+
outCiphertext,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
actionsOrchard.push(action);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// flag field
|
|
354
|
+
const flagsOrchard = transaction.subarray(offset, offset + 1);
|
|
355
|
+
offset += 1;
|
|
356
|
+
// value balance orchard
|
|
357
|
+
const valueBalanceOrchard = transaction.subarray(offset, offset + 8);
|
|
358
|
+
offset += 8;
|
|
359
|
+
|
|
360
|
+
const anchorOrchard = transaction.subarray(offset, offset + 32);
|
|
361
|
+
offset += 32;
|
|
362
|
+
|
|
363
|
+
// read the size of proof
|
|
364
|
+
varint = getVarint(transaction, offset);
|
|
365
|
+
const sizeProofsOrchard = transaction.subarray(offset, offset + varint[1]);
|
|
366
|
+
offset += varint[1];
|
|
367
|
+
|
|
368
|
+
// proof field
|
|
369
|
+
const proofsOrchard = transaction.subarray(offset, offset + varint[0]);
|
|
370
|
+
offset += varint[0];
|
|
371
|
+
|
|
372
|
+
// vSpendAuthSigsOrchard field
|
|
373
|
+
const vSpendAuthSigsOrchard = transaction.subarray(offset, offset + nActionsOrchard * 64);
|
|
374
|
+
offset += nActionsOrchard * 64;
|
|
375
|
+
|
|
376
|
+
// bindingSigOrchard
|
|
377
|
+
const bindingSigOrchard = transaction.subarray(offset, offset + 64);
|
|
378
|
+
offset += 64;
|
|
379
|
+
|
|
380
|
+
orchard = {
|
|
381
|
+
vActions: actionsOrchard,
|
|
382
|
+
flags: flagsOrchard,
|
|
383
|
+
valueBalance: valueBalanceOrchard,
|
|
384
|
+
anchor: anchorOrchard,
|
|
385
|
+
sizeProofs: sizeProofsOrchard,
|
|
386
|
+
proofs: proofsOrchard,
|
|
387
|
+
vSpendsAuthSigs: vSpendAuthSigsOrchard,
|
|
388
|
+
bindingSig: bindingSigOrchard,
|
|
389
|
+
} as OrchardData;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return { orchard, offset };
|
|
393
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface Transaction {
|
|
|
40
40
|
extraData?: Buffer;
|
|
41
41
|
// zCash specific
|
|
42
42
|
consensusBranchId?: Buffer;
|
|
43
|
+
sapling?: SaplingData;
|
|
44
|
+
orchard?: OrchardData;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export interface TrustedInput {
|
|
@@ -47,3 +49,52 @@ export interface TrustedInput {
|
|
|
47
49
|
value: Buffer;
|
|
48
50
|
sequence: Buffer;
|
|
49
51
|
}
|
|
52
|
+
|
|
53
|
+
// zCash specific
|
|
54
|
+
/**
|
|
55
|
+
*/
|
|
56
|
+
export interface SaplingData {
|
|
57
|
+
vSpendsSapling: SaplingSpendDescriptionV5[];
|
|
58
|
+
vOutputSapling: SaplingOutputDescriptionV5[];
|
|
59
|
+
valueBalanceSapling: Buffer;
|
|
60
|
+
anchorSapling: Buffer;
|
|
61
|
+
vSpendProofsSapling: Buffer;
|
|
62
|
+
vSpendAuthSigsSapling: Buffer;
|
|
63
|
+
vOutputProofsSapling: Buffer;
|
|
64
|
+
bindingSigSapling: Buffer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface SaplingSpendDescriptionV5 {
|
|
68
|
+
cv: Buffer;
|
|
69
|
+
nullifier: Buffer;
|
|
70
|
+
rk: Buffer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface SaplingOutputDescriptionV5 {
|
|
74
|
+
cv: Buffer;
|
|
75
|
+
cmu: Buffer;
|
|
76
|
+
ephemeralKey: Buffer;
|
|
77
|
+
encCiphertext: Buffer;
|
|
78
|
+
outCiphertext: Buffer;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface OrchardAction {
|
|
82
|
+
cv: Buffer;
|
|
83
|
+
nullifier: Buffer;
|
|
84
|
+
rk: Buffer;
|
|
85
|
+
cmx: Buffer;
|
|
86
|
+
ephemeralKey: Buffer;
|
|
87
|
+
encCiphertext: Buffer;
|
|
88
|
+
outCiphertext: Buffer;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface OrchardData {
|
|
92
|
+
vActions: OrchardAction[];
|
|
93
|
+
flags: Buffer;
|
|
94
|
+
valueBalance: Buffer;
|
|
95
|
+
anchor: Buffer;
|
|
96
|
+
sizeProofs: Buffer;
|
|
97
|
+
proofs: Buffer;
|
|
98
|
+
vSpendsAuthSigs: Buffer;
|
|
99
|
+
bindingSig: Buffer;
|
|
100
|
+
}
|