@ledgerhq/hw-app-canton 0.6.0 → 0.7.0-nightly.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/CHANGELOG.md +6 -0
- package/README.md +3 -2
- package/lib/Canton.d.ts +51 -5
- package/lib/Canton.d.ts.map +1 -1
- package/lib/Canton.js +217 -38
- package/lib/Canton.js.map +1 -1
- package/lib-es/Canton.d.ts +51 -5
- package/lib-es/Canton.d.ts.map +1 -1
- package/lib-es/Canton.js +218 -39
- package/lib-es/Canton.js.map +1 -1
- package/package.json +5 -5
- package/src/Canton.test.ts +118 -39
- package/src/Canton.ts +306 -47
- package/lib/Canton.integ.test.d.ts +0 -2
- package/lib/Canton.integ.test.d.ts.map +0 -1
- package/lib/Canton.integ.test.js +0 -88
- package/lib/Canton.integ.test.js.map +0 -1
- package/lib/Canton.test.d.ts +0 -2
- package/lib/Canton.test.d.ts.map +0 -1
- package/lib/Canton.test.js +0 -127
- package/lib/Canton.test.js.map +0 -1
- package/lib-es/Canton.integ.test.d.ts +0 -2
- package/lib-es/Canton.integ.test.d.ts.map +0 -1
- package/lib-es/Canton.integ.test.js +0 -60
- package/lib-es/Canton.integ.test.js.map +0 -1
- package/lib-es/Canton.test.d.ts +0 -2
- package/lib-es/Canton.test.d.ts.map +0 -1
- package/lib-es/Canton.test.js +0 -122
- package/lib-es/Canton.test.js.map +0 -1
package/src/Canton.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type Transport from "@ledgerhq/hw-transport";
|
|
2
|
-
import {
|
|
2
|
+
import { TransportStatusError } from "@ledgerhq/errors";
|
|
3
3
|
import BIPPath from "bip32-path";
|
|
4
4
|
|
|
5
5
|
const CLA = 0xe0;
|
|
6
6
|
|
|
7
7
|
const P1_NON_CONFIRM = 0x00;
|
|
8
8
|
const P1_CONFIRM = 0x01;
|
|
9
|
+
const P1_SIGN_UNTYPED_VERSIONED_MESSAGE = 0x01;
|
|
10
|
+
const P1_SIGN_PREPARED_TRANSACTION = 0x02;
|
|
9
11
|
|
|
10
|
-
// P2 indicating no information.
|
|
11
12
|
const P2_NONE = 0x00;
|
|
12
|
-
// P2 indicating first APDU in a large request.
|
|
13
13
|
const P2_FIRST = 0x01;
|
|
14
|
-
// P2 indicating that this is not the last APDU in a large request.
|
|
15
14
|
const P2_MORE = 0x02;
|
|
16
|
-
// P2 indicating that this is the last APDU of a message in a multi message request.
|
|
17
15
|
const P2_MSG_END = 0x04;
|
|
18
16
|
|
|
19
17
|
const INS = {
|
|
@@ -30,6 +28,7 @@ const STATUS = {
|
|
|
30
28
|
|
|
31
29
|
const ED25519_SIGNATURE_HEX_LENGTH = 128; // hex characters (64 bytes)
|
|
32
30
|
const CANTON_SIGNATURE_HEX_LENGTH = 132; // hex characters (66 bytes with framing)
|
|
31
|
+
const MAX_APDU_DATA_LENGTH = 255;
|
|
33
32
|
|
|
34
33
|
export type AppConfig = {
|
|
35
34
|
version: string;
|
|
@@ -38,11 +37,22 @@ export type AppConfig = {
|
|
|
38
37
|
export type CantonAddress = {
|
|
39
38
|
publicKey: string;
|
|
40
39
|
address: string;
|
|
41
|
-
path: string;
|
|
40
|
+
path: string;
|
|
42
41
|
};
|
|
43
42
|
|
|
44
43
|
export type CantonSignature = string;
|
|
45
44
|
|
|
45
|
+
export type CantonPreparedTransaction = {
|
|
46
|
+
damlTransaction: Uint8Array;
|
|
47
|
+
nodes: Uint8Array[];
|
|
48
|
+
metadata: Uint8Array;
|
|
49
|
+
inputContracts: Uint8Array[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type CantonUntypedVersionedMessage = {
|
|
53
|
+
transactions: string[];
|
|
54
|
+
};
|
|
55
|
+
|
|
46
56
|
/**
|
|
47
57
|
* Canton BOLOS API
|
|
48
58
|
*/
|
|
@@ -71,9 +81,10 @@ export default class Canton {
|
|
|
71
81
|
const serializedPath = this.serializePath(bipPath);
|
|
72
82
|
|
|
73
83
|
const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
|
|
74
|
-
const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
|
|
75
84
|
|
|
76
|
-
const
|
|
85
|
+
const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
|
|
86
|
+
this.checkTransportResponse(response);
|
|
87
|
+
const responseData = this.extractResponseData(response);
|
|
77
88
|
const { publicKey } = this.extractPublicKeyAndChainCode(responseData);
|
|
78
89
|
|
|
79
90
|
const address = "canton_" + this.publicKeyToAddress(publicKey);
|
|
@@ -86,13 +97,34 @@ export default class Canton {
|
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
/**
|
|
89
|
-
* Sign a Canton transaction
|
|
100
|
+
* Sign a Canton transaction
|
|
101
|
+
* using the appropriate signing method based on transaction type.
|
|
90
102
|
*
|
|
91
103
|
* @param path a path in BIP-32 format
|
|
92
|
-
* @param
|
|
104
|
+
* @param data either prepared transaction components, untyped versioned message, or txHash string (backwards compatibility)
|
|
93
105
|
* @return the signature
|
|
94
106
|
*/
|
|
95
|
-
async signTransaction(
|
|
107
|
+
async signTransaction(
|
|
108
|
+
path: string,
|
|
109
|
+
data: CantonPreparedTransaction | CantonUntypedVersionedMessage | string,
|
|
110
|
+
): Promise<CantonSignature> {
|
|
111
|
+
// Backwards compatibility: handle txHash string format
|
|
112
|
+
if (typeof data === "string") {
|
|
113
|
+
return this.signTxHash(path, data);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if ("damlTransaction" in data) {
|
|
117
|
+
return this.signPreparedTransaction(path, data);
|
|
118
|
+
} else {
|
|
119
|
+
return this.signUntypedVersionedMessage(path, data);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Sign a transaction hash (backwards compatibility)
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
private async signTxHash(path: string, txHash: string): Promise<CantonSignature> {
|
|
96
128
|
// 1. Send the derivation path
|
|
97
129
|
const bipPath = BIPPath.fromString(path).toPathArray();
|
|
98
130
|
const serializedPath = this.serializePath(bipPath);
|
|
@@ -100,26 +132,181 @@ export default class Canton {
|
|
|
100
132
|
const pathResponse = await this.transport.send(
|
|
101
133
|
CLA,
|
|
102
134
|
INS.SIGN,
|
|
103
|
-
|
|
135
|
+
P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
|
|
104
136
|
P2_FIRST | P2_MORE,
|
|
105
137
|
serializedPath,
|
|
106
138
|
);
|
|
107
139
|
|
|
108
|
-
this.
|
|
140
|
+
this.checkTransportResponse(pathResponse);
|
|
141
|
+
|
|
142
|
+
// 2. Send the transaction hash as a single transaction
|
|
143
|
+
const transactionBuffer = Buffer.from(txHash, "hex");
|
|
109
144
|
|
|
110
|
-
// 2. Send the transaction hash
|
|
111
145
|
const response = await this.transport.send(
|
|
112
146
|
CLA,
|
|
113
147
|
INS.SIGN,
|
|
114
|
-
|
|
148
|
+
P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
|
|
115
149
|
P2_MSG_END,
|
|
116
|
-
|
|
150
|
+
transactionBuffer,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
this.checkTransportResponse(response);
|
|
154
|
+
const responseData = this.extractResponseData(response);
|
|
155
|
+
return this.cleanSignatureFormat(responseData);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sign a prepared Canton transaction
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
162
|
+
private async signPreparedTransaction(
|
|
163
|
+
path: string,
|
|
164
|
+
components: CantonPreparedTransaction,
|
|
165
|
+
): Promise<CantonSignature> {
|
|
166
|
+
let responseData: Buffer | null = null;
|
|
167
|
+
|
|
168
|
+
// 1. Send the derivation path
|
|
169
|
+
const bipPath = BIPPath.fromString(path).toPathArray();
|
|
170
|
+
const serializedPath = this.serializePath(bipPath);
|
|
171
|
+
|
|
172
|
+
const pathResponse = await this.transport.send(
|
|
173
|
+
CLA,
|
|
174
|
+
INS.SIGN,
|
|
175
|
+
P1_SIGN_PREPARED_TRANSACTION,
|
|
176
|
+
P2_FIRST | P2_MORE,
|
|
177
|
+
serializedPath,
|
|
117
178
|
);
|
|
118
179
|
|
|
119
|
-
|
|
120
|
-
|
|
180
|
+
this.checkTransportResponse(pathResponse);
|
|
181
|
+
|
|
182
|
+
// 2. Send the DAML transaction
|
|
183
|
+
await this.sendChunkedData({
|
|
184
|
+
ins: INS.SIGN,
|
|
185
|
+
p1: P1_SIGN_PREPARED_TRANSACTION,
|
|
186
|
+
payload: Buffer.from(components.damlTransaction),
|
|
187
|
+
isFinal: false,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// 3. Send each node
|
|
191
|
+
for (const [i, node] of components.nodes.entries()) {
|
|
192
|
+
this.validateUint8Array(node, `Node at index ${i}`);
|
|
193
|
+
await this.sendChunkedData({
|
|
194
|
+
ins: INS.SIGN,
|
|
195
|
+
p1: P1_SIGN_PREPARED_TRANSACTION,
|
|
196
|
+
payload: Buffer.from(node),
|
|
197
|
+
isFinal: false,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 4. Send the metadata
|
|
202
|
+
const isFinal = components.inputContracts.length === 0;
|
|
203
|
+
const result = await this.sendChunkedData({
|
|
204
|
+
ins: INS.SIGN,
|
|
205
|
+
p1: P1_SIGN_PREPARED_TRANSACTION,
|
|
206
|
+
payload: Buffer.from(components.metadata),
|
|
207
|
+
isFinal,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (isFinal) {
|
|
211
|
+
responseData = result;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 5. Send each input contract - last one should return data
|
|
215
|
+
for (const [i, inputContract] of components.inputContracts.entries()) {
|
|
216
|
+
this.validateUint8Array(inputContract, `Input contract at index ${i}`);
|
|
217
|
+
|
|
218
|
+
const isFinal = i === components.inputContracts.length - 1;
|
|
219
|
+
const result = await this.sendChunkedData({
|
|
220
|
+
ins: INS.SIGN,
|
|
221
|
+
p1: P1_SIGN_PREPARED_TRANSACTION,
|
|
222
|
+
payload: Buffer.from(inputContract),
|
|
223
|
+
isFinal,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (isFinal) {
|
|
227
|
+
responseData = result;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!responseData) {
|
|
232
|
+
throw new Error("No response data received from device");
|
|
233
|
+
}
|
|
121
234
|
|
|
122
|
-
return this.cleanSignatureFormat(
|
|
235
|
+
return this.cleanSignatureFormat(responseData);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Sign topology transactions for Canton onboarding
|
|
240
|
+
* @private
|
|
241
|
+
*/
|
|
242
|
+
private async signUntypedVersionedMessage(
|
|
243
|
+
path: string,
|
|
244
|
+
data: CantonUntypedVersionedMessage,
|
|
245
|
+
): Promise<CantonSignature> {
|
|
246
|
+
const { transactions } = data;
|
|
247
|
+
|
|
248
|
+
if (!transactions || transactions.length === 0) {
|
|
249
|
+
throw new TypeError("At least one transaction is required");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 1. Send the derivation path
|
|
253
|
+
const bipPath = BIPPath.fromString(path).toPathArray();
|
|
254
|
+
const serializedPath = this.serializePath(bipPath);
|
|
255
|
+
|
|
256
|
+
const pathResponse = await this.transport.send(
|
|
257
|
+
CLA,
|
|
258
|
+
INS.SIGN,
|
|
259
|
+
P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
|
|
260
|
+
P2_FIRST | P2_MORE,
|
|
261
|
+
serializedPath,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
this.checkTransportResponse(pathResponse);
|
|
265
|
+
|
|
266
|
+
// 2. Send each transaction using chunking for large data
|
|
267
|
+
for (const [i, transaction] of transactions.entries()) {
|
|
268
|
+
if (!transaction) {
|
|
269
|
+
throw new TypeError(`Transaction at index ${i} is undefined or null`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const transactionBuffer = Buffer.from(transaction, "hex");
|
|
273
|
+
const isLastTransaction = i === transactions.length - 1;
|
|
274
|
+
|
|
275
|
+
if (transactionBuffer.length <= MAX_APDU_DATA_LENGTH) {
|
|
276
|
+
// Small transaction - send directly
|
|
277
|
+
const p2 = isLastTransaction ? P2_MSG_END : P2_MORE | P2_MSG_END;
|
|
278
|
+
|
|
279
|
+
const response = await this.transport.send(
|
|
280
|
+
CLA,
|
|
281
|
+
INS.SIGN,
|
|
282
|
+
P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
|
|
283
|
+
p2,
|
|
284
|
+
transactionBuffer,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
if (isLastTransaction) {
|
|
288
|
+
this.checkTransportResponse(response);
|
|
289
|
+
const responseData = this.extractResponseData(response);
|
|
290
|
+
return this.cleanSignatureFormat(responseData);
|
|
291
|
+
} else {
|
|
292
|
+
this.checkTransportResponse(response);
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
// Large transaction - use chunking
|
|
296
|
+
const responseData = await this.sendChunkedData({
|
|
297
|
+
ins: INS.SIGN,
|
|
298
|
+
p1: P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
|
|
299
|
+
payload: transactionBuffer,
|
|
300
|
+
isFinal: isLastTransaction,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (isLastTransaction && responseData) {
|
|
304
|
+
return this.cleanSignatureFormat(responseData);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
throw new TypeError("No transactions provided");
|
|
123
310
|
}
|
|
124
311
|
|
|
125
312
|
/**
|
|
@@ -135,7 +322,8 @@ export default class Canton {
|
|
|
135
322
|
Buffer.alloc(0),
|
|
136
323
|
);
|
|
137
324
|
|
|
138
|
-
|
|
325
|
+
this.checkTransportResponse(response);
|
|
326
|
+
const responseData = this.extractResponseData(response);
|
|
139
327
|
const { major, minor, patch } = this.extractVersion(responseData);
|
|
140
328
|
|
|
141
329
|
return {
|
|
@@ -143,48 +331,119 @@ export default class Canton {
|
|
|
143
331
|
};
|
|
144
332
|
}
|
|
145
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Validate Uint8Array with descriptive error message
|
|
336
|
+
* @private
|
|
337
|
+
*/
|
|
338
|
+
private validateUint8Array(value: any, context: string): void {
|
|
339
|
+
if (!value) {
|
|
340
|
+
throw new TypeError(`${context} is undefined or null`);
|
|
341
|
+
}
|
|
342
|
+
if (!(value instanceof Uint8Array)) {
|
|
343
|
+
throw new TypeError(`${context} is not a Uint8Array: ${typeof value}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Unified chunking strategy for sending data to device
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
private async sendChunkedData({
|
|
352
|
+
ins,
|
|
353
|
+
p1,
|
|
354
|
+
payload,
|
|
355
|
+
isFinal = false,
|
|
356
|
+
}: {
|
|
357
|
+
ins: number;
|
|
358
|
+
p1: number;
|
|
359
|
+
payload: Buffer;
|
|
360
|
+
isFinal?: boolean;
|
|
361
|
+
}): Promise<Buffer | null> {
|
|
362
|
+
const chunks = this.createChunks(payload);
|
|
363
|
+
let responseData: Buffer | null = null;
|
|
364
|
+
|
|
365
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
366
|
+
const isLastChunk = i === chunks.length - 1;
|
|
367
|
+
let p2 = P2_MORE;
|
|
368
|
+
|
|
369
|
+
if (isLastChunk) {
|
|
370
|
+
p2 = isFinal ? P2_MSG_END : P2_MORE | P2_MSG_END;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const response = await this.transport.send(CLA, ins, p1, p2, chunks[i]);
|
|
374
|
+
|
|
375
|
+
if (isFinal && isLastChunk) {
|
|
376
|
+
this.checkTransportResponse(response);
|
|
377
|
+
responseData = this.extractResponseData(response);
|
|
378
|
+
} else {
|
|
379
|
+
this.checkTransportResponse(response);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return responseData;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Create optimized chunks from payload
|
|
388
|
+
* @private
|
|
389
|
+
*/
|
|
390
|
+
private createChunks(payload: Buffer): Buffer[] {
|
|
391
|
+
if (payload.length <= MAX_APDU_DATA_LENGTH) {
|
|
392
|
+
return [payload];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const totalChunks = Math.ceil(payload.length / MAX_APDU_DATA_LENGTH);
|
|
396
|
+
const chunks: Buffer[] = new Array(totalChunks);
|
|
397
|
+
let offset = 0;
|
|
398
|
+
|
|
399
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
400
|
+
const chunkSize = Math.min(MAX_APDU_DATA_LENGTH, payload.length - offset);
|
|
401
|
+
chunks[i] = payload.slice(offset, offset + chunkSize);
|
|
402
|
+
offset += chunkSize;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return chunks;
|
|
406
|
+
}
|
|
407
|
+
|
|
146
408
|
/**
|
|
147
409
|
* Converts 65-byte Canton format to 64-byte Ed25519:
|
|
148
410
|
* [40][64_bytes_signature][00] (132 hex chars)
|
|
149
411
|
* @private
|
|
150
412
|
*/
|
|
151
|
-
private cleanSignatureFormat(signature:
|
|
152
|
-
|
|
153
|
-
|
|
413
|
+
private cleanSignatureFormat(signature: Buffer): CantonSignature {
|
|
414
|
+
const signatureHex = signature.toString("hex");
|
|
415
|
+
|
|
416
|
+
if (signatureHex.length === ED25519_SIGNATURE_HEX_LENGTH) {
|
|
417
|
+
return signatureHex;
|
|
154
418
|
}
|
|
155
419
|
|
|
156
|
-
if (
|
|
157
|
-
const cleanedSignature =
|
|
420
|
+
if (signatureHex.length === CANTON_SIGNATURE_HEX_LENGTH) {
|
|
421
|
+
const cleanedSignature = signatureHex.slice(2, -2);
|
|
158
422
|
return cleanedSignature;
|
|
159
423
|
}
|
|
160
424
|
|
|
161
|
-
|
|
162
|
-
return signature;
|
|
425
|
+
return signatureHex;
|
|
163
426
|
}
|
|
164
427
|
|
|
165
428
|
/**
|
|
166
|
-
*
|
|
429
|
+
* Check transport response for errors and throw appropriate exceptions
|
|
167
430
|
* @private
|
|
168
431
|
*/
|
|
169
|
-
private
|
|
170
|
-
response: Buffer,
|
|
171
|
-
errorType: "address" | "transaction" | "version",
|
|
172
|
-
): Buffer {
|
|
432
|
+
private checkTransportResponse(response: Buffer): void {
|
|
173
433
|
const statusCode = response.readUInt16BE(response.length - 2);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
switch (errorType) {
|
|
178
|
-
case "address":
|
|
179
|
-
throw new UserRefusedAddress();
|
|
180
|
-
case "transaction":
|
|
181
|
-
throw new UserRefusedOnDevice();
|
|
182
|
-
default:
|
|
183
|
-
throw new Error();
|
|
184
|
-
}
|
|
434
|
+
|
|
435
|
+
if (statusCode !== STATUS.OK) {
|
|
436
|
+
throw new TransportStatusError(statusCode);
|
|
185
437
|
}
|
|
438
|
+
}
|
|
186
439
|
|
|
187
|
-
|
|
440
|
+
/**
|
|
441
|
+
* Extract response data from transport response
|
|
442
|
+
* APDU responses have format: [data][status_code(2_bytes)]
|
|
443
|
+
* @private
|
|
444
|
+
*/
|
|
445
|
+
private extractResponseData(response: Buffer): Buffer {
|
|
446
|
+
return response.slice(0, -2);
|
|
188
447
|
}
|
|
189
448
|
|
|
190
449
|
/**
|
|
@@ -251,9 +510,9 @@ export default class Canton {
|
|
|
251
510
|
*/
|
|
252
511
|
private extractVersion(data: Buffer): { major: number; minor: number; patch: number } {
|
|
253
512
|
return {
|
|
254
|
-
major: parseInt(data.subarray(0, 1).toString("hex"), 16),
|
|
255
|
-
minor: parseInt(data.subarray(1, 2).toString("hex"), 16),
|
|
256
|
-
patch: parseInt(data.subarray(2, 3).toString("hex"), 16),
|
|
513
|
+
major: Number.parseInt(data.subarray(0, 1).toString("hex"), 16),
|
|
514
|
+
minor: Number.parseInt(data.subarray(1, 2).toString("hex"), 16),
|
|
515
|
+
patch: Number.parseInt(data.subarray(2, 3).toString("hex"), 16),
|
|
257
516
|
};
|
|
258
517
|
}
|
|
259
518
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Canton.integ.test.d.ts","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":""}
|
package/lib/Canton.integ.test.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const hw_transport_node_speculos_http_1 = __importStar(require("@ledgerhq/hw-transport-node-speculos-http"));
|
|
30
|
-
const Canton_1 = __importDefault(require("./Canton"));
|
|
31
|
-
describe("AppCanton", () => {
|
|
32
|
-
let transport;
|
|
33
|
-
beforeAll(async () => {
|
|
34
|
-
transport = await hw_transport_node_speculos_http_1.default.open({});
|
|
35
|
-
});
|
|
36
|
-
afterAll(async () => {
|
|
37
|
-
transport.close();
|
|
38
|
-
});
|
|
39
|
-
describe("getAppConfiguration", () => {
|
|
40
|
-
it("returns app version", async () => {
|
|
41
|
-
// GIVEN
|
|
42
|
-
const app = new Canton_1.default(transport);
|
|
43
|
-
// WHEN
|
|
44
|
-
const result = await app.getAppConfiguration();
|
|
45
|
-
// THEN
|
|
46
|
-
expect(result).toEqual({
|
|
47
|
-
version: "2.2.2",
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
describe("getAddress", () => {
|
|
52
|
-
it("retrieves address from app", async () => {
|
|
53
|
-
// GIVEN
|
|
54
|
-
const app = new Canton_1.default(transport);
|
|
55
|
-
const derivationPath = "44'/6767'/0'/0'/0'";
|
|
56
|
-
// WHEN
|
|
57
|
-
const result = await app.getAddress(derivationPath);
|
|
58
|
-
// THEN
|
|
59
|
-
expect(result).toEqual({
|
|
60
|
-
address: "canton_1a7a97e0",
|
|
61
|
-
publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe("signTransaction", () => {
|
|
66
|
-
it("returns sign transaction", async () => {
|
|
67
|
-
// GIVEN
|
|
68
|
-
const app = new Canton_1.default(transport);
|
|
69
|
-
const derivationPath = "44'/6767'/0'/0'/0'";
|
|
70
|
-
const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
|
|
71
|
-
// WHEN
|
|
72
|
-
const signPromise = app.signTransaction(derivationPath, txHash);
|
|
73
|
-
// Waiting Speculos receive APDUs
|
|
74
|
-
const delay = (ms) => new Promise(f => setTimeout(f, ms));
|
|
75
|
-
await delay(500);
|
|
76
|
-
// Valid transaction butotn interaction sequence
|
|
77
|
-
await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.BOTH);
|
|
78
|
-
await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.RIGHT);
|
|
79
|
-
await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.RIGHT);
|
|
80
|
-
await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.RIGHT);
|
|
81
|
-
await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.BOTH);
|
|
82
|
-
const result = await signPromise;
|
|
83
|
-
// THEN
|
|
84
|
-
expect(result).toEqual("40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a0000");
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
//# sourceMappingURL=Canton.integ.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Canton.integ.test.js","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6GAAkG;AAClG,sDAA8B;AAE9B,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,SAAgC,CAAC;IAErC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,SAAS,GAAG,MAAM,yCAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,SAAS,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;YACnC,QAAQ;YACR,MAAM,GAAG,GAAG,IAAI,gBAAM,CAAC,SAAS,CAAC,CAAC;YAElC,OAAO;YACP,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,mBAAmB,EAAE,CAAC;YAE/C,OAAO;YACP,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,OAAO,EAAE,OAAO;aACjB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,QAAQ;YACR,MAAM,GAAG,GAAG,IAAI,gBAAM,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,cAAc,GAAG,oBAAoB,CAAC;YAE5C,OAAO;YACP,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAEpD,OAAO;YACP,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,kEAAkE;aAC9E,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,QAAQ;YACR,MAAM,GAAG,GAAG,IAAI,gBAAM,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,cAAc,GAAG,oBAAoB,CAAC;YAC5C,MAAM,MAAM,GAAG,kEAAkE,CAAC;YAElF,OAAO;YACP,MAAM,WAAW,GAAG,GAAG,CAAC,eAAe,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YAEhE,iCAAiC;YACjC,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACjF,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YACjB,gDAAgD;YAChD,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,IAAI,CAAC,CAAC;YAE5C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;YAEjC,OAAO;YACP,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CACpB,sIAAsI,CACvI,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/lib/Canton.test.d.ts
DELETED
package/lib/Canton.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Canton.test.d.ts","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":""}
|