@ledgerhq/hw-app-canton 0.3.0-nightly.2 → 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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +20 -7
- package/lib/Canton.d.ts +6 -0
- package/lib/Canton.d.ts.map +1 -1
- package/lib/Canton.js +41 -9
- package/lib/Canton.js.map +1 -1
- package/lib/Canton.test.js +40 -15
- package/lib/Canton.test.js.map +1 -1
- package/lib-es/Canton.d.ts +6 -0
- package/lib-es/Canton.d.ts.map +1 -1
- package/lib-es/Canton.js +41 -9
- package/lib-es/Canton.js.map +1 -1
- package/lib-es/Canton.test.js +40 -15
- package/lib-es/Canton.test.js.map +1 -1
- package/package.json +5 -5
- package/src/Canton.test.ts +45 -21
- package/src/Canton.ts +51 -12
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
> @ledgerhq/hw-app-canton@0.3.0
|
|
2
|
+
> @ledgerhq/hw-app-canton@0.3.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,24 +1,37 @@
|
|
|
1
1
|
# @ledgerhq/hw-app-canton
|
|
2
2
|
|
|
3
|
-
## 0.3.
|
|
3
|
+
## 0.3.1-nightly.0
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Patch Changes
|
|
6
6
|
|
|
7
|
-
- [#
|
|
7
|
+
- [#11722](https://github.com/LedgerHQ/ledger-live/pull/11722) [`a87922d`](https://github.com/LedgerHQ/ledger-live/commit/a87922dc99e4f2e4b40a46fd52ad08a71012fe94) Thanks [@hedi-edelbloute](https://github.com/hedi-edelbloute)! - Add canton onboard ui flow
|
|
8
8
|
|
|
9
|
-
## 0.3.0
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#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
|
|
10
14
|
|
|
11
15
|
### Patch Changes
|
|
12
16
|
|
|
13
|
-
- Updated dependencies [[`12277dc`](https://github.com/LedgerHQ/ledger-live/commit/12277dcb478f24152060e3e11e2eb37d650b5b60), [`
|
|
14
|
-
- @ledgerhq/coin-canton@0.4.0
|
|
17
|
+
- Updated dependencies [[`12277dc`](https://github.com/LedgerHQ/ledger-live/commit/12277dcb478f24152060e3e11e2eb37d650b5b60), [`212f772`](https://github.com/LedgerHQ/ledger-live/commit/212f772b17dc3db97009ebe62912f8f183c1ef2e), [`8936f39`](https://github.com/LedgerHQ/ledger-live/commit/8936f390edbe9cbc36ac6590b01562daf5c580e1)]:
|
|
18
|
+
- @ledgerhq/coin-canton@0.4.0
|
|
19
|
+
- @ledgerhq/errors@6.25.0
|
|
20
|
+
- @ledgerhq/hw-transport@6.31.10
|
|
15
21
|
|
|
16
|
-
## 0.3.0-
|
|
22
|
+
## 0.3.0-next.0
|
|
17
23
|
|
|
18
24
|
### Minor Changes
|
|
19
25
|
|
|
20
26
|
- [#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
|
|
21
27
|
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`12277dc`](https://github.com/LedgerHQ/ledger-live/commit/12277dcb478f24152060e3e11e2eb37d650b5b60), [`212f772`](https://github.com/LedgerHQ/ledger-live/commit/212f772b17dc3db97009ebe62912f8f183c1ef2e), [`8936f39`](https://github.com/LedgerHQ/ledger-live/commit/8936f390edbe9cbc36ac6590b01562daf5c580e1)]:
|
|
31
|
+
- @ledgerhq/coin-canton@0.4.0-next.0
|
|
32
|
+
- @ledgerhq/errors@6.25.0-next.0
|
|
33
|
+
- @ledgerhq/hw-transport@6.31.10-next.0
|
|
34
|
+
|
|
22
35
|
## 0.2.2
|
|
23
36
|
|
|
24
37
|
### Patch Changes
|
package/lib/Canton.d.ts
CHANGED
|
@@ -34,6 +34,12 @@ export default class Canton {
|
|
|
34
34
|
* @return the app configuration including version
|
|
35
35
|
*/
|
|
36
36
|
getAppConfiguration(): Promise<AppConfig>;
|
|
37
|
+
/**
|
|
38
|
+
* Converts 65-byte Canton format to 64-byte Ed25519:
|
|
39
|
+
* [40][64_bytes_signature][00] (132 hex chars)
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
private cleanSignatureFormat;
|
|
37
43
|
/**
|
|
38
44
|
* Helper method to handle transport response and check for errors
|
|
39
45
|
* @private
|
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;
|
|
1
|
+
{"version":3,"file":"Canton.d.ts","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAiCpD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,EAAE,SAAS,CAAC;gBAET,SAAS,EAAE,SAAS,EAAE,WAAW,SAAgC;IAU7E;;;;;;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,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA+B7E;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAiB/C;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqB/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAwBjC;;;OAGG;IACH,OAAO,CAAC,cAAc;CAOvB"}
|
package/lib/Canton.js
CHANGED
|
@@ -14,6 +14,8 @@ const P2_NONE = 0x00;
|
|
|
14
14
|
const P2_FIRST = 0x01;
|
|
15
15
|
// P2 indicating that this is not the last APDU in a large request.
|
|
16
16
|
const P2_MORE = 0x02;
|
|
17
|
+
// P2 indicating that this is the last APDU of a message in a multi message request.
|
|
18
|
+
const P2_MSG_END = 0x04;
|
|
17
19
|
const INS = {
|
|
18
20
|
GET_VERSION: 0x03,
|
|
19
21
|
GET_APP_NAME: 0x04,
|
|
@@ -24,6 +26,8 @@ const STATUS = {
|
|
|
24
26
|
OK: 0x9000,
|
|
25
27
|
USER_CANCEL: 0x6985,
|
|
26
28
|
};
|
|
29
|
+
const ED25519_SIGNATURE_HEX_LENGTH = 128; // hex characters (64 bytes)
|
|
30
|
+
const CANTON_SIGNATURE_HEX_LENGTH = 132; // hex characters (66 bytes with framing)
|
|
27
31
|
/**
|
|
28
32
|
* Canton BOLOS API
|
|
29
33
|
*/
|
|
@@ -41,7 +45,8 @@ class Canton {
|
|
|
41
45
|
* @return the address and public key
|
|
42
46
|
*/
|
|
43
47
|
async getAddress(path, display = false) {
|
|
44
|
-
|
|
48
|
+
// TODO: replace with the actual path fix wrong path "44'/6767'/0'" being used
|
|
49
|
+
const bipPath = bip32_path_1.default.fromString("m/44'/6767'/0'/0'/0'").toPathArray();
|
|
45
50
|
const serializedPath = this.serializePath(bipPath);
|
|
46
51
|
const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
|
|
47
52
|
const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
|
|
@@ -64,15 +69,16 @@ class Canton {
|
|
|
64
69
|
*/
|
|
65
70
|
async signTransaction(path, txHash) {
|
|
66
71
|
// 1. Send the derivation path
|
|
67
|
-
|
|
72
|
+
// TODO: replace with the actual path fix wrong path "44'/6767'/0'" being used
|
|
73
|
+
const bipPath = bip32_path_1.default.fromString("m/44'/6767'/0'/0'/0'").toPathArray();
|
|
68
74
|
const serializedPath = this.serializePath(bipPath);
|
|
69
75
|
const pathResponse = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM, P2_FIRST | P2_MORE, serializedPath);
|
|
70
76
|
this.handleTransportResponse(pathResponse, "transaction");
|
|
71
77
|
// 2. Send the transaction hash
|
|
72
|
-
const response = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM,
|
|
78
|
+
const response = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM, P2_MSG_END, Buffer.from(txHash, "hex"));
|
|
73
79
|
const responseData = this.handleTransportResponse(response, "transaction");
|
|
74
|
-
const
|
|
75
|
-
return
|
|
80
|
+
const rawSignature = responseData.toString("hex");
|
|
81
|
+
return this.cleanSignatureFormat(rawSignature);
|
|
76
82
|
}
|
|
77
83
|
/**
|
|
78
84
|
* Get the app configuration.
|
|
@@ -86,6 +92,22 @@ class Canton {
|
|
|
86
92
|
version: `${major}.${minor}.${patch}`,
|
|
87
93
|
};
|
|
88
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Converts 65-byte Canton format to 64-byte Ed25519:
|
|
97
|
+
* [40][64_bytes_signature][00] (132 hex chars)
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
cleanSignatureFormat(signature) {
|
|
101
|
+
if (signature.length === ED25519_SIGNATURE_HEX_LENGTH) {
|
|
102
|
+
return signature;
|
|
103
|
+
}
|
|
104
|
+
if (signature.length === CANTON_SIGNATURE_HEX_LENGTH) {
|
|
105
|
+
const cleanedSignature = signature.slice(2, -2);
|
|
106
|
+
return cleanedSignature;
|
|
107
|
+
}
|
|
108
|
+
console.warn(`[Canton]: Unknown signature format (${signature.length} chars)`);
|
|
109
|
+
return signature;
|
|
110
|
+
}
|
|
89
111
|
/**
|
|
90
112
|
* Helper method to handle transport response and check for errors
|
|
91
113
|
* @private
|
|
@@ -135,10 +157,20 @@ class Canton {
|
|
|
135
157
|
* @private
|
|
136
158
|
*/
|
|
137
159
|
extractPubkeyAndChainCode(data) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
160
|
+
// Parse the response according to the Python unpack_get_addr_response format:
|
|
161
|
+
// response = pubkey_len (1) + pubkey (var) + chaincode_len (1) + chaincode (var)
|
|
162
|
+
let offset = 0;
|
|
163
|
+
// Extract public key length (1 byte)
|
|
164
|
+
const pubkeySize = data.readUInt8(offset);
|
|
165
|
+
offset += 1;
|
|
166
|
+
// Extract public key
|
|
167
|
+
const pubKey = data.subarray(offset, offset + pubkeySize);
|
|
168
|
+
offset += pubkeySize;
|
|
169
|
+
// Extract chain code length (1 byte)
|
|
170
|
+
const chainCodeSize = data.readUInt8(offset);
|
|
171
|
+
offset += 1;
|
|
172
|
+
// Extract chain code
|
|
173
|
+
const chainCode = data.subarray(offset, offset + chainCodeSize);
|
|
142
174
|
return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
|
|
143
175
|
}
|
|
144
176
|
/**
|
package/lib/Canton.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":";;;;;AACA,6CAA2E;AAC3E,4DAAiC;AAEjC,MAAM,GAAG,GAAG,IAAI,CAAC;AAEjB,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,gCAAgC;AAChC,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,+CAA+C;AAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,mEAAmE;AACnE,MAAM,OAAO,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":";;;;;AACA,6CAA2E;AAC3E,4DAAiC;AAEjC,MAAM,GAAG,GAAG,IAAI,CAAC;AAEjB,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,gCAAgC;AAChC,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,+CAA+C;AAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,mEAAmE;AACnE,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,oFAAoF;AACpF,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,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,MAAM,4BAA4B,GAAG,GAAG,CAAC,CAAC,4BAA4B;AACtE,MAAM,2BAA2B,GAAG,GAAG,CAAC,CAAC,yCAAyC;AAalF;;GAEG;AACH,MAAqB,MAAM;IACzB,SAAS,CAAY;IAErB,YAAY,SAAoB,EAAE,WAAW,GAAG,6BAA6B;QAC3E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,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,8EAA8E;QAC9E,MAAM,OAAO,GAAG,oBAAO,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,WAAW,EAAE,CAAC;QACzE,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,OAAO,EAAE,cAAc,CAAC,CAAC;QAE3F,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,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC;QAChC,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,EAAE,MAAM;YACjB,OAAO;SACR,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,MAAc;QAChD,8BAA8B;QAC9B,8EAA8E;QAC9E,MAAM,OAAO,GAAG,oBAAO,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,WAAW,EAAE,CAAC;QACzE,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAEnD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAC5C,GAAG,EACH,GAAG,CAAC,IAAI,EACR,cAAc,EACd,QAAQ,GAAG,OAAO,EAClB,cAAc,CACf,CAAC;QAEF,IAAI,CAAC,uBAAuB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QAE1D,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACxC,GAAG,EACH,GAAG,CAAC,IAAI,EACR,cAAc,EACd,UAAU,EACV,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAC3B,CAAC;QAEF,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC3E,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAElD,OAAO,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC;IACjD,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,OAAO,EACP,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;;;;OAIG;IACK,oBAAoB,CAAC,SAAiB;QAC5C,IAAI,SAAS,CAAC,MAAM,KAAK,4BAA4B,EAAE,CAAC;YACtD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,2BAA2B,EAAE,CAAC;YACrD,MAAM,gBAAgB,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAChD,OAAO,gBAAgB,CAAC;QAC1B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,uCAAuC,SAAS,CAAC,MAAM,SAAS,CAAC,CAAC;QAC/E,OAAO,SAAS,CAAC;IACnB,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,2BAAkB,EAAE,CAAC;gBACjC,KAAK,aAAa;oBAChB,MAAM,IAAI,4BAAmB,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,8EAA8E;QAC9E,iFAAiF;QAEjF,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,qCAAqC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,CAAC;QAEZ,qBAAqB;QACrB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC;QAC1D,MAAM,IAAI,UAAU,CAAC;QAErB,qCAAqC;QACrC,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,IAAI,CAAC,CAAC;QAEZ,qBAAqB;QACrB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC;QAEhE,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;AApND,yBAoNC"}
|
package/lib/Canton.test.js
CHANGED
|
@@ -8,8 +8,11 @@ const Canton_1 = __importDefault(require("./Canton"));
|
|
|
8
8
|
describe("Canton", () => {
|
|
9
9
|
describe("decorateAppAPIMethods", () => {
|
|
10
10
|
it("should properly decorate transport methods", async () => {
|
|
11
|
+
// GIVEN
|
|
11
12
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(new hw_transport_mocker_1.RecordStore());
|
|
13
|
+
// WHEN
|
|
12
14
|
const canton = new Canton_1.default(transport);
|
|
15
|
+
// THEN
|
|
13
16
|
expect(canton.transport).toBeDefined();
|
|
14
17
|
expect(typeof canton.getAddress).toBe("function");
|
|
15
18
|
expect(typeof canton.signTransaction).toBe("function");
|
|
@@ -18,39 +21,53 @@ describe("Canton", () => {
|
|
|
18
21
|
});
|
|
19
22
|
describe("getAddress", () => {
|
|
20
23
|
it("should get address without display", async () => {
|
|
24
|
+
// GIVEN
|
|
21
25
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(hw_transport_mocker_1.RecordStore.fromString(`
|
|
22
26
|
=> e005000015058000002c80001a6f800000008000000080000000
|
|
23
|
-
<=
|
|
27
|
+
<= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
|
|
24
28
|
`));
|
|
25
29
|
const canton = new Canton_1.default(transport);
|
|
30
|
+
// WHEN
|
|
26
31
|
const result = await canton.getAddress("44'/6767'/0'/0'/0'");
|
|
27
|
-
|
|
28
|
-
expect(result
|
|
29
|
-
|
|
32
|
+
// THEN
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
address: "canton_1a7a97e0",
|
|
35
|
+
publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
|
|
36
|
+
});
|
|
30
37
|
});
|
|
31
38
|
it("should get address with display", async () => {
|
|
39
|
+
// GIVEN
|
|
32
40
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(hw_transport_mocker_1.RecordStore.fromString(`
|
|
33
41
|
=> e005010015058000002c80001a6f800000008000000080000000
|
|
34
|
-
<=
|
|
42
|
+
<= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
|
|
35
43
|
`));
|
|
36
44
|
const canton = new Canton_1.default(transport);
|
|
45
|
+
// WHEN
|
|
37
46
|
const result = await canton.getAddress("44'/6767'/0'/0'/0'", true);
|
|
38
|
-
|
|
39
|
-
expect(result
|
|
40
|
-
|
|
47
|
+
// THEN
|
|
48
|
+
expect(result).toEqual({
|
|
49
|
+
address: "canton_1a7a97e0",
|
|
50
|
+
publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
|
|
51
|
+
});
|
|
41
52
|
});
|
|
42
53
|
it("should throw on invalid derivation path", async () => {
|
|
54
|
+
// GIVEN
|
|
43
55
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(new hw_transport_mocker_1.RecordStore());
|
|
44
56
|
const canton = new Canton_1.default(transport);
|
|
57
|
+
// WHEN & THEN
|
|
45
58
|
return expect(canton.getAddress("invalid path")).rejects.toThrow();
|
|
46
59
|
});
|
|
47
|
-
|
|
60
|
+
// unskip when TODO comment is handled
|
|
61
|
+
it.skip("should handle various derivation paths", async () => {
|
|
62
|
+
// GIVEN
|
|
48
63
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(hw_transport_mocker_1.RecordStore.fromString(`
|
|
49
64
|
=> e005000015058000002c80001a6f800000008000000080000001
|
|
50
|
-
<=
|
|
65
|
+
<= 205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d702205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d7029000
|
|
51
66
|
`));
|
|
52
67
|
const canton = new Canton_1.default(transport);
|
|
68
|
+
// WHEN
|
|
53
69
|
const result = await canton.getAddress("44'/6767'/0'/0'/1'");
|
|
70
|
+
// THEN
|
|
54
71
|
expect(result).toBeDefined();
|
|
55
72
|
expect(result.address).toBeDefined();
|
|
56
73
|
expect(result.publicKey).toBeDefined();
|
|
@@ -63,37 +80,45 @@ describe("Canton", () => {
|
|
|
63
80
|
// should handle empty transaction
|
|
64
81
|
// should request blind signature when required
|
|
65
82
|
it("should sign transaction hash", async () => {
|
|
83
|
+
// GIVEN
|
|
66
84
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(hw_transport_mocker_1.RecordStore.fromString(`
|
|
67
85
|
=> e006000315058000002c80001a6f800000008000000080000000
|
|
68
86
|
<= 9000
|
|
69
|
-
=>
|
|
87
|
+
=> e006000420d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd
|
|
70
88
|
<= 40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00009000
|
|
71
89
|
`));
|
|
72
90
|
const canton = new Canton_1.default(transport);
|
|
73
91
|
const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
|
|
92
|
+
// WHEN
|
|
74
93
|
const result = await canton.signTransaction("44'/6767'/0'/0'/0'", txHash);
|
|
75
|
-
|
|
94
|
+
// THEN
|
|
95
|
+
expect(result).toEqual("a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00");
|
|
76
96
|
});
|
|
77
97
|
it("should handle user refused transaction", async () => {
|
|
98
|
+
// GIVEN
|
|
78
99
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(hw_transport_mocker_1.RecordStore.fromString(`
|
|
79
100
|
=> e006010015058000002c80001a6f800000008000000080000000
|
|
80
101
|
<= 6985
|
|
81
102
|
`));
|
|
82
103
|
const canton = new Canton_1.default(transport);
|
|
104
|
+
// WHEN & THEN
|
|
83
105
|
return expect(canton.signTransaction("44'/6767'/0'/0'/0'", "test")).rejects.toThrow();
|
|
84
106
|
});
|
|
85
107
|
});
|
|
86
108
|
describe("getAppConfiguration", () => {
|
|
87
109
|
it("should get app configuration", async () => {
|
|
110
|
+
// GIVEN
|
|
88
111
|
const transport = await (0, hw_transport_mocker_1.openTransportReplayer)(hw_transport_mocker_1.RecordStore.fromString(`
|
|
89
112
|
=> e003000000
|
|
90
113
|
<= 0202029000
|
|
91
114
|
`));
|
|
92
115
|
const canton = new Canton_1.default(transport);
|
|
116
|
+
// WHEN
|
|
93
117
|
const result = await canton.getAppConfiguration();
|
|
94
|
-
|
|
95
|
-
expect(result).
|
|
96
|
-
|
|
118
|
+
// THEN
|
|
119
|
+
expect(result).toEqual({
|
|
120
|
+
version: "2.2.2",
|
|
121
|
+
});
|
|
97
122
|
});
|
|
98
123
|
// should handle configuration error
|
|
99
124
|
});
|
package/lib/Canton.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Canton.test.js","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":";;;;;AAAA,uEAAmF;AACnF,sDAA8B;AAE9B,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,SAAS,GAAG,MAAM,IAAA,2CAAqB,EAAC,IAAI,iCAAW,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"Canton.test.js","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":"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"}
|
package/lib-es/Canton.d.ts
CHANGED
|
@@ -34,6 +34,12 @@ export default class Canton {
|
|
|
34
34
|
* @return the app configuration including version
|
|
35
35
|
*/
|
|
36
36
|
getAppConfiguration(): Promise<AppConfig>;
|
|
37
|
+
/**
|
|
38
|
+
* Converts 65-byte Canton format to 64-byte Ed25519:
|
|
39
|
+
* [40][64_bytes_signature][00] (132 hex chars)
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
private cleanSignatureFormat;
|
|
37
43
|
/**
|
|
38
44
|
* Helper method to handle transport response and check for errors
|
|
39
45
|
* @private
|
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;
|
|
1
|
+
{"version":3,"file":"Canton.d.ts","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAiCpD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACzB,SAAS,EAAE,SAAS,CAAC;gBAET,SAAS,EAAE,SAAS,EAAE,WAAW,SAAgC;IAU7E;;;;;;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,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA+B7E;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC;IAiB/C;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAqB/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAwBjC;;;OAGG;IACH,OAAO,CAAC,cAAc;CAOvB"}
|
package/lib-es/Canton.js
CHANGED
|
@@ -9,6 +9,8 @@ const P2_NONE = 0x00;
|
|
|
9
9
|
const P2_FIRST = 0x01;
|
|
10
10
|
// P2 indicating that this is not the last APDU in a large request.
|
|
11
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;
|
|
12
14
|
const INS = {
|
|
13
15
|
GET_VERSION: 0x03,
|
|
14
16
|
GET_APP_NAME: 0x04,
|
|
@@ -19,6 +21,8 @@ const STATUS = {
|
|
|
19
21
|
OK: 0x9000,
|
|
20
22
|
USER_CANCEL: 0x6985,
|
|
21
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)
|
|
22
26
|
/**
|
|
23
27
|
* Canton BOLOS API
|
|
24
28
|
*/
|
|
@@ -36,7 +40,8 @@ export default class Canton {
|
|
|
36
40
|
* @return the address and public key
|
|
37
41
|
*/
|
|
38
42
|
async getAddress(path, display = false) {
|
|
39
|
-
|
|
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();
|
|
40
45
|
const serializedPath = this.serializePath(bipPath);
|
|
41
46
|
const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
|
|
42
47
|
const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
|
|
@@ -59,15 +64,16 @@ export default class Canton {
|
|
|
59
64
|
*/
|
|
60
65
|
async signTransaction(path, txHash) {
|
|
61
66
|
// 1. Send the derivation path
|
|
62
|
-
|
|
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();
|
|
63
69
|
const serializedPath = this.serializePath(bipPath);
|
|
64
70
|
const pathResponse = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM, P2_FIRST | P2_MORE, serializedPath);
|
|
65
71
|
this.handleTransportResponse(pathResponse, "transaction");
|
|
66
72
|
// 2. Send the transaction hash
|
|
67
|
-
const response = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM,
|
|
73
|
+
const response = await this.transport.send(CLA, INS.SIGN, P1_NON_CONFIRM, P2_MSG_END, Buffer.from(txHash, "hex"));
|
|
68
74
|
const responseData = this.handleTransportResponse(response, "transaction");
|
|
69
|
-
const
|
|
70
|
-
return
|
|
75
|
+
const rawSignature = responseData.toString("hex");
|
|
76
|
+
return this.cleanSignatureFormat(rawSignature);
|
|
71
77
|
}
|
|
72
78
|
/**
|
|
73
79
|
* Get the app configuration.
|
|
@@ -81,6 +87,22 @@ export default class Canton {
|
|
|
81
87
|
version: `${major}.${minor}.${patch}`,
|
|
82
88
|
};
|
|
83
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
|
+
}
|
|
84
106
|
/**
|
|
85
107
|
* Helper method to handle transport response and check for errors
|
|
86
108
|
* @private
|
|
@@ -130,10 +152,20 @@ export default class Canton {
|
|
|
130
152
|
* @private
|
|
131
153
|
*/
|
|
132
154
|
extractPubkeyAndChainCode(data) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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);
|
|
137
169
|
return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
|
|
138
170
|
}
|
|
139
171
|
/**
|
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;AAC3E,OAAO,OAAO,MAAM,YAAY,CAAC;AAEjC,MAAM,GAAG,GAAG,IAAI,CAAC;AAEjB,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,gCAAgC;AAChC,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,+CAA+C;AAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,mEAAmE;AACnE,MAAM,OAAO,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"Canton.js","sourceRoot":"","sources":["../src/Canton.ts"],"names":[],"mappings":"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"}
|
package/lib-es/Canton.test.js
CHANGED
|
@@ -3,8 +3,11 @@ import Canton from "./Canton";
|
|
|
3
3
|
describe("Canton", () => {
|
|
4
4
|
describe("decorateAppAPIMethods", () => {
|
|
5
5
|
it("should properly decorate transport methods", async () => {
|
|
6
|
+
// GIVEN
|
|
6
7
|
const transport = await openTransportReplayer(new RecordStore());
|
|
8
|
+
// WHEN
|
|
7
9
|
const canton = new Canton(transport);
|
|
10
|
+
// THEN
|
|
8
11
|
expect(canton.transport).toBeDefined();
|
|
9
12
|
expect(typeof canton.getAddress).toBe("function");
|
|
10
13
|
expect(typeof canton.signTransaction).toBe("function");
|
|
@@ -13,39 +16,53 @@ describe("Canton", () => {
|
|
|
13
16
|
});
|
|
14
17
|
describe("getAddress", () => {
|
|
15
18
|
it("should get address without display", async () => {
|
|
19
|
+
// GIVEN
|
|
16
20
|
const transport = await openTransportReplayer(RecordStore.fromString(`
|
|
17
21
|
=> e005000015058000002c80001a6f800000008000000080000000
|
|
18
|
-
<=
|
|
22
|
+
<= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
|
|
19
23
|
`));
|
|
20
24
|
const canton = new Canton(transport);
|
|
25
|
+
// WHEN
|
|
21
26
|
const result = await canton.getAddress("44'/6767'/0'/0'/0'");
|
|
22
|
-
|
|
23
|
-
expect(result
|
|
24
|
-
|
|
27
|
+
// THEN
|
|
28
|
+
expect(result).toEqual({
|
|
29
|
+
address: "canton_1a7a97e0",
|
|
30
|
+
publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
|
|
31
|
+
});
|
|
25
32
|
});
|
|
26
33
|
it("should get address with display", async () => {
|
|
34
|
+
// GIVEN
|
|
27
35
|
const transport = await openTransportReplayer(RecordStore.fromString(`
|
|
28
36
|
=> e005010015058000002c80001a6f800000008000000080000000
|
|
29
|
-
<=
|
|
37
|
+
<= 20c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c5109720c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c510979000
|
|
30
38
|
`));
|
|
31
39
|
const canton = new Canton(transport);
|
|
40
|
+
// WHEN
|
|
32
41
|
const result = await canton.getAddress("44'/6767'/0'/0'/0'", true);
|
|
33
|
-
|
|
34
|
-
expect(result
|
|
35
|
-
|
|
42
|
+
// THEN
|
|
43
|
+
expect(result).toEqual({
|
|
44
|
+
address: "canton_1a7a97e0",
|
|
45
|
+
publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
|
|
46
|
+
});
|
|
36
47
|
});
|
|
37
48
|
it("should throw on invalid derivation path", async () => {
|
|
49
|
+
// GIVEN
|
|
38
50
|
const transport = await openTransportReplayer(new RecordStore());
|
|
39
51
|
const canton = new Canton(transport);
|
|
52
|
+
// WHEN & THEN
|
|
40
53
|
return expect(canton.getAddress("invalid path")).rejects.toThrow();
|
|
41
54
|
});
|
|
42
|
-
|
|
55
|
+
// unskip when TODO comment is handled
|
|
56
|
+
it.skip("should handle various derivation paths", async () => {
|
|
57
|
+
// GIVEN
|
|
43
58
|
const transport = await openTransportReplayer(RecordStore.fromString(`
|
|
44
59
|
=> e005000015058000002c80001a6f800000008000000080000001
|
|
45
|
-
<=
|
|
60
|
+
<= 205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d702205e66a10773c0860e73bb6015947806555765df5f9b5b4636df4255a57c57d7029000
|
|
46
61
|
`));
|
|
47
62
|
const canton = new Canton(transport);
|
|
63
|
+
// WHEN
|
|
48
64
|
const result = await canton.getAddress("44'/6767'/0'/0'/1'");
|
|
65
|
+
// THEN
|
|
49
66
|
expect(result).toBeDefined();
|
|
50
67
|
expect(result.address).toBeDefined();
|
|
51
68
|
expect(result.publicKey).toBeDefined();
|
|
@@ -58,37 +75,45 @@ describe("Canton", () => {
|
|
|
58
75
|
// should handle empty transaction
|
|
59
76
|
// should request blind signature when required
|
|
60
77
|
it("should sign transaction hash", async () => {
|
|
78
|
+
// GIVEN
|
|
61
79
|
const transport = await openTransportReplayer(RecordStore.fromString(`
|
|
62
80
|
=> e006000315058000002c80001a6f800000008000000080000000
|
|
63
81
|
<= 9000
|
|
64
|
-
=>
|
|
82
|
+
=> e006000420d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd
|
|
65
83
|
<= 40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00009000
|
|
66
84
|
`));
|
|
67
85
|
const canton = new Canton(transport);
|
|
68
86
|
const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
|
|
87
|
+
// WHEN
|
|
69
88
|
const result = await canton.signTransaction("44'/6767'/0'/0'/0'", txHash);
|
|
70
|
-
|
|
89
|
+
// THEN
|
|
90
|
+
expect(result).toEqual("a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00");
|
|
71
91
|
});
|
|
72
92
|
it("should handle user refused transaction", async () => {
|
|
93
|
+
// GIVEN
|
|
73
94
|
const transport = await openTransportReplayer(RecordStore.fromString(`
|
|
74
95
|
=> e006010015058000002c80001a6f800000008000000080000000
|
|
75
96
|
<= 6985
|
|
76
97
|
`));
|
|
77
98
|
const canton = new Canton(transport);
|
|
99
|
+
// WHEN & THEN
|
|
78
100
|
return expect(canton.signTransaction("44'/6767'/0'/0'/0'", "test")).rejects.toThrow();
|
|
79
101
|
});
|
|
80
102
|
});
|
|
81
103
|
describe("getAppConfiguration", () => {
|
|
82
104
|
it("should get app configuration", async () => {
|
|
105
|
+
// GIVEN
|
|
83
106
|
const transport = await openTransportReplayer(RecordStore.fromString(`
|
|
84
107
|
=> e003000000
|
|
85
108
|
<= 0202029000
|
|
86
109
|
`));
|
|
87
110
|
const canton = new Canton(transport);
|
|
111
|
+
// WHEN
|
|
88
112
|
const result = await canton.getAppConfiguration();
|
|
89
|
-
|
|
90
|
-
expect(result).
|
|
91
|
-
|
|
113
|
+
// THEN
|
|
114
|
+
expect(result).toEqual({
|
|
115
|
+
version: "2.2.2",
|
|
116
|
+
});
|
|
92
117
|
});
|
|
93
118
|
// should handle configuration error
|
|
94
119
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Canton.test.js","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,MAAM,MAAM,UAAU,CAAC;AAE9B,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC;
|
|
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.
|
|
3
|
+
"version": "0.3.1-nightly.0",
|
|
4
4
|
"description": "Ledger Hardware Wallet Canton Application API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"license": "Apache-2.0",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"bip32-path": "^0.4.2",
|
|
29
|
-
"@ledgerhq/
|
|
30
|
-
"@ledgerhq/
|
|
29
|
+
"@ledgerhq/errors": "^6.25.0",
|
|
30
|
+
"@ledgerhq/hw-transport": "^6.31.10"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/jest": "^29.5.10",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"source-map-support": "^0.5.21",
|
|
39
39
|
"ts-jest": "^29.1.1",
|
|
40
40
|
"ts-node": "^10.4.0",
|
|
41
|
-
"@ledgerhq/hw-transport-mocker": "^6.29.10
|
|
42
|
-
"@ledgerhq/hw-transport-node-speculos-http": "^6.
|
|
41
|
+
"@ledgerhq/hw-transport-mocker": "^6.29.10",
|
|
42
|
+
"@ledgerhq/hw-transport-node-speculos-http": "^6.29.10"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"clean": "rimraf lib lib-es",
|
package/src/Canton.test.ts
CHANGED
|
@@ -4,9 +4,13 @@ import Canton from "./Canton";
|
|
|
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
|
-
<=
|
|
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
|
-
|
|
30
|
-
expect(result
|
|
31
|
-
|
|
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
|
-
<=
|
|
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
|
-
|
|
46
|
-
expect(result
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
<=
|
|
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();
|
|
@@ -83,54 +101,60 @@ describe("Canton", () => {
|
|
|
83
101
|
// should request blind signature when required
|
|
84
102
|
|
|
85
103
|
it("should sign transaction hash", async () => {
|
|
104
|
+
// GIVEN
|
|
86
105
|
const transport = await openTransportReplayer(
|
|
87
106
|
RecordStore.fromString(`
|
|
88
107
|
=> e006000315058000002c80001a6f800000008000000080000000
|
|
89
108
|
<= 9000
|
|
90
|
-
=>
|
|
109
|
+
=> e006000420d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd
|
|
91
110
|
<= 40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00009000
|
|
92
111
|
`),
|
|
93
112
|
);
|
|
94
|
-
|
|
95
113
|
const canton = new Canton(transport);
|
|
96
114
|
const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
|
|
97
115
|
|
|
116
|
+
// WHEN
|
|
98
117
|
const result = await canton.signTransaction("44'/6767'/0'/0'/0'", txHash);
|
|
99
118
|
|
|
119
|
+
// THEN
|
|
100
120
|
expect(result).toEqual(
|
|
101
|
-
"
|
|
121
|
+
"a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a00",
|
|
102
122
|
);
|
|
103
123
|
});
|
|
104
124
|
|
|
105
125
|
it("should handle user refused transaction", async () => {
|
|
126
|
+
// GIVEN
|
|
106
127
|
const transport = await openTransportReplayer(
|
|
107
128
|
RecordStore.fromString(`
|
|
108
129
|
=> e006010015058000002c80001a6f800000008000000080000000
|
|
109
130
|
<= 6985
|
|
110
131
|
`),
|
|
111
132
|
);
|
|
112
|
-
|
|
113
133
|
const canton = new Canton(transport);
|
|
114
134
|
|
|
135
|
+
// WHEN & THEN
|
|
115
136
|
return expect(canton.signTransaction("44'/6767'/0'/0'/0'", "test")).rejects.toThrow();
|
|
116
137
|
});
|
|
117
138
|
});
|
|
118
139
|
|
|
119
140
|
describe("getAppConfiguration", () => {
|
|
120
141
|
it("should get app configuration", async () => {
|
|
142
|
+
// GIVEN
|
|
121
143
|
const transport = await openTransportReplayer(
|
|
122
144
|
RecordStore.fromString(`
|
|
123
145
|
=> e003000000
|
|
124
146
|
<= 0202029000
|
|
125
147
|
`),
|
|
126
148
|
);
|
|
127
|
-
|
|
128
149
|
const canton = new Canton(transport);
|
|
150
|
+
|
|
151
|
+
// WHEN
|
|
129
152
|
const result = await canton.getAppConfiguration();
|
|
130
153
|
|
|
131
|
-
|
|
132
|
-
expect(result).
|
|
133
|
-
|
|
154
|
+
// THEN
|
|
155
|
+
expect(result).toEqual({
|
|
156
|
+
version: "2.2.2",
|
|
157
|
+
});
|
|
134
158
|
});
|
|
135
159
|
|
|
136
160
|
// should handle configuration error
|
package/src/Canton.ts
CHANGED
|
@@ -13,6 +13,8 @@ const P2_NONE = 0x00;
|
|
|
13
13
|
const P2_FIRST = 0x01;
|
|
14
14
|
// P2 indicating that this is not the last APDU in a large request.
|
|
15
15
|
const P2_MORE = 0x02;
|
|
16
|
+
// P2 indicating that this is the last APDU of a message in a multi message request.
|
|
17
|
+
const P2_MSG_END = 0x04;
|
|
16
18
|
|
|
17
19
|
const INS = {
|
|
18
20
|
GET_VERSION: 0x03,
|
|
@@ -26,6 +28,9 @@ const STATUS = {
|
|
|
26
28
|
USER_CANCEL: 0x6985,
|
|
27
29
|
};
|
|
28
30
|
|
|
31
|
+
const ED25519_SIGNATURE_HEX_LENGTH = 128; // hex characters (64 bytes)
|
|
32
|
+
const CANTON_SIGNATURE_HEX_LENGTH = 132; // hex characters (66 bytes with framing)
|
|
33
|
+
|
|
29
34
|
export type AppConfig = {
|
|
30
35
|
version: string;
|
|
31
36
|
};
|
|
@@ -61,7 +66,8 @@ export default class Canton {
|
|
|
61
66
|
* @return the address and public key
|
|
62
67
|
*/
|
|
63
68
|
async getAddress(path: string, display: boolean = false): Promise<CantonAddress> {
|
|
64
|
-
|
|
69
|
+
// TODO: replace with the actual path fix wrong path "44'/6767'/0'" being used
|
|
70
|
+
const bipPath = BIPPath.fromString("m/44'/6767'/0'/0'/0'").toPathArray();
|
|
65
71
|
const serializedPath = this.serializePath(bipPath);
|
|
66
72
|
|
|
67
73
|
const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
|
|
@@ -89,7 +95,8 @@ export default class Canton {
|
|
|
89
95
|
*/
|
|
90
96
|
async signTransaction(path: string, txHash: string): Promise<CantonSignature> {
|
|
91
97
|
// 1. Send the derivation path
|
|
92
|
-
|
|
98
|
+
// TODO: replace with the actual path fix wrong path "44'/6767'/0'" being used
|
|
99
|
+
const bipPath = BIPPath.fromString("m/44'/6767'/0'/0'/0'").toPathArray();
|
|
93
100
|
const serializedPath = this.serializePath(bipPath);
|
|
94
101
|
|
|
95
102
|
const pathResponse = await this.transport.send(
|
|
@@ -107,13 +114,14 @@ export default class Canton {
|
|
|
107
114
|
CLA,
|
|
108
115
|
INS.SIGN,
|
|
109
116
|
P1_NON_CONFIRM,
|
|
110
|
-
|
|
117
|
+
P2_MSG_END,
|
|
111
118
|
Buffer.from(txHash, "hex"),
|
|
112
119
|
);
|
|
113
120
|
|
|
114
121
|
const responseData = this.handleTransportResponse(response, "transaction");
|
|
115
|
-
const
|
|
116
|
-
|
|
122
|
+
const rawSignature = responseData.toString("hex");
|
|
123
|
+
|
|
124
|
+
return this.cleanSignatureFormat(rawSignature);
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
/**
|
|
@@ -137,6 +145,25 @@ export default class Canton {
|
|
|
137
145
|
};
|
|
138
146
|
}
|
|
139
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Converts 65-byte Canton format to 64-byte Ed25519:
|
|
150
|
+
* [40][64_bytes_signature][00] (132 hex chars)
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
private cleanSignatureFormat(signature: string): CantonSignature {
|
|
154
|
+
if (signature.length === ED25519_SIGNATURE_HEX_LENGTH) {
|
|
155
|
+
return signature;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (signature.length === CANTON_SIGNATURE_HEX_LENGTH) {
|
|
159
|
+
const cleanedSignature = signature.slice(2, -2);
|
|
160
|
+
return cleanedSignature;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.warn(`[Canton]: Unknown signature format (${signature.length} chars)`);
|
|
164
|
+
return signature;
|
|
165
|
+
}
|
|
166
|
+
|
|
140
167
|
/**
|
|
141
168
|
* Helper method to handle transport response and check for errors
|
|
142
169
|
* @private
|
|
@@ -196,14 +223,26 @@ export default class Canton {
|
|
|
196
223
|
* @private
|
|
197
224
|
*/
|
|
198
225
|
private extractPubkeyAndChainCode(data: Buffer): { pubKey: string; chainCode: string } {
|
|
199
|
-
|
|
200
|
-
|
|
226
|
+
// Parse the response according to the Python unpack_get_addr_response format:
|
|
227
|
+
// response = pubkey_len (1) + pubkey (var) + chaincode_len (1) + chaincode (var)
|
|
228
|
+
|
|
229
|
+
let offset = 0;
|
|
230
|
+
|
|
231
|
+
// Extract public key length (1 byte)
|
|
232
|
+
const pubkeySize = data.readUInt8(offset);
|
|
233
|
+
offset += 1;
|
|
234
|
+
|
|
235
|
+
// Extract public key
|
|
236
|
+
const pubKey = data.subarray(offset, offset + pubkeySize);
|
|
237
|
+
offset += pubkeySize;
|
|
238
|
+
|
|
239
|
+
// Extract chain code length (1 byte)
|
|
240
|
+
const chainCodeSize = data.readUInt8(offset);
|
|
241
|
+
offset += 1;
|
|
242
|
+
|
|
243
|
+
// Extract chain code
|
|
244
|
+
const chainCode = data.subarray(offset, offset + chainCodeSize);
|
|
201
245
|
|
|
202
|
-
const chainCodeSize = parseInt(
|
|
203
|
-
data.subarray(pubkeySize + 1, pubkeySize + 2).toString("hex"),
|
|
204
|
-
16,
|
|
205
|
-
);
|
|
206
|
-
const chainCode = data.subarray(pubkeySize + 2, pubkeySize + chainCodeSize + 2);
|
|
207
246
|
return { pubKey: pubKey.toString("hex"), chainCode: chainCode.toString("hex") };
|
|
208
247
|
}
|
|
209
248
|
|