@ledgerhq/hw-app-canton 0.2.2-nightly.2 → 0.3.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @ledgerhq/hw-app-canton@0.2.2-nightly.1 build /home/runner/work/ledger-live/ledger-live/libs/ledgerjs/packages/hw-app-canton
2
+ > @ledgerhq/hw-app-canton@0.2.2 build /home/runner/work/ledger-live/ledger-live/libs/ledgerjs/packages/hw-app-canton
3
3
  > tsc && tsc -m esnext --moduleResolution bundler --outDir lib-es
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,27 +1,24 @@
1
1
  # @ledgerhq/hw-app-canton
2
2
 
3
- ## 0.2.2-nightly.2
3
+ ## 0.3.0-nightly.0
4
4
 
5
- ### Patch Changes
5
+ ### Minor Changes
6
6
 
7
- - Updated dependencies []:
8
- - @ledgerhq/coin-canton@0.2.2-nightly.2
7
+ - [#11481](https://github.com/LedgerHQ/ledger-live/pull/11481) [`d571ef1`](https://github.com/LedgerHQ/ledger-live/commit/d571ef1104092e41de4dfb6dd9b26d27348b82cf) Thanks [@sprohaszka-ledger](https://github.com/sprohaszka-ledger)! - Add integration tests and communicate with a real transport implementation
9
8
 
10
- ## 0.2.2-nightly.1
9
+ ## 0.2.2
11
10
 
12
11
  ### Patch Changes
13
12
 
14
- - Updated dependencies [[`212f772`](https://github.com/LedgerHQ/ledger-live/commit/212f772b17dc3db97009ebe62912f8f183c1ef2e), [`8936f39`](https://github.com/LedgerHQ/ledger-live/commit/8936f390edbe9cbc36ac6590b01562daf5c580e1)]:
15
- - @ledgerhq/coin-canton@0.2.2-nightly.1
16
- - @ledgerhq/errors@6.25.0-nightly.0
17
- - @ledgerhq/hw-transport@6.31.10-nightly.0
13
+ - Updated dependencies [[`80f8f1e`](https://github.com/LedgerHQ/ledger-live/commit/80f8f1eaef8a7bc84ba5441790296dec6cbfa199)]:
14
+ - @ledgerhq/coin-canton@0.3.0
18
15
 
19
- ## 0.2.2-nightly.0
16
+ ## 0.2.2-next.0
20
17
 
21
18
  ### Patch Changes
22
19
 
23
- - Updated dependencies []:
24
- - @ledgerhq/coin-canton@0.2.2-nightly.0
20
+ - Updated dependencies [[`80f8f1e`](https://github.com/LedgerHQ/ledger-live/commit/80f8f1eaef8a7bc84ba5441790296dec6cbfa199)]:
21
+ - @ledgerhq/coin-canton@0.3.0-next.0
25
22
 
26
23
  ## 0.2.1
27
24
 
package/README.md CHANGED
@@ -74,3 +74,35 @@ Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/
74
74
  ### MockCantonDevice
75
75
 
76
76
  Mock Canton "@ledgerhq/hw-app-canton" device implementation for development and testing
77
+
78
+ ## Integration tests
79
+
80
+ ### 1. Prerequisite
81
+
82
+ Download latest version of Ledger SDK.
83
+
84
+ ```sh
85
+ docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
86
+ ```
87
+
88
+ The rest of the documentation is about testing on NanoSP.
89
+
90
+ ### 2. Compile app-canton
91
+
92
+ You have to [compile](https://github.com/ledgerhq/app-canton) or retrieve the app binaries. In the following line, the binaries directory is set as `<ABSOLUTE_PATH_TO_ELFS>`
93
+
94
+ ### 3. Launch CantonApp within Speculos
95
+
96
+ ```sh
97
+ docker run --rm -t -d -v "<ABSOLUTE_PATH_TO_ELFS>:/app" -p 5000:5000 ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest
98
+
99
+ docker exec -it speculos bash -c 'speculos --model "nanosp" /app/exchange_nanosp.elf -l /app/ethereum_nanosp.elf --display headless'
100
+ ```
101
+
102
+ ### 4. Launch integration tests
103
+
104
+ From Ledger Live root directory:
105
+
106
+ ```sh
107
+ pnpm ljs:hw-app-canton test-integ
108
+ ```
@@ -20,7 +20,7 @@ describe("Canton", () => {
20
20
  RecordStore.fromString(`
21
21
  => e005000015058000002c80001a6f800000008000000080000000
22
22
  <= 4d65a10662b9759d62bb59048366705454654cf4f9b4b3525cf314429e46c6919000
23
- `)
23
+ `),
24
24
  );
25
25
 
26
26
  const canton = new Canton(transport);
@@ -36,7 +36,7 @@ describe("Canton", () => {
36
36
  RecordStore.fromString(`
37
37
  => e005010015058000002c80001a6f800000008000000080000000
38
38
  <= 4d65a10662b9759d62bb59048366705454654cf4f9b4b3525cf314429e46c6919000
39
- `)
39
+ `),
40
40
  );
41
41
 
42
42
  const canton = new Canton(transport);
@@ -51,9 +51,7 @@ describe("Canton", () => {
51
51
  const transport = await openTransportReplayer(new RecordStore());
52
52
  const canton = new Canton(transport);
53
53
 
54
- return expect(
55
- canton.getAddress("invalid path")
56
- ).rejects.toThrow();
54
+ return expect(canton.getAddress("invalid path")).rejects.toThrow();
57
55
  });
58
56
 
59
57
  it("should handle various derivation paths", async () => {
@@ -61,7 +59,7 @@ describe("Canton", () => {
61
59
  RecordStore.fromString(`
62
60
  => e005000015058000002c80001a6f800000008000000080000001
63
61
  <= 5e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d7029000
64
- `)
62
+ `),
65
63
  );
66
64
 
67
65
  const canton = new Canton(transport);
@@ -89,14 +87,12 @@ describe("Canton", () => {
89
87
  RecordStore.fromString(`
90
88
  => e006010015058000002c80001a6f800000008000000080000000
91
89
  <= 6985
92
- `)
90
+ `),
93
91
  );
94
92
 
95
93
  const canton = new Canton(transport);
96
94
 
97
- return expect(
98
- canton.signTransaction("44'/6767'/0'/0'/0'", "test")
99
- ).rejects.toThrow();
95
+ return expect(canton.signTransaction("44'/6767'/0'/0'/0'", "test")).rejects.toThrow();
100
96
  });
101
97
  });
102
98
 
@@ -104,9 +100,9 @@ describe("Canton", () => {
104
100
  it("should get app configuration", async () => {
105
101
  const transport = await openTransportReplayer(
106
102
  RecordStore.fromString(`
107
- => e004000000
108
- <= 00000100069000
109
- `)
103
+ => e003000000
104
+ <= 0202029000
105
+ `),
110
106
  );
111
107
 
112
108
  const canton = new Canton(transport);
@@ -119,4 +115,4 @@ describe("Canton", () => {
119
115
 
120
116
  // should handle configuration error
121
117
  });
122
- });
118
+ });
package/jest.config.ts CHANGED
@@ -3,6 +3,7 @@ import baseConfig from "../../jest.config";
3
3
  export default {
4
4
  ...baseConfig,
5
5
  rootDir: __dirname,
6
+ testPathIgnorePatterns: [...baseConfig.testPathIgnorePatterns, ".*\\.integ\\.test\\.[tj]s"],
6
7
  collectCoverageFrom: [
7
8
  "src/**/*.ts",
8
9
  "!src/**/*.test.{ts,tsx}",
@@ -0,0 +1,7 @@
1
+ import baseConfig from "../../jest.config";
2
+
3
+ export default {
4
+ ...baseConfig,
5
+ rootDir: __dirname,
6
+ testRegex: ".integ.test.ts$",
7
+ };
package/lib/Canton.d.ts CHANGED
@@ -44,5 +44,15 @@ export default class Canton {
44
44
  * @private
45
45
  */
46
46
  private hashString;
47
+ /**
48
+ * Extract Pubkey info from APDU response
49
+ * @private
50
+ */
51
+ private extractPubkeyAndChainCode;
52
+ /**
53
+ * Extract AppVersion from APDU response
54
+ * @private
55
+ */
56
+ private extractVersion;
47
57
  }
48
58
  //# sourceMappingURL=Canton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Canton.d.ts","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAoB3D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,gBAAgB,CAAC;gBAEpB,SAAS,EAAE,SAAS,EAAE,WAAW,SAAgC;IAO7E;;;;;;OAMG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAqBhF;;;;;;OAMG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAa5E;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAc/C;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAe/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;CASnB"}
1
+ {"version":3,"file":"Canton.d.ts","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAqB3D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,gBAAgB,CAAC;gBAEpB,SAAS,EAAE,SAAS,EAAE,WAAW,SAAgC;IAW7E;;;;;;OAMG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAsBhF;;;;;;OAMG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAa5E;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAiB/C;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqB/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;;OAGG;IACH,OAAO,CAAC,cAAc;CAOvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Canton.integ.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canton.integ.test.d.ts","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const hw_transport_node_speculos_http_1 = __importDefault(require("@ledgerhq/hw-transport-node-speculos-http"));
7
+ const Canton_1 = __importDefault(require("./Canton"));
8
+ describe("AppCanton", () => {
9
+ let transport;
10
+ beforeAll(async () => {
11
+ transport = await hw_transport_node_speculos_http_1.default.open({});
12
+ });
13
+ describe("getAppConfiguration", () => {
14
+ it("returns app version", async () => {
15
+ // GIVEN
16
+ const app = new Canton_1.default(transport);
17
+ // WHEN
18
+ const result = await app.getAppConfiguration();
19
+ // THEN
20
+ expect(result).toEqual({
21
+ version: "2.2.2",
22
+ });
23
+ });
24
+ });
25
+ describe("getAddress", () => {
26
+ it("retrieves address from app", async () => {
27
+ // GIVEN
28
+ const app = new Canton_1.default(transport);
29
+ const derivationPath = "44'/6767'/0'/0'/0'";
30
+ // WHEN
31
+ const result = await app.getAddress(derivationPath);
32
+ // THEN
33
+ expect(result).toEqual({
34
+ address: "canton_10cd9ed0",
35
+ publicKey: "0x043b462de34ec31fba274f2a381947aef26697912194312fc289c46cc1b2b4f6b00828dc1e4f96001b10463083edf85f2e0550862a3dc99ed411ca6d25f2bc19a8",
36
+ });
37
+ });
38
+ });
39
+ describe("signTransaction", () => {
40
+ it.todo("returns sign transaction");
41
+ });
42
+ });
43
+ //# sourceMappingURL=Canton.integ.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canton.integ.test.js","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":";;;;;AAAA,gHAA8E;AAC9E,sDAA8B;AAG9B,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,SAAoB,CAAC;IAEzB,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,SAAS,GAAG,MAAM,yCAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,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,EACP,sIAAsI;aACzI,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/lib/Canton.js CHANGED
@@ -11,7 +11,8 @@ const P1_NON_CONFIRM = 0x00;
11
11
  const P1_CONFIRM = 0x01;
12
12
  const P2 = 0x00;
13
13
  const INS = {
14
- GET_VERSION: 0x04,
14
+ GET_VERSION: 0x03,
15
+ GET_APP_NAME: 0x04,
15
16
  GET_ADDR: 0x05,
16
17
  SIGN: 0x06,
17
18
  };
@@ -28,7 +29,7 @@ class Canton {
28
29
  constructor(transport, scrambleKey = "canton_default_scramble_key") {
29
30
  this.transport = transport;
30
31
  this.transportMock = new MockDevice_1.MockCantonDevice();
31
- transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction"], scrambleKey);
32
+ transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction", "getAppConfiguration"], scrambleKey);
32
33
  }
33
34
  /**
34
35
  * Get a Canton address for a given BIP-32 path.
@@ -41,10 +42,11 @@ class Canton {
41
42
  const bipPath = bip32_path_1.default.fromString(path).toPathArray();
42
43
  const serializedPath = this.serializePath(bipPath);
43
44
  const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
44
- const response = await this.transportMock.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
45
+ const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
45
46
  const responseData = this.handleTransportResponse(response, "address");
47
+ const { pubKey } = this.extractPubkeyAndChainCode(responseData);
46
48
  // Handle 65-byte uncompressed SECP256R1 public key
47
- const publicKey = "0x" + responseData.toString("hex");
49
+ const publicKey = "0x" + pubKey;
48
50
  const addressHash = this.hashString(publicKey);
49
51
  const address = "canton_" + addressHash.substring(0, 36);
50
52
  return {
@@ -73,7 +75,9 @@ class Canton {
73
75
  * @return the app configuration including version
74
76
  */
75
77
  async getAppConfiguration() {
76
- const [major, minor, patch] = await this.transportMock.send(CLA, INS.GET_VERSION, P1_NON_CONFIRM, P2, Buffer.alloc(0));
78
+ const response = await this.transport.send(CLA, INS.GET_VERSION, P1_NON_CONFIRM, P2, Buffer.alloc(0));
79
+ const responseData = this.handleTransportResponse(response, "version");
80
+ const { major, minor, patch } = this.extractVersion(responseData);
77
81
  return {
78
82
  version: `${major}.${minor}.${patch}`,
79
83
  };
@@ -86,11 +90,13 @@ class Canton {
86
90
  const statusCode = response.readUInt16BE(response.length - 2);
87
91
  const responseData = response.slice(0, response.length - 2);
88
92
  if (statusCode === STATUS.USER_CANCEL) {
89
- if (errorType === "address") {
90
- throw new errors_1.UserRefusedAddress();
91
- }
92
- else {
93
- throw new errors_1.UserRefusedOnDevice();
93
+ switch (errorType) {
94
+ case "address":
95
+ throw new errors_1.UserRefusedAddress();
96
+ case "transaction":
97
+ throw new errors_1.UserRefusedOnDevice();
98
+ default:
99
+ throw new Error();
94
100
  }
95
101
  }
96
102
  return responseData;
@@ -120,6 +126,28 @@ class Canton {
120
126
  }
121
127
  return Math.abs(hash).toString(16);
122
128
  }
129
+ /**
130
+ * Extract Pubkey info from APDU response
131
+ * @private
132
+ */
133
+ extractPubkeyAndChainCode(data) {
134
+ const pubkeySize = parseInt(data.subarray(0, 1).toString("hex"), 16);
135
+ const pubKey = data.subarray(1, pubkeySize + 1);
136
+ const chainCodeSize = parseInt(data.subarray(pubkeySize + 1, pubkeySize + 2).toString("hex"), 16);
137
+ const chainCode = data.subarray(pubkeySize + 2, pubkeySize + chainCodeSize + 2);
138
+ return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
139
+ }
140
+ /**
141
+ * Extract AppVersion from APDU response
142
+ * @private
143
+ */
144
+ extractVersion(data) {
145
+ return {
146
+ major: parseInt(data.subarray(0, 1).toString("hex"), 16),
147
+ minor: parseInt(data.subarray(1, 2).toString("hex"), 16),
148
+ patch: parseInt(data.subarray(2, 3).toString("hex"), 16),
149
+ };
150
+ }
123
151
  }
124
152
  exports.default = Canton;
125
153
  //# sourceMappingURL=Canton.js.map
package/lib/Canton.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"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"}
@@ -44,5 +44,15 @@ export default class Canton {
44
44
  * @private
45
45
  */
46
46
  private hashString;
47
+ /**
48
+ * Extract Pubkey info from APDU response
49
+ * @private
50
+ */
51
+ private extractPubkeyAndChainCode;
52
+ /**
53
+ * Extract AppVersion from APDU response
54
+ * @private
55
+ */
56
+ private extractVersion;
47
57
  }
48
58
  //# sourceMappingURL=Canton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Canton.d.ts","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAoB3D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,gBAAgB,CAAC;gBAEpB,SAAS,EAAE,SAAS,EAAE,WAAW,SAAgC;IAO7E;;;;;;OAMG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAqBhF;;;;;;OAMG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAa5E;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAc/C;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAe/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;CASnB"}
1
+ {"version":3,"file":"Canton.d.ts","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAqB3D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,gBAAgB,CAAC;gBAEpB,SAAS,EAAE,SAAS,EAAE,WAAW,SAAgC;IAW7E;;;;;;OAMG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAsBhF;;;;;;OAMG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAa5E;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAiB/C;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqB/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;;OAGG;IACH,OAAO,CAAC,cAAc;CAOvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Canton.integ.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canton.integ.test.d.ts","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ import SpeculosTransportHttp from "@ledgerhq/hw-transport-node-speculos-http";
2
+ import Canton from "./Canton";
3
+ describe("AppCanton", () => {
4
+ let transport;
5
+ beforeAll(async () => {
6
+ transport = await SpeculosTransportHttp.open({});
7
+ });
8
+ describe("getAppConfiguration", () => {
9
+ it("returns app version", async () => {
10
+ // GIVEN
11
+ const app = new Canton(transport);
12
+ // WHEN
13
+ const result = await app.getAppConfiguration();
14
+ // THEN
15
+ expect(result).toEqual({
16
+ version: "2.2.2",
17
+ });
18
+ });
19
+ });
20
+ describe("getAddress", () => {
21
+ it("retrieves address from app", async () => {
22
+ // GIVEN
23
+ const app = new Canton(transport);
24
+ const derivationPath = "44'/6767'/0'/0'/0'";
25
+ // WHEN
26
+ const result = await app.getAddress(derivationPath);
27
+ // THEN
28
+ expect(result).toEqual({
29
+ address: "canton_10cd9ed0",
30
+ publicKey: "0x043b462de34ec31fba274f2a381947aef26697912194312fc289c46cc1b2b4f6b00828dc1e4f96001b10463083edf85f2e0550862a3dc99ed411ca6d25f2bc19a8",
31
+ });
32
+ });
33
+ });
34
+ describe("signTransaction", () => {
35
+ it.todo("returns sign transaction");
36
+ });
37
+ });
38
+ //# sourceMappingURL=Canton.integ.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Canton.integ.test.js","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,MAAM,2CAA2C,CAAC;AAC9E,OAAO,MAAM,MAAM,UAAU,CAAC;AAG9B,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,SAAoB,CAAC;IAEzB,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,SAAS,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,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,MAAM,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,MAAM,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,EACP,sIAAsI;aACzI,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/lib-es/Canton.js CHANGED
@@ -6,7 +6,8 @@ const P1_NON_CONFIRM = 0x00;
6
6
  const P1_CONFIRM = 0x01;
7
7
  const P2 = 0x00;
8
8
  const INS = {
9
- GET_VERSION: 0x04,
9
+ GET_VERSION: 0x03,
10
+ GET_APP_NAME: 0x04,
10
11
  GET_ADDR: 0x05,
11
12
  SIGN: 0x06,
12
13
  };
@@ -23,7 +24,7 @@ export default class Canton {
23
24
  constructor(transport, scrambleKey = "canton_default_scramble_key") {
24
25
  this.transport = transport;
25
26
  this.transportMock = new MockCantonDevice();
26
- transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction"], scrambleKey);
27
+ transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction", "getAppConfiguration"], scrambleKey);
27
28
  }
28
29
  /**
29
30
  * Get a Canton address for a given BIP-32 path.
@@ -36,10 +37,11 @@ export default class Canton {
36
37
  const bipPath = BIPPath.fromString(path).toPathArray();
37
38
  const serializedPath = this.serializePath(bipPath);
38
39
  const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
39
- const response = await this.transportMock.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
40
+ const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
40
41
  const responseData = this.handleTransportResponse(response, "address");
42
+ const { pubKey } = this.extractPubkeyAndChainCode(responseData);
41
43
  // Handle 65-byte uncompressed SECP256R1 public key
42
- const publicKey = "0x" + responseData.toString("hex");
44
+ const publicKey = "0x" + pubKey;
43
45
  const addressHash = this.hashString(publicKey);
44
46
  const address = "canton_" + addressHash.substring(0, 36);
45
47
  return {
@@ -68,7 +70,9 @@ export default class Canton {
68
70
  * @return the app configuration including version
69
71
  */
70
72
  async getAppConfiguration() {
71
- const [major, minor, patch] = await this.transportMock.send(CLA, INS.GET_VERSION, P1_NON_CONFIRM, P2, Buffer.alloc(0));
73
+ const response = await this.transport.send(CLA, INS.GET_VERSION, P1_NON_CONFIRM, P2, Buffer.alloc(0));
74
+ const responseData = this.handleTransportResponse(response, "version");
75
+ const { major, minor, patch } = this.extractVersion(responseData);
72
76
  return {
73
77
  version: `${major}.${minor}.${patch}`,
74
78
  };
@@ -81,11 +85,13 @@ export default class Canton {
81
85
  const statusCode = response.readUInt16BE(response.length - 2);
82
86
  const responseData = response.slice(0, response.length - 2);
83
87
  if (statusCode === STATUS.USER_CANCEL) {
84
- if (errorType === "address") {
85
- throw new UserRefusedAddress();
86
- }
87
- else {
88
- throw new UserRefusedOnDevice();
88
+ switch (errorType) {
89
+ case "address":
90
+ throw new UserRefusedAddress();
91
+ case "transaction":
92
+ throw new UserRefusedOnDevice();
93
+ default:
94
+ throw new Error();
89
95
  }
90
96
  }
91
97
  return responseData;
@@ -115,5 +121,27 @@ export default class Canton {
115
121
  }
116
122
  return Math.abs(hash).toString(16);
117
123
  }
124
+ /**
125
+ * Extract Pubkey info from APDU response
126
+ * @private
127
+ */
128
+ 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);
133
+ return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
134
+ }
135
+ /**
136
+ * Extract AppVersion from APDU response
137
+ * @private
138
+ */
139
+ extractVersion(data) {
140
+ return {
141
+ major: parseInt(data.subarray(0, 1).toString("hex"), 16),
142
+ minor: parseInt(data.subarray(1, 2).toString("hex"), 16),
143
+ patch: parseInt(data.subarray(2, 3).toString("hex"), 16),
144
+ };
145
+ }
118
146
  }
119
147
  //# sourceMappingURL=Canton.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.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.2.2-nightly.2",
3
+ "version": "0.3.0-nightly.0",
4
4
  "description": "Ledger Hardware Wallet Canton Application API",
5
5
  "keywords": [
6
6
  "Ledger",
@@ -28,7 +28,7 @@
28
28
  "bip32-path": "^0.4.2",
29
29
  "@ledgerhq/errors": "^6.25.0-nightly.0",
30
30
  "@ledgerhq/hw-transport": "^6.31.10-nightly.0",
31
- "@ledgerhq/coin-canton": "^0.2.2-nightly.2"
31
+ "@ledgerhq/coin-canton": "^0.3.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/jest": "^29.5.10",
@@ -39,7 +39,8 @@
39
39
  "source-map-support": "^0.5.21",
40
40
  "ts-jest": "^29.1.1",
41
41
  "ts-node": "^10.4.0",
42
- "@ledgerhq/hw-transport-mocker": "^6.29.10-nightly.0"
42
+ "@ledgerhq/hw-transport-mocker": "^6.29.10-nightly.0",
43
+ "@ledgerhq/hw-transport-node-speculos-http": "^6.29.10-nightly.0"
43
44
  },
44
45
  "scripts": {
45
46
  "clean": "rimraf lib lib-es",
@@ -51,7 +52,8 @@
51
52
  "doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
52
53
  "lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
53
54
  "lint:fix": "pnpm lint --fix",
54
- "test": "jest",
55
+ "test": "jest --config=jest.config.ts",
56
+ "test-integ": "jest --config=jest.integ.config.ts",
55
57
  "unimported": "unimported"
56
58
  }
57
59
  }
@@ -0,0 +1,48 @@
1
+ import SpeculosTransportHttp from "@ledgerhq/hw-transport-node-speculos-http";
2
+ import Canton from "./Canton";
3
+ import Transport from "@ledgerhq/hw-transport";
4
+
5
+ describe("AppCanton", () => {
6
+ let transport: Transport;
7
+
8
+ beforeAll(async () => {
9
+ transport = await SpeculosTransportHttp.open({});
10
+ });
11
+
12
+ describe("getAppConfiguration", () => {
13
+ it("returns app version", async () => {
14
+ // GIVEN
15
+ const app = new Canton(transport);
16
+
17
+ // WHEN
18
+ const result = await app.getAppConfiguration();
19
+
20
+ // THEN
21
+ expect(result).toEqual({
22
+ version: "2.2.2",
23
+ });
24
+ });
25
+ });
26
+
27
+ describe("getAddress", () => {
28
+ it("retrieves address from app", async () => {
29
+ // GIVEN
30
+ const app = new Canton(transport);
31
+ const derivationPath = "44'/6767'/0'/0'/0'";
32
+
33
+ // WHEN
34
+ const result = await app.getAddress(derivationPath);
35
+
36
+ // THEN
37
+ expect(result).toEqual({
38
+ address: "canton_10cd9ed0",
39
+ publicKey:
40
+ "0x043b462de34ec31fba274f2a381947aef26697912194312fc289c46cc1b2b4f6b00828dc1e4f96001b10463083edf85f2e0550862a3dc99ed411ca6d25f2bc19a8",
41
+ });
42
+ });
43
+ });
44
+
45
+ describe("signTransaction", () => {
46
+ it.todo("returns sign transaction");
47
+ });
48
+ });
package/src/Canton.ts CHANGED
@@ -13,7 +13,8 @@ const P1_CONFIRM = 0x01;
13
13
  const P2 = 0x00;
14
14
 
15
15
  const INS = {
16
- GET_VERSION: 0x04,
16
+ GET_VERSION: 0x03,
17
+ GET_APP_NAME: 0x04,
17
18
  GET_ADDR: 0x05,
18
19
  SIGN: 0x06,
19
20
  };
@@ -34,7 +35,11 @@ export default class Canton {
34
35
  this.transport = transport;
35
36
  this.transportMock = new MockCantonDevice();
36
37
 
37
- transport.decorateAppAPIMethods(this, ["getAddress", "signTransaction"], scrambleKey);
38
+ transport.decorateAppAPIMethods(
39
+ this,
40
+ ["getAddress", "signTransaction", "getAppConfiguration"],
41
+ scrambleKey,
42
+ );
38
43
  }
39
44
 
40
45
  /**
@@ -49,12 +54,13 @@ export default class Canton {
49
54
  const serializedPath = this.serializePath(bipPath);
50
55
 
51
56
  const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
52
- const response = await this.transportMock.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
57
+ const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2, serializedPath);
53
58
 
54
59
  const responseData = this.handleTransportResponse(response, "address");
60
+ const { pubKey } = this.extractPubkeyAndChainCode(responseData);
55
61
 
56
62
  // Handle 65-byte uncompressed SECP256R1 public key
57
- const publicKey = "0x" + responseData.toString("hex");
63
+ const publicKey = "0x" + pubKey;
58
64
 
59
65
  const addressHash = this.hashString(publicKey);
60
66
  const address = "canton_" + addressHash.substring(0, 36);
@@ -90,7 +96,7 @@ export default class Canton {
90
96
  * @return the app configuration including version
91
97
  */
92
98
  async getAppConfiguration(): Promise<AppConfig> {
93
- const [major, minor, patch] = await this.transportMock.send(
99
+ const response = await this.transport.send(
94
100
  CLA,
95
101
  INS.GET_VERSION,
96
102
  P1_NON_CONFIRM,
@@ -98,6 +104,9 @@ export default class Canton {
98
104
  Buffer.alloc(0),
99
105
  );
100
106
 
107
+ const responseData = this.handleTransportResponse(response, "version");
108
+ const { major, minor, patch } = this.extractVersion(responseData);
109
+
101
110
  return {
102
111
  version: `${major}.${minor}.${patch}`,
103
112
  };
@@ -107,15 +116,21 @@ export default class Canton {
107
116
  * Helper method to handle transport response and check for errors
108
117
  * @private
109
118
  */
110
- private handleTransportResponse(response: Buffer, errorType: "address" | "transaction"): Buffer {
119
+ private handleTransportResponse(
120
+ response: Buffer,
121
+ errorType: "address" | "transaction" | "version",
122
+ ): Buffer {
111
123
  const statusCode = response.readUInt16BE(response.length - 2);
112
124
  const responseData = response.slice(0, response.length - 2);
113
125
 
114
126
  if (statusCode === STATUS.USER_CANCEL) {
115
- if (errorType === "address") {
116
- throw new UserRefusedAddress();
117
- } else {
118
- throw new UserRefusedOnDevice();
127
+ switch (errorType) {
128
+ case "address":
129
+ throw new UserRefusedAddress();
130
+ case "transaction":
131
+ throw new UserRefusedOnDevice();
132
+ default:
133
+ throw new Error();
119
134
  }
120
135
  }
121
136
 
@@ -150,4 +165,32 @@ export default class Canton {
150
165
  }
151
166
  return Math.abs(hash).toString(16);
152
167
  }
168
+
169
+ /**
170
+ * Extract Pubkey info from APDU response
171
+ * @private
172
+ */
173
+ private extractPubkeyAndChainCode(data: Buffer): { pubKey: string; chainCode: string } {
174
+ const pubkeySize = parseInt(data.subarray(0, 1).toString("hex"), 16);
175
+ const pubKey = data.subarray(1, pubkeySize + 1);
176
+
177
+ const chainCodeSize = parseInt(
178
+ data.subarray(pubkeySize + 1, pubkeySize + 2).toString("hex"),
179
+ 16,
180
+ );
181
+ const chainCode = data.subarray(pubkeySize + 2, pubkeySize + chainCodeSize + 2);
182
+ return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
183
+ }
184
+
185
+ /**
186
+ * Extract AppVersion from APDU response
187
+ * @private
188
+ */
189
+ private extractVersion(data: Buffer): { major: number; minor: number; patch: number } {
190
+ return {
191
+ major: parseInt(data.subarray(0, 1).toString("hex"), 16),
192
+ minor: parseInt(data.subarray(1, 2).toString("hex"), 16),
193
+ patch: parseInt(data.subarray(2, 3).toString("hex"), 16),
194
+ };
195
+ }
153
196
  }