@ledgerhq/hw-app-canton 0.3.0 → 0.3.1-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/lib-es/Canton.js CHANGED
@@ -1,10 +1,16 @@
1
1
  import { UserRefusedAddress, UserRefusedOnDevice } from "@ledgerhq/errors";
2
2
  import BIPPath from "bip32-path";
3
- import { MockCantonDevice } from "./MockDevice";
4
3
  const CLA = 0xe0;
5
4
  const P1_NON_CONFIRM = 0x00;
6
5
  const P1_CONFIRM = 0x01;
7
- const P2 = 0x00;
6
+ // P2 indicating no information.
7
+ const P2_NONE = 0x00;
8
+ // P2 indicating first APDU in a large request.
9
+ const P2_FIRST = 0x01;
10
+ // P2 indicating that this is not the last APDU in a large request.
11
+ const P2_MORE = 0x02;
12
+ // P2 indicating that this is the last APDU of a message in a multi message request.
13
+ const P2_MSG_END = 0x04;
8
14
  const INS = {
9
15
  GET_VERSION: 0x03,
10
16
  GET_APP_NAME: 0x04,
@@ -15,15 +21,15 @@ const STATUS = {
15
21
  OK: 0x9000,
16
22
  USER_CANCEL: 0x6985,
17
23
  };
24
+ const ED25519_SIGNATURE_HEX_LENGTH = 128; // hex characters (64 bytes)
25
+ const CANTON_SIGNATURE_HEX_LENGTH = 132; // hex characters (66 bytes with framing)
18
26
  /**
19
27
  * Canton BOLOS API
20
28
  */
21
29
  export default class Canton {
22
30
  transport;
23
- transportMock;
24
31
  constructor(transport, scrambleKey = "canton_default_scramble_key") {
25
32
  this.transport = transport;
26
- this.transportMock = new MockCantonDevice();
27
33
  transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction", "getAppConfiguration"], scrambleKey);
28
34
  }
29
35
  /**
@@ -34,18 +40,18 @@ export default class Canton {
34
40
  * @return the address and public key
35
41
  */
36
42
  async getAddress(path, display = false) {
37
- const bipPath = BIPPath.fromString(path).toPathArray();
43
+ // TODO: replace with the actual path fix wrong path "44'/6767'/0'" being used
44
+ const bipPath = BIPPath.fromString("m/44'/6767'/0'/0'/0'").toPathArray();
38
45
  const serializedPath = this.serializePath(bipPath);
39
46
  const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
40
- const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
47
+ const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
41
48
  const responseData = this.handleTransportResponse(response, "address");
42
49
  const { pubKey } = this.extractPubkeyAndChainCode(responseData);
43
- // Handle 65-byte uncompressed SECP256R1 public key
44
50
  const publicKey = "0x" + pubKey;
45
51
  const addressHash = this.hashString(publicKey);
46
52
  const address = "canton_" + addressHash.substring(0, 36);
47
53
  return {
48
- publicKey,
54
+ publicKey: pubKey,
49
55
  address,
50
56
  };
51
57
  }
@@ -53,30 +59,50 @@ export default class Canton {
53
59
  * Sign a Canton transaction.
54
60
  *
55
61
  * @param path a path in BIP-32 format
56
- * @param rawTx the raw transaction to sign
62
+ * @param txHash the transaction hash to sign
57
63
  * @return the signature
58
64
  */
59
- async signTransaction(path, rawTx) {
60
- const bipPath = BIPPath.fromString(path).toPathArray();
65
+ async signTransaction(path, txHash) {
66
+ // 1. Send the derivation path
67
+ // TODO: replace with the actual path fix wrong path "44'/6767'/0'" being used
68
+ const bipPath = BIPPath.fromString("m/44'/6767'/0'/0'/0'").toPathArray();
61
69
  const serializedPath = this.serializePath(bipPath);
62
- const payload = Buffer.concat([serializedPath, Buffer.from(rawTx, "hex")]);
63
- const response = await this.transportMock.send(CLA, INS.SIGN, P1_CONFIRM, P2, payload);
70
+ const pathResponse = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM, P2_FIRST | P2_MORE, serializedPath);
71
+ this.handleTransportResponse(pathResponse, "transaction");
72
+ // 2. Send the transaction hash
73
+ const response = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM, P2_MSG_END, Buffer.from(txHash, "hex"));
64
74
  const responseData = this.handleTransportResponse(response, "transaction");
65
- const signature = "0x" + responseData.toString("hex");
66
- return signature;
75
+ const rawSignature = responseData.toString("hex");
76
+ return this.cleanSignatureFormat(rawSignature);
67
77
  }
68
78
  /**
69
79
  * Get the app configuration.
70
80
  * @return the app configuration including version
71
81
  */
72
82
  async getAppConfiguration() {
73
- const response = await this.transport.send(CLA, INS.GET_VERSION, P1_NON_CONFIRM, P2, Buffer.alloc(0));
83
+ const response = await this.transport.send(CLA, INS.GET_VERSION, P1_NON_CONFIRM, P2_NONE, Buffer.alloc(0));
74
84
  const responseData = this.handleTransportResponse(response, "version");
75
85
  const { major, minor, patch } = this.extractVersion(responseData);
76
86
  return {
77
87
  version: `${major}.${minor}.${patch}`,
78
88
  };
79
89
  }
90
+ /**
91
+ * Converts 65-byte Canton format to 64-byte Ed25519:
92
+ * [40][64_bytes_signature][00] (132 hex chars)
93
+ * @private
94
+ */
95
+ cleanSignatureFormat(signature) {
96
+ if (signature.length === ED25519_SIGNATURE_HEX_LENGTH) {
97
+ return signature;
98
+ }
99
+ if (signature.length === CANTON_SIGNATURE_HEX_LENGTH) {
100
+ const cleanedSignature = signature.slice(2, -2);
101
+ return cleanedSignature;
102
+ }
103
+ console.warn(`[Canton]: Unknown signature format (${signature.length} chars)`);
104
+ return signature;
105
+ }
80
106
  /**
81
107
  * Helper method to handle transport response and check for errors
82
108
  * @private
@@ -126,10 +152,20 @@ export default class Canton {
126
152
  * @private
127
153
  */
128
154
  extractPubkeyAndChainCode(data) {
129
- const pubkeySize = parseInt(data.subarray(0, 1).toString("hex"), 16);
130
- const pubKey = data.subarray(1, pubkeySize + 1);
131
- const chainCodeSize = parseInt(data.subarray(pubkeySize + 1, pubkeySize + 2).toString("hex"), 16);
132
- const chainCode = data.subarray(pubkeySize + 2, pubkeySize + chainCodeSize + 2);
155
+ // Parse the response according to the Python unpack_get_addr_response format:
156
+ // response = pubkey_len (1) + pubkey (var) + chaincode_len (1) + chaincode (var)
157
+ let offset = 0;
158
+ // Extract public key length (1 byte)
159
+ const pubkeySize = data.readUInt8(offset);
160
+ offset += 1;
161
+ // Extract public key
162
+ const pubKey = data.subarray(offset, offset + pubkeySize);
163
+ offset += pubkeySize;
164
+ // Extract chain code length (1 byte)
165
+ const chainCodeSize = data.readUInt8(offset);
166
+ offset += 1;
167
+ // Extract chain code
168
+ const chainCode = data.subarray(offset, offset + chainCodeSize);
133
169
  return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
134
170
  }
135
171
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,OAAO,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAa,MAAM,cAAc,CAAC;AAE3D,MAAM,GAAG,GAAG,IAAI,CAAC;AAEjB,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,EAAE,GAAG,IAAI,CAAC;AAEhB,MAAM,GAAG,GAAG;IACV,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,IAAI;IAClB,QAAQ,EAAE,IAAI;IACd,IAAI,EAAE,IAAI;CACX,CAAC;AAEF,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,MAAM;IACV,WAAW,EAAE,MAAM;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,CAAY;IACrB,aAAa,CAAmB;IAEhC,YAAY,SAAoB,EAAE,WAAW,GAAG,6BAA6B;QAC3E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,aAAa,GAAG,IAAI,gBAAgB,EAAE,CAAC;QAE5C,SAAS,CAAC,qBAAqB,CAC7B,IAAI,EACJ,CAAC,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,CAAC,EACxD,WAAW,CACZ,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,UAAmB,KAAK;QACrD,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACvD,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAEnD,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC;QACjD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC;QAEtF,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACvE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,yBAAyB,CAAC,YAAY,CAAC,CAAC;QAEhE,mDAAmD;QACnD,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC;QAEhC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEzD,OAAO;YACL,SAAS;YACT,OAAO;SACR,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,KAAa;QAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QACvD,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAE3E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAEvF,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAE3E,MAAM,SAAS,GAAG,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,mBAAmB;QACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACxC,GAAG,EACH,GAAG,CAAC,WAAW,EACf,cAAc,EACd,EAAE,EACF,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAChB,CAAC;QAEF,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACvE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QAElE,OAAO;YACL,OAAO,EAAE,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE;SACtC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,uBAAuB,CAC7B,QAAgB,EAChB,SAAgD;QAEhD,MAAM,UAAU,GAAG,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9D,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE5D,IAAI,UAAU,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC;YACtC,QAAQ,SAAS,EAAE,CAAC;gBAClB,KAAK,SAAS;oBACZ,MAAM,IAAI,kBAAkB,EAAE,CAAC;gBACjC,KAAK,aAAa;oBAChB,MAAM,IAAI,mBAAmB,EAAE,CAAC;gBAClC;oBACE,MAAM,IAAI,KAAK,EAAE,CAAC;YACtB,CAAC;QACH,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED;;;OAGG;IACK,aAAa,CAAC,IAAc;QAClC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,kCAAkC;QACnE,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;YAC9B,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,uCAAuC;QACrF,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACK,UAAU,CAAC,GAAW;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;YACjC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,4BAA4B;QAClD,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;IAED;;;OAGG;IACK,yBAAyB,CAAC,IAAY;QAC5C,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;QAEhD,MAAM,aAAa,GAAG,QAAQ,CAC5B,IAAI,CAAC,QAAQ,CAAC,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC7D,EAAE,CACH,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC;QAChF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAClF,CAAC;IAED;;;OAGG;IACK,cAAc,CAAC,IAAY;QACjC,OAAO;YACL,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACxD,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACxD,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;SACzD,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Canton.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canton.test.d.ts","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker";
2
+ import Canton from "./Canton";
3
+ describe("Canton", () => {
4
+ describe("decorateAppAPIMethods", () => {
5
+ it("should properly decorate transport methods", async () => {
6
+ // GIVEN
7
+ const transport = await openTransportReplayer(new RecordStore());
8
+ // WHEN
9
+ const canton = new Canton(transport);
10
+ // THEN
11
+ expect(canton.transport).toBeDefined();
12
+ expect(typeof canton.getAddress).toBe("function");
13
+ expect(typeof canton.signTransaction).toBe("function");
14
+ expect(typeof canton.getAppConfiguration).toBe("function");
15
+ });
16
+ });
17
+ describe("getAddress", () => {
18
+ it("should get address without display", async () => {
19
+ // GIVEN
20
+ const transport = await openTransportReplayer(RecordStore.fromString(`
21
+ => e005000015058000002c80001a6f800000008000000080000000
22
+ <= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
23
+ `));
24
+ const canton = new Canton(transport);
25
+ // WHEN
26
+ const result = await canton.getAddress("44'/6767'/0'/0'/0'");
27
+ // THEN
28
+ expect(result).toEqual({
29
+ address: "canton_1a7a97e0",
30
+ publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
31
+ });
32
+ });
33
+ it("should get address with display", async () => {
34
+ // GIVEN
35
+ const transport = await openTransportReplayer(RecordStore.fromString(`
36
+ => e005010015058000002c80001a6f800000008000000080000000
37
+ <= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
38
+ `));
39
+ const canton = new Canton(transport);
40
+ // WHEN
41
+ const result = await canton.getAddress("44'/6767'/0'/0'/0'", true);
42
+ // THEN
43
+ expect(result).toEqual({
44
+ address: "canton_1a7a97e0",
45
+ publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
46
+ });
47
+ });
48
+ it("should throw on invalid derivation path", async () => {
49
+ // GIVEN
50
+ const transport = await openTransportReplayer(new RecordStore());
51
+ const canton = new Canton(transport);
52
+ // WHEN & THEN
53
+ return expect(canton.getAddress("invalid path")).rejects.toThrow();
54
+ });
55
+ // unskip when TODO comment is handled
56
+ it.skip("should handle various derivation paths", async () => {
57
+ // GIVEN
58
+ const transport = await openTransportReplayer(RecordStore.fromString(`
59
+ => e005000015058000002c80001a6f800000008000000080000001
60
+ <= 205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d702205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d7029000
61
+ `));
62
+ const canton = new Canton(transport);
63
+ // WHEN
64
+ const result = await canton.getAddress("44'/6767'/0'/0'/1'");
65
+ // THEN
66
+ expect(result).toBeDefined();
67
+ expect(result.address).toBeDefined();
68
+ expect(result.publicKey).toBeDefined();
69
+ });
70
+ // should handle user refused address
71
+ });
72
+ describe("signTransaction", () => {
73
+ // should sign transaction
74
+ // should handle large transaction payloads
75
+ // should handle empty transaction
76
+ // should request blind signature when required
77
+ it("should sign transaction hash", async () => {
78
+ // GIVEN
79
+ const transport = await openTransportReplayer(RecordStore.fromString(`
80
+ => e006000315058000002c80001a6f800000008000000080000000
81
+ <= 9000
82
+ => e006000420d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd
83
+ <= 40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00009000
84
+ `));
85
+ const canton = new Canton(transport);
86
+ const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
87
+ // WHEN
88
+ const result = await canton.signTransaction("44'/6767'/0'/0'/0'", txHash);
89
+ // THEN
90
+ expect(result).toEqual("a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00");
91
+ });
92
+ it("should handle user refused transaction", async () => {
93
+ // GIVEN
94
+ const transport = await openTransportReplayer(RecordStore.fromString(`
95
+ => e006010015058000002c80001a6f800000008000000080000000
96
+ <= 6985
97
+ `));
98
+ const canton = new Canton(transport);
99
+ // WHEN & THEN
100
+ return expect(canton.signTransaction("44'/6767'/0'/0'/0'", "test")).rejects.toThrow();
101
+ });
102
+ });
103
+ describe("getAppConfiguration", () => {
104
+ it("should get app configuration", async () => {
105
+ // GIVEN
106
+ const transport = await openTransportReplayer(RecordStore.fromString(`
107
+ => e003000000
108
+ <= 0202029000
109
+ `));
110
+ const canton = new Canton(transport);
111
+ // WHEN
112
+ const result = await canton.getAppConfiguration();
113
+ // THEN
114
+ expect(result).toEqual({
115
+ version: "2.2.2",
116
+ });
117
+ });
118
+ // should handle configuration error
119
+ });
120
+ });
121
+ //# sourceMappingURL=Canton.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canton.test.js","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/hw-app-canton",
3
- "version": "0.3.0",
3
+ "version": "0.3.1-nightly.0",
4
4
  "description": "Ledger Hardware Wallet Canton Application API",
5
5
  "keywords": [
6
6
  "Ledger",
@@ -27,8 +27,7 @@
27
27
  "dependencies": {
28
28
  "bip32-path": "^0.4.2",
29
29
  "@ledgerhq/errors": "^6.25.0",
30
- "@ledgerhq/hw-transport": "^6.31.10",
31
- "@ledgerhq/coin-canton": "^0.4.0"
30
+ "@ledgerhq/hw-transport": "^6.31.10"
32
31
  },
33
32
  "devDependencies": {
34
33
  "@types/jest": "^29.5.10",
@@ -1,13 +1,15 @@
1
- import SpeculosTransportHttp from "@ledgerhq/hw-transport-node-speculos-http";
1
+ import SpeculosTransportHttp, { SpeculosButton } from "@ledgerhq/hw-transport-node-speculos-http";
2
2
  import Canton from "./Canton";
3
- import Transport from "@ledgerhq/hw-transport";
4
3
 
5
4
  describe("AppCanton", () => {
6
- let transport: Transport;
5
+ let transport: SpeculosTransportHttp;
7
6
 
8
7
  beforeAll(async () => {
9
8
  transport = await SpeculosTransportHttp.open({});
10
9
  });
10
+ afterAll(async () => {
11
+ transport.close();
12
+ });
11
13
 
12
14
  describe("getAppConfiguration", () => {
13
15
  it("returns app version", async () => {
@@ -35,14 +37,38 @@ describe("AppCanton", () => {
35
37
 
36
38
  // THEN
37
39
  expect(result).toEqual({
38
- address: "canton_10cd9ed0",
39
- publicKey:
40
- "0x043b462de34ec31fba274f2a381947aef26697912194312fc289c46cc1b2b4f6b00828dc1e4f96001b10463083edf85f2e0550862a3dc99ed411ca6d25f2bc19a8",
40
+ address: "canton_1a7a97e0",
41
+ publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
41
42
  });
42
43
  });
43
44
  });
44
45
 
45
46
  describe("signTransaction", () => {
46
- it.todo("returns sign transaction");
47
+ it("returns sign transaction", async () => {
48
+ // GIVEN
49
+ const app = new Canton(transport);
50
+ const derivationPath = "44'/6767'/0'/0'/0'";
51
+ const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
52
+
53
+ // WHEN
54
+ const signPromise = app.signTransaction(derivationPath, txHash);
55
+
56
+ // Waiting Speculos receive APDUs
57
+ const delay = (ms: number): Promise<void> => new Promise(f => setTimeout(f, ms));
58
+ await delay(500);
59
+ // Valid transaction butotn interaction sequence
60
+ await transport.button(SpeculosButton.BOTH);
61
+ await transport.button(SpeculosButton.RIGHT);
62
+ await transport.button(SpeculosButton.RIGHT);
63
+ await transport.button(SpeculosButton.RIGHT);
64
+ await transport.button(SpeculosButton.BOTH);
65
+
66
+ const result = await signPromise;
67
+
68
+ // THEN
69
+ expect(result).toEqual(
70
+ "40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a0000",
71
+ );
72
+ });
47
73
  });
48
74
  });
@@ -1,12 +1,16 @@
1
1
  import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker";
2
- import Canton from "../src/Canton";
2
+ import Canton from "./Canton";
3
3
 
4
4
  describe("Canton", () => {
5
5
  describe("decorateAppAPIMethods", () => {
6
6
  it("should properly decorate transport methods", async () => {
7
+ // GIVEN
7
8
  const transport = await openTransportReplayer(new RecordStore());
9
+
10
+ // WHEN
8
11
  const canton = new Canton(transport);
9
12
 
13
+ // THEN
10
14
  expect(canton.transport).toBeDefined();
11
15
  expect(typeof canton.getAddress).toBe("function");
12
16
  expect(typeof canton.signTransaction).toBe("function");
@@ -16,55 +20,69 @@ describe("Canton", () => {
16
20
 
17
21
  describe("getAddress", () => {
18
22
  it("should get address without display", async () => {
23
+ // GIVEN
19
24
  const transport = await openTransportReplayer(
20
25
  RecordStore.fromString(`
21
26
  => e005000015058000002c80001a6f800000008000000080000000
22
- <= 4d65a10662b9759d62bb59048366705454654cf4f9b4b3525cf314429e46c6919000
27
+ <= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
23
28
  `),
24
29
  );
25
-
26
30
  const canton = new Canton(transport);
31
+
32
+ // WHEN
27
33
  const result = await canton.getAddress("44'/6767'/0'/0'/0'");
28
34
 
29
- expect(result).toBeDefined();
30
- expect(result.address).toBeDefined();
31
- expect(result.publicKey).toBeDefined();
35
+ // THEN
36
+ expect(result).toEqual({
37
+ address: "canton_1a7a97e0",
38
+ publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
39
+ });
32
40
  });
33
41
 
34
42
  it("should get address with display", async () => {
43
+ // GIVEN
35
44
  const transport = await openTransportReplayer(
36
45
  RecordStore.fromString(`
37
46
  => e005010015058000002c80001a6f800000008000000080000000
38
- <= 4d65a10662b9759d62bb59048366705454654cf4f9b4b3525cf314429e46c6919000
47
+ <= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
39
48
  `),
40
49
  );
41
-
42
50
  const canton = new Canton(transport);
51
+
52
+ // WHEN
43
53
  const result = await canton.getAddress("44'/6767'/0'/0'/0'", true);
44
54
 
45
- expect(result).toBeDefined();
46
- expect(result.address).toBeDefined();
47
- expect(result.publicKey).toBeDefined();
55
+ // THEN
56
+ expect(result).toEqual({
57
+ address: "canton_1a7a97e0",
58
+ publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
59
+ });
48
60
  });
49
61
 
50
62
  it("should throw on invalid derivation path", async () => {
63
+ // GIVEN
51
64
  const transport = await openTransportReplayer(new RecordStore());
52
65
  const canton = new Canton(transport);
53
66
 
67
+ // WHEN & THEN
54
68
  return expect(canton.getAddress("invalid path")).rejects.toThrow();
55
69
  });
56
70
 
57
- it("should handle various derivation paths", async () => {
71
+ // unskip when TODO comment is handled
72
+ it.skip("should handle various derivation paths", async () => {
73
+ // GIVEN
58
74
  const transport = await openTransportReplayer(
59
75
  RecordStore.fromString(`
60
76
  => e005000015058000002c80001a6f800000008000000080000001
61
- <= 5e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d7029000
77
+ <= 205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d702205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d7029000
62
78
  `),
63
79
  );
64
-
65
80
  const canton = new Canton(transport);
81
+
82
+ // WHEN
66
83
  const result = await canton.getAddress("44'/6767'/0'/0'/1'");
67
84
 
85
+ // THEN
68
86
  expect(result).toBeDefined();
69
87
  expect(result.address).toBeDefined();
70
88
  expect(result.publicKey).toBeDefined();
@@ -82,35 +100,61 @@ describe("Canton", () => {
82
100
 
83
101
  // should request blind signature when required
84
102
 
103
+ it("should sign transaction hash", async () => {
104
+ // GIVEN
105
+ const transport = await openTransportReplayer(
106
+ RecordStore.fromString(`
107
+ => e006000315058000002c80001a6f800000008000000080000000
108
+ <= 9000
109
+ => e006000420d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd
110
+ <= 40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00009000
111
+ `),
112
+ );
113
+ const canton = new Canton(transport);
114
+ const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
115
+
116
+ // WHEN
117
+ const result = await canton.signTransaction("44'/6767'/0'/0'/0'", txHash);
118
+
119
+ // THEN
120
+ expect(result).toEqual(
121
+ "a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00",
122
+ );
123
+ });
124
+
85
125
  it("should handle user refused transaction", async () => {
126
+ // GIVEN
86
127
  const transport = await openTransportReplayer(
87
128
  RecordStore.fromString(`
88
129
  => e006010015058000002c80001a6f800000008000000080000000
89
130
  <= 6985
90
131
  `),
91
132
  );
92
-
93
133
  const canton = new Canton(transport);
94
134
 
135
+ // WHEN & THEN
95
136
  return expect(canton.signTransaction("44'/6767'/0'/0'/0'", "test")).rejects.toThrow();
96
137
  });
97
138
  });
98
139
 
99
140
  describe("getAppConfiguration", () => {
100
141
  it("should get app configuration", async () => {
142
+ // GIVEN
101
143
  const transport = await openTransportReplayer(
102
144
  RecordStore.fromString(`
103
145
  => e003000000
104
146
  <= 0202029000
105
147
  `),
106
148
  );
107
-
108
149
  const canton = new Canton(transport);
150
+
151
+ // WHEN
109
152
  const result = await canton.getAppConfiguration();
110
153
 
111
- expect(result).toBeDefined();
112
- expect(result).toHaveProperty("version");
113
- expect(typeof result.version).toBe("string");
154
+ // THEN
155
+ expect(result).toEqual({
156
+ version: "2.2.2",
157
+ });
114
158
  });
115
159
 
116
160
  // should handle configuration error