@ledgerhq/hw-app-canton 0.2.2 → 0.3.0-nightly.1
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 +13 -0
- package/README.md +32 -0
- package/__tests__/Canton.test.ts +10 -14
- package/jest.config.ts +1 -0
- package/jest.integ.config.ts +7 -0
- package/lib/Canton.d.ts +10 -0
- package/lib/Canton.d.ts.map +1 -1
- package/lib/Canton.integ.test.d.ts +2 -0
- package/lib/Canton.integ.test.d.ts.map +1 -0
- package/lib/Canton.integ.test.js +43 -0
- package/lib/Canton.integ.test.js.map +1 -0
- package/lib/Canton.js +38 -10
- package/lib/Canton.js.map +1 -1
- package/lib-es/Canton.d.ts +10 -0
- package/lib-es/Canton.d.ts.map +1 -1
- package/lib-es/Canton.integ.test.d.ts +2 -0
- package/lib-es/Canton.integ.test.d.ts.map +1 -0
- package/lib-es/Canton.integ.test.js +38 -0
- package/lib-es/Canton.integ.test.js.map +1 -0
- package/lib-es/Canton.js +38 -10
- package/lib-es/Canton.js.map +1 -1
- package/package.json +9 -7
- package/src/Canton.integ.test.ts +48 -0
- package/src/Canton.ts +53 -10
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> @ledgerhq/hw-app-canton@0.
|
|
2
|
+
> @ledgerhq/hw-app-canton@0.3.0-nightly.0 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,5 +1,18 @@
|
|
|
1
1
|
# @ledgerhq/hw-app-canton
|
|
2
2
|
|
|
3
|
+
## 0.3.0-nightly.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`12277dc`](https://github.com/LedgerHQ/ledger-live/commit/12277dcb478f24152060e3e11e2eb37d650b5b60), [`c1209a7`](https://github.com/LedgerHQ/ledger-live/commit/c1209a70f6362fe8a52139ad5ad0b4705aac00fb), [`58ef394`](https://github.com/LedgerHQ/ledger-live/commit/58ef39468870e56745a3a4bc95a1292a1e1f64ca)]:
|
|
8
|
+
- @ledgerhq/coin-canton@0.4.0-nightly.0
|
|
9
|
+
|
|
10
|
+
## 0.3.0-nightly.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- [#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
|
|
15
|
+
|
|
3
16
|
## 0.2.2
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
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
|
+
```
|
package/__tests__/Canton.test.ts
CHANGED
|
@@ -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
|
-
=>
|
|
108
|
-
<=
|
|
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
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
|
package/lib/Canton.d.ts.map
CHANGED
|
@@ -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;
|
|
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 @@
|
|
|
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:
|
|
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.
|
|
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" +
|
|
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
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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":";;;;;AACA,6CAA2E;AAE3E,4DAAiC;AAEjC,6CAA2D;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,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,MAAqB,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,6BAAgB,EAAE,CAAC;QAE5C,SAAS,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"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"}
|
package/lib-es/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
|
package/lib-es/Canton.d.ts.map
CHANGED
|
@@ -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;
|
|
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 @@
|
|
|
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:
|
|
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.
|
|
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" +
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
package/lib-es/Canton.js.map
CHANGED
|
@@ -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,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,
|
|
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.
|
|
3
|
+
"version": "0.3.0-nightly.1",
|
|
4
4
|
"description": "Ledger Hardware Wallet Canton Application API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"license": "Apache-2.0",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"bip32-path": "^0.4.2",
|
|
29
|
-
"@ledgerhq/errors": "^6.
|
|
30
|
-
"@ledgerhq/hw-transport": "^6.31.
|
|
31
|
-
"@ledgerhq/coin-canton": "^0.
|
|
29
|
+
"@ledgerhq/errors": "^6.25.0-nightly.0",
|
|
30
|
+
"@ledgerhq/hw-transport": "^6.31.10-nightly.0",
|
|
31
|
+
"@ledgerhq/coin-canton": "^0.4.0-nightly.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/jest": "^29.5.10",
|
|
@@ -39,19 +39,21 @@
|
|
|
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.
|
|
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",
|
|
46
47
|
"build": "tsc && tsc -m esnext --moduleResolution bundler --outDir lib-es",
|
|
47
|
-
"coverage": "jest --coverage --passWithNoTests
|
|
48
|
+
"coverage": "jest --coverage --passWithNoTests",
|
|
48
49
|
"prewatch": "pnpm build",
|
|
49
50
|
"watch": "tsc --watch",
|
|
50
51
|
"watch:es": "tsc --watch -m esnext --moduleResolution bundler --outDir lib-es",
|
|
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:
|
|
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(
|
|
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.
|
|
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" +
|
|
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
|
|
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(
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
}
|