@aztec/node-keystore 2.0.3 → 2.1.0-rc.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/dest/keystore_manager.d.ts +5 -0
- package/dest/keystore_manager.d.ts.map +1 -1
- package/dest/keystore_manager.js +64 -0
- package/dest/signer.d.ts +8 -0
- package/dest/signer.d.ts.map +1 -1
- package/dest/signer.js +51 -0
- package/package.json +4 -4
- package/src/keystore_manager.ts +76 -0
- package/src/signer.ts +76 -0
|
@@ -28,6 +28,11 @@ export declare class KeystoreManager {
|
|
|
28
28
|
* @param keystore Parsed keystore configuration
|
|
29
29
|
*/
|
|
30
30
|
constructor(keystore: KeyStore);
|
|
31
|
+
/**
|
|
32
|
+
* Validates all remote signers in the keystore are accessible and have the required addresses.
|
|
33
|
+
* Should be called after construction if validation is needed.
|
|
34
|
+
*/
|
|
35
|
+
validateSigners(): Promise<void>;
|
|
31
36
|
/**
|
|
32
37
|
* Validates that attester addresses are unique across all validators
|
|
33
38
|
* Only checks simple private key attesters, not JSON-V3 or mnemonic attesters,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keystore_manager.d.ts","sourceRoot":"","sources":["../src/keystore_manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAKhD,OAAO,KAAK,EAEV,WAAW,EAIX,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,iBAAiB,IAAI,uBAAuB,EAC7C,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGpB,KAAK,CAAC,EAAE,KAAK;gBAD7B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC;;;;OAIG;gBACS,QAAQ,EAAE,QAAQ;IAK9B;;;;;OAKG;IACH,OAAO,CAAC,+BAA+B;IAkBvC;;;OAGG;IACH,OAAO,CAAC,0CAA0C;IAoDlD;;OAEG;IACH,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE;IAK1D;;OAEG;IACH,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE;IAc3D,kCAAkC,IAAI,SAAS,EAAE;IAWjD;;OAEG;IACH,oBAAoB,IAAI,SAAS,EAAE;IAQnC;;OAEG;IACH,mBAAmB,IAAI;QAAE,EAAE,EAAE,UAAU,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,SAAS;IAkCvF;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAOpD;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,UAAU;IAgBtD;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY;IAKrD;;;OAGG;IACH,kBAAkB,IAAI,WAAW,GAAG,SAAS;IAI7C;;;OAGG;IACH,eAAe,IAAI,cAAc,GAAG,SAAS;IAI7C;;;OAGG;IACH,uCAAuC,IAAI,IAAI;IAqB/C;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA+BpC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkD9B;;OAEG;IACH,OAAO,CAAC,gCAAgC;IAwBxC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA8BjC;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAI3E;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAI1F;;;OAGG;IACH,8BAA8B,CAC5B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,UAAU,GAC1B,qBAAqB,GAAG,SAAS;CAkHrC"}
|
|
1
|
+
{"version":3,"file":"keystore_manager.d.ts","sourceRoot":"","sources":["../src/keystore_manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAKhD,OAAO,KAAK,EAEV,WAAW,EAIX,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,iBAAiB,IAAI,uBAAuB,EAC7C,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGpB,KAAK,CAAC,EAAE,KAAK;gBAD7B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC;;;;OAIG;gBACS,QAAQ,EAAE,QAAQ;IAK9B;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAwEtC;;;;;OAKG;IACH,OAAO,CAAC,+BAA+B;IAkBvC;;;OAGG;IACH,OAAO,CAAC,0CAA0C;IAoDlD;;OAEG;IACH,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE;IAK1D;;OAEG;IACH,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE;IAc3D,kCAAkC,IAAI,SAAS,EAAE;IAWjD;;OAEG;IACH,oBAAoB,IAAI,SAAS,EAAE;IAQnC;;OAEG;IACH,mBAAmB,IAAI;QAAE,EAAE,EAAE,UAAU,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,SAAS;IAkCvF;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAOpD;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,UAAU;IAgBtD;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY;IAKrD;;;OAGG;IACH,kBAAkB,IAAI,WAAW,GAAG,SAAS;IAI7C;;;OAGG;IACH,eAAe,IAAI,cAAc,GAAG,SAAS;IAI7C;;;OAGG;IACH,uCAAuC,IAAI,IAAI;IAqB/C;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA+BpC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkD9B;;OAEG;IACH,OAAO,CAAC,gCAAgC;IAwBxC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA8BjC;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAI3E;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAI1F;;;OAGG;IACH,8BAA8B,CAC5B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,UAAU,GAC1B,qBAAqB,GAAG,SAAS;CAkHrC"}
|
package/dest/keystore_manager.js
CHANGED
|
@@ -31,6 +31,70 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
31
31
|
this.validateUniqueAttesterAddresses();
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
+
* Validates all remote signers in the keystore are accessible and have the required addresses.
|
|
35
|
+
* Should be called after construction if validation is needed.
|
|
36
|
+
*/ async validateSigners() {
|
|
37
|
+
// Collect all remote signers with their addresses grouped by URL
|
|
38
|
+
const remoteSignersByUrl = new Map();
|
|
39
|
+
// Helper to extract remote signer URL from config
|
|
40
|
+
const getUrl = (config)=>{
|
|
41
|
+
return typeof config === 'string' ? config : config.remoteSignerUrl;
|
|
42
|
+
};
|
|
43
|
+
// Helper to collect remote signers from accounts
|
|
44
|
+
const collectRemoteSigners = (accounts, defaultRemoteSigner)=>{
|
|
45
|
+
const processAccount = (account)=>{
|
|
46
|
+
if (typeof account === 'object' && !('path' in account) && !('mnemonic' in account)) {
|
|
47
|
+
// This is a remote signer account
|
|
48
|
+
const remoteSigner = account;
|
|
49
|
+
const address = 'address' in remoteSigner ? remoteSigner.address : remoteSigner;
|
|
50
|
+
let url;
|
|
51
|
+
if ('remoteSignerUrl' in remoteSigner && remoteSigner.remoteSignerUrl) {
|
|
52
|
+
url = remoteSigner.remoteSignerUrl;
|
|
53
|
+
} else if (defaultRemoteSigner) {
|
|
54
|
+
url = getUrl(defaultRemoteSigner);
|
|
55
|
+
} else {
|
|
56
|
+
return; // No remote signer URL available
|
|
57
|
+
}
|
|
58
|
+
if (!remoteSignersByUrl.has(url)) {
|
|
59
|
+
remoteSignersByUrl.set(url, new Set());
|
|
60
|
+
}
|
|
61
|
+
remoteSignersByUrl.get(url).add(address.toString());
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
if (Array.isArray(accounts)) {
|
|
65
|
+
accounts.forEach((account)=>collectRemoteSigners(account, defaultRemoteSigner));
|
|
66
|
+
} else if (typeof accounts === 'object' && 'mnemonic' in accounts) {
|
|
67
|
+
// Skip mnemonic configs
|
|
68
|
+
} else {
|
|
69
|
+
processAccount(accounts);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
// Collect from validators
|
|
73
|
+
const validatorCount = this.getValidatorCount();
|
|
74
|
+
for(let i = 0; i < validatorCount; i++){
|
|
75
|
+
const validator = this.getValidator(i);
|
|
76
|
+
const remoteSigner = validator.remoteSigner || this.keystore.remoteSigner;
|
|
77
|
+
collectRemoteSigners(validator.attester, remoteSigner);
|
|
78
|
+
if (validator.publisher) {
|
|
79
|
+
collectRemoteSigners(validator.publisher, remoteSigner);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Collect from slasher
|
|
83
|
+
if (this.keystore.slasher) {
|
|
84
|
+
collectRemoteSigners(this.keystore.slasher, this.keystore.remoteSigner);
|
|
85
|
+
}
|
|
86
|
+
// Collect from prover
|
|
87
|
+
if (this.keystore.prover && typeof this.keystore.prover === 'object' && 'publisher' in this.keystore.prover) {
|
|
88
|
+
collectRemoteSigners(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
89
|
+
}
|
|
90
|
+
// Validate each remote signer URL with all its addresses
|
|
91
|
+
for (const [url, addresses] of remoteSignersByUrl.entries()){
|
|
92
|
+
if (addresses.size > 0) {
|
|
93
|
+
await RemoteSigner.validateAccess(url, Array.from(addresses));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
34
98
|
* Validates that attester addresses are unique across all validators
|
|
35
99
|
* Only checks simple private key attesters, not JSON-V3 or mnemonic attesters,
|
|
36
100
|
* these are validated when decrypting the JSON-V3 keystore files
|
package/dest/signer.d.ts
CHANGED
|
@@ -39,6 +39,14 @@ export declare class RemoteSigner implements EthSigner {
|
|
|
39
39
|
private readonly config;
|
|
40
40
|
private fetch;
|
|
41
41
|
constructor(address: EthAddress, config: EthRemoteSignerConfig, fetch?: typeof globalThis.fetch);
|
|
42
|
+
/**
|
|
43
|
+
* Validates that a web3signer is accessible and that the given addresses are available.
|
|
44
|
+
* @param remoteSignerUrl - The URL of the web3signer (can be string or EthRemoteSignerConfig)
|
|
45
|
+
* @param addresses - The addresses to check for availability
|
|
46
|
+
* @param fetch - Optional fetch implementation for testing
|
|
47
|
+
* @throws Error if the web3signer is not accessible or if any address is not available
|
|
48
|
+
*/
|
|
49
|
+
static validateAccess(remoteSignerUrl: EthRemoteSignerConfig, addresses: string[], fetch?: typeof globalThis.fetch): Promise<void>;
|
|
42
50
|
/**
|
|
43
51
|
* Sign a message using eth_sign via remote JSON-RPC.
|
|
44
52
|
*/
|
package/dest/signer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAiC,MAAM,iCAAiC,CAAC;AAI3F,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAKzB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IAG3B,MAAM,EAAE,MAAM;IACd,GAAG,EAAE,MAAM;IACX,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;gBAJzB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA;CAK5B;AAED;;GAEG;AACH,qBAAa,WAAY,YAAW,SAAS;IAG/B,OAAO,CAAC,UAAU;IAF9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAErB,UAAU,EAAE,QAAQ;IAIxC,IAAI,OAAO,IAAI,UAAU,CAExB;IAED,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIlD,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAKjE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;CAc1E;AAmBD;;GAEG;AACH,qBAAa,YAAa,YAAW,SAAS;aAE1B,OAAO,EAAE,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,KAAK;gBAFG,OAAO,EAAE,UAAU,EAClB,MAAM,EAAE,qBAAqB,EACtC,KAAK,GAAE,OAAO,UAAU,CAAC,KAAwB;IAG3D;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIxD;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIvE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIzE;;OAEG;IACH;;OAEG;YACW,sBAAsB;IAcpC;;OAEG;YACW,+BAA+B;IAkB7C;;OAEG;YACW,iCAAiC;IA+C/C;;OAEG;YACW,kBAAkB;IAyChC;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAiC,MAAM,iCAAiC,CAAC;AAI3F,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAKzB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IAG3B,MAAM,EAAE,MAAM;IACd,GAAG,EAAE,MAAM;IACX,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;gBAJzB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA;CAK5B;AAED;;GAEG;AACH,qBAAa,WAAY,YAAW,SAAS;IAG/B,OAAO,CAAC,UAAU;IAF9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAErB,UAAU,EAAE,QAAQ;IAIxC,IAAI,OAAO,IAAI,UAAU,CAExB;IAED,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIlD,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAKjE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;CAc1E;AAmBD;;GAEG;AACH,qBAAa,YAAa,YAAW,SAAS;aAE1B,OAAO,EAAE,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,KAAK;gBAFG,OAAO,EAAE,UAAU,EAClB,MAAM,EAAE,qBAAqB,EACtC,KAAK,GAAE,OAAO,UAAU,CAAC,KAAwB;IAG3D;;;;;;OAMG;WACU,cAAc,CACzB,eAAe,EAAE,qBAAqB,EACtC,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,GAAE,OAAO,UAAU,CAAC,KAAwB,GAChD,OAAO,CAAC,IAAI,CAAC;IAiEhB;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIxD;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIvE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIzE;;OAEG;IACH;;OAEG;YACW,sBAAsB;IAcpC;;OAEG;YACW,+BAA+B;IAkB7C;;OAEG;YACW,iCAAiC;IA+C/C;;OAEG;YACW,kBAAkB;IAyChC;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
|
package/dest/signer.js
CHANGED
|
@@ -63,6 +63,57 @@ import { hashTypedData, keccak256, parseTransaction, serializeTransaction } from
|
|
|
63
63
|
this.fetch = fetch;
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
+
* Validates that a web3signer is accessible and that the given addresses are available.
|
|
67
|
+
* @param remoteSignerUrl - The URL of the web3signer (can be string or EthRemoteSignerConfig)
|
|
68
|
+
* @param addresses - The addresses to check for availability
|
|
69
|
+
* @param fetch - Optional fetch implementation for testing
|
|
70
|
+
* @throws Error if the web3signer is not accessible or if any address is not available
|
|
71
|
+
*/ static async validateAccess(remoteSignerUrl, addresses, fetch = globalThis.fetch) {
|
|
72
|
+
const url = typeof remoteSignerUrl === 'string' ? remoteSignerUrl : remoteSignerUrl.remoteSignerUrl;
|
|
73
|
+
try {
|
|
74
|
+
// Check if the web3signer is reachable by calling eth_accounts
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
'Content-Type': 'application/json'
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
jsonrpc: '2.0',
|
|
82
|
+
method: 'eth_accounts',
|
|
83
|
+
params: [],
|
|
84
|
+
id: 1
|
|
85
|
+
})
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
const errorText = await response.text();
|
|
89
|
+
throw new SignerError(`Web3Signer validation failed: ${response.status} ${response.statusText} - ${errorText}`, 'eth_accounts', url, response.status);
|
|
90
|
+
}
|
|
91
|
+
const result = await response.json();
|
|
92
|
+
if (result.error) {
|
|
93
|
+
throw new SignerError(`Web3Signer JSON-RPC error during validation: ${result.error.code} - ${result.error.message}`, 'eth_accounts', url, 200, result.error.code);
|
|
94
|
+
}
|
|
95
|
+
if (!result.result || !Array.isArray(result.result)) {
|
|
96
|
+
throw new Error('Invalid response from Web3Signer: expected array of accounts');
|
|
97
|
+
}
|
|
98
|
+
// Normalize addresses to lowercase for comparison
|
|
99
|
+
const availableAccounts = result.result.map((addr)=>addr.toLowerCase());
|
|
100
|
+
const requestedAddresses = addresses.map((addr)=>addr.toLowerCase());
|
|
101
|
+
// Check if all requested addresses are available
|
|
102
|
+
const missingAddresses = requestedAddresses.filter((addr)=>!availableAccounts.includes(addr));
|
|
103
|
+
if (missingAddresses.length > 0) {
|
|
104
|
+
throw new Error(`The following addresses are not available in the web3signer: ${missingAddresses.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof SignerError) {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
if (error.code === 'ECONNREFUSED' || error.cause?.code === 'ECONNREFUSED') {
|
|
111
|
+
throw new Error(`Unable to connect to web3signer at ${url}. Please ensure it is running and accessible.`);
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
66
117
|
* Sign a message using eth_sign via remote JSON-RPC.
|
|
67
118
|
*/ async signMessage(message) {
|
|
68
119
|
return await this.makeJsonRpcSignRequest(message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/node-keystore",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
]
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@aztec/ethereum": "2.0.
|
|
66
|
-
"@aztec/foundation": "2.0.
|
|
67
|
-
"@aztec/stdlib": "2.0.
|
|
65
|
+
"@aztec/ethereum": "2.1.0-rc.1",
|
|
66
|
+
"@aztec/foundation": "2.1.0-rc.1",
|
|
67
|
+
"@aztec/stdlib": "2.1.0-rc.1",
|
|
68
68
|
"@ethersproject/wallet": "^5.7.0",
|
|
69
69
|
"tslib": "^2.4.0",
|
|
70
70
|
"viem": "2.23.7",
|
package/src/keystore_manager.ts
CHANGED
|
@@ -58,6 +58,82 @@ export class KeystoreManager {
|
|
|
58
58
|
this.validateUniqueAttesterAddresses();
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Validates all remote signers in the keystore are accessible and have the required addresses.
|
|
63
|
+
* Should be called after construction if validation is needed.
|
|
64
|
+
*/
|
|
65
|
+
async validateSigners(): Promise<void> {
|
|
66
|
+
// Collect all remote signers with their addresses grouped by URL
|
|
67
|
+
const remoteSignersByUrl = new Map<string, Set<string>>();
|
|
68
|
+
|
|
69
|
+
// Helper to extract remote signer URL from config
|
|
70
|
+
const getUrl = (config: EthRemoteSignerConfig): string => {
|
|
71
|
+
return typeof config === 'string' ? config : config.remoteSignerUrl;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Helper to collect remote signers from accounts
|
|
75
|
+
const collectRemoteSigners = (accounts: EthAccounts, defaultRemoteSigner?: EthRemoteSignerConfig): void => {
|
|
76
|
+
const processAccount = (account: EthAccount): void => {
|
|
77
|
+
if (typeof account === 'object' && !('path' in account) && !('mnemonic' in (account as any))) {
|
|
78
|
+
// This is a remote signer account
|
|
79
|
+
const remoteSigner = account as EthRemoteSignerAccount;
|
|
80
|
+
const address = 'address' in remoteSigner ? remoteSigner.address : remoteSigner;
|
|
81
|
+
|
|
82
|
+
let url: string;
|
|
83
|
+
if ('remoteSignerUrl' in remoteSigner && remoteSigner.remoteSignerUrl) {
|
|
84
|
+
url = remoteSigner.remoteSignerUrl;
|
|
85
|
+
} else if (defaultRemoteSigner) {
|
|
86
|
+
url = getUrl(defaultRemoteSigner);
|
|
87
|
+
} else {
|
|
88
|
+
return; // No remote signer URL available
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!remoteSignersByUrl.has(url)) {
|
|
92
|
+
remoteSignersByUrl.set(url, new Set());
|
|
93
|
+
}
|
|
94
|
+
remoteSignersByUrl.get(url)!.add(address.toString());
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (Array.isArray(accounts)) {
|
|
99
|
+
accounts.forEach(account => collectRemoteSigners(account, defaultRemoteSigner));
|
|
100
|
+
} else if (typeof accounts === 'object' && 'mnemonic' in accounts) {
|
|
101
|
+
// Skip mnemonic configs
|
|
102
|
+
} else {
|
|
103
|
+
processAccount(accounts as EthAccount);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Collect from validators
|
|
108
|
+
const validatorCount = this.getValidatorCount();
|
|
109
|
+
for (let i = 0; i < validatorCount; i++) {
|
|
110
|
+
const validator = this.getValidator(i);
|
|
111
|
+
const remoteSigner = validator.remoteSigner || this.keystore.remoteSigner;
|
|
112
|
+
|
|
113
|
+
collectRemoteSigners(validator.attester, remoteSigner);
|
|
114
|
+
if (validator.publisher) {
|
|
115
|
+
collectRemoteSigners(validator.publisher, remoteSigner);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Collect from slasher
|
|
120
|
+
if (this.keystore.slasher) {
|
|
121
|
+
collectRemoteSigners(this.keystore.slasher, this.keystore.remoteSigner);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Collect from prover
|
|
125
|
+
if (this.keystore.prover && typeof this.keystore.prover === 'object' && 'publisher' in this.keystore.prover) {
|
|
126
|
+
collectRemoteSigners(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Validate each remote signer URL with all its addresses
|
|
130
|
+
for (const [url, addresses] of remoteSignersByUrl.entries()) {
|
|
131
|
+
if (addresses.size > 0) {
|
|
132
|
+
await RemoteSigner.validateAccess(url, Array.from(addresses));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
61
137
|
/**
|
|
62
138
|
* Validates that attester addresses are unique across all validators
|
|
63
139
|
* Only checks simple private key attesters, not JSON-V3 or mnemonic attesters,
|
package/src/signer.ts
CHANGED
|
@@ -104,6 +104,82 @@ export class RemoteSigner implements EthSigner {
|
|
|
104
104
|
private fetch: typeof globalThis.fetch = globalThis.fetch,
|
|
105
105
|
) {}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Validates that a web3signer is accessible and that the given addresses are available.
|
|
109
|
+
* @param remoteSignerUrl - The URL of the web3signer (can be string or EthRemoteSignerConfig)
|
|
110
|
+
* @param addresses - The addresses to check for availability
|
|
111
|
+
* @param fetch - Optional fetch implementation for testing
|
|
112
|
+
* @throws Error if the web3signer is not accessible or if any address is not available
|
|
113
|
+
*/
|
|
114
|
+
static async validateAccess(
|
|
115
|
+
remoteSignerUrl: EthRemoteSignerConfig,
|
|
116
|
+
addresses: string[],
|
|
117
|
+
fetch: typeof globalThis.fetch = globalThis.fetch,
|
|
118
|
+
): Promise<void> {
|
|
119
|
+
const url = typeof remoteSignerUrl === 'string' ? remoteSignerUrl : remoteSignerUrl.remoteSignerUrl;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// Check if the web3signer is reachable by calling eth_accounts
|
|
123
|
+
const response = await fetch(url, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: {
|
|
126
|
+
'Content-Type': 'application/json',
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
jsonrpc: '2.0',
|
|
130
|
+
method: 'eth_accounts',
|
|
131
|
+
params: [],
|
|
132
|
+
id: 1,
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
const errorText = await response.text();
|
|
138
|
+
throw new SignerError(
|
|
139
|
+
`Web3Signer validation failed: ${response.status} ${response.statusText} - ${errorText}`,
|
|
140
|
+
'eth_accounts',
|
|
141
|
+
url,
|
|
142
|
+
response.status,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const result = await response.json();
|
|
147
|
+
|
|
148
|
+
if (result.error) {
|
|
149
|
+
throw new SignerError(
|
|
150
|
+
`Web3Signer JSON-RPC error during validation: ${result.error.code} - ${result.error.message}`,
|
|
151
|
+
'eth_accounts',
|
|
152
|
+
url,
|
|
153
|
+
200,
|
|
154
|
+
result.error.code,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!result.result || !Array.isArray(result.result)) {
|
|
159
|
+
throw new Error('Invalid response from Web3Signer: expected array of accounts');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Normalize addresses to lowercase for comparison
|
|
163
|
+
const availableAccounts: string[] = result.result.map((addr: string) => addr.toLowerCase());
|
|
164
|
+
const requestedAddresses = addresses.map(addr => addr.toLowerCase());
|
|
165
|
+
|
|
166
|
+
// Check if all requested addresses are available
|
|
167
|
+
const missingAddresses = requestedAddresses.filter(addr => !availableAccounts.includes(addr));
|
|
168
|
+
|
|
169
|
+
if (missingAddresses.length > 0) {
|
|
170
|
+
throw new Error(`The following addresses are not available in the web3signer: ${missingAddresses.join(', ')}`);
|
|
171
|
+
}
|
|
172
|
+
} catch (error: any) {
|
|
173
|
+
if (error instanceof SignerError) {
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
if (error.code === 'ECONNREFUSED' || error.cause?.code === 'ECONNREFUSED') {
|
|
177
|
+
throw new Error(`Unable to connect to web3signer at ${url}. Please ensure it is running and accessible.`);
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
107
183
|
/**
|
|
108
184
|
* Sign a message using eth_sign via remote JSON-RPC.
|
|
109
185
|
*/
|