@aztec/validator-client 0.0.0-test.1 → 0.0.1-commit.b655e406

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.
Files changed (57) hide show
  1. package/dest/block_proposal_handler.d.ts +52 -0
  2. package/dest/block_proposal_handler.d.ts.map +1 -0
  3. package/dest/block_proposal_handler.js +286 -0
  4. package/dest/config.d.ts +2 -13
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +31 -7
  7. package/dest/duties/validation_service.d.ts +16 -8
  8. package/dest/duties/validation_service.d.ts.map +1 -1
  9. package/dest/duties/validation_service.js +35 -11
  10. package/dest/factory.d.ts +21 -4
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +13 -6
  13. package/dest/index.d.ts +3 -1
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +3 -1
  16. package/dest/key_store/index.d.ts +2 -0
  17. package/dest/key_store/index.d.ts.map +1 -1
  18. package/dest/key_store/index.js +2 -0
  19. package/dest/key_store/interface.d.ts +54 -5
  20. package/dest/key_store/interface.d.ts.map +1 -1
  21. package/dest/key_store/interface.js +3 -3
  22. package/dest/key_store/local_key_store.d.ts +40 -10
  23. package/dest/key_store/local_key_store.d.ts.map +1 -1
  24. package/dest/key_store/local_key_store.js +63 -16
  25. package/dest/key_store/node_keystore_adapter.d.ts +138 -0
  26. package/dest/key_store/node_keystore_adapter.d.ts.map +1 -0
  27. package/dest/key_store/node_keystore_adapter.js +316 -0
  28. package/dest/key_store/web3signer_key_store.d.ts +67 -0
  29. package/dest/key_store/web3signer_key_store.d.ts.map +1 -0
  30. package/dest/key_store/web3signer_key_store.js +152 -0
  31. package/dest/metrics.d.ts +11 -4
  32. package/dest/metrics.d.ts.map +1 -1
  33. package/dest/metrics.js +52 -15
  34. package/dest/validator.d.ts +48 -61
  35. package/dest/validator.d.ts.map +1 -1
  36. package/dest/validator.js +262 -165
  37. package/package.json +25 -19
  38. package/src/block_proposal_handler.ts +343 -0
  39. package/src/config.ts +42 -22
  40. package/src/duties/validation_service.ts +69 -14
  41. package/src/factory.ts +56 -11
  42. package/src/index.ts +3 -1
  43. package/src/key_store/index.ts +2 -0
  44. package/src/key_store/interface.ts +61 -5
  45. package/src/key_store/local_key_store.ts +67 -17
  46. package/src/key_store/node_keystore_adapter.ts +375 -0
  47. package/src/key_store/web3signer_key_store.ts +192 -0
  48. package/src/metrics.ts +68 -17
  49. package/src/validator.ts +381 -233
  50. package/dest/errors/index.d.ts +0 -2
  51. package/dest/errors/index.d.ts.map +0 -1
  52. package/dest/errors/index.js +0 -1
  53. package/dest/errors/validator.error.d.ts +0 -29
  54. package/dest/errors/validator.error.d.ts.map +0 -1
  55. package/dest/errors/validator.error.js +0 -45
  56. package/src/errors/index.ts +0 -1
  57. package/src/errors/validator.error.ts +0 -55
@@ -0,0 +1,192 @@
1
+ import type { Buffer32 } from '@aztec/foundation/buffer';
2
+ import { normalizeSignature } from '@aztec/foundation/crypto';
3
+ import { EthAddress } from '@aztec/foundation/eth-address';
4
+ import { Signature } from '@aztec/foundation/eth-signature';
5
+
6
+ import type { TypedDataDefinition } from 'viem';
7
+
8
+ import type { ValidatorKeyStore } from './interface.js';
9
+
10
+ /**
11
+ * Web3Signer Key Store
12
+ *
13
+ * An implementation of the Key store using Web3Signer remote signing service.
14
+ * This implementation uses the Web3Signer JSON-RPC API for secp256k1 signatures.
15
+ */
16
+ export class Web3SignerKeyStore implements ValidatorKeyStore {
17
+ constructor(
18
+ private addresses: EthAddress[],
19
+ private baseUrl: string,
20
+ ) {}
21
+
22
+ /**
23
+ * Get the address of a signer by index
24
+ *
25
+ * @param index - The index of the signer
26
+ * @returns the address
27
+ */
28
+ public getAddress(index: number): EthAddress {
29
+ if (index >= this.addresses.length) {
30
+ throw new Error(`Index ${index} is out of bounds.`);
31
+ }
32
+ return this.addresses[index];
33
+ }
34
+
35
+ /**
36
+ * Get all addresses
37
+ *
38
+ * @returns all addresses
39
+ */
40
+ public getAddresses(): EthAddress[] {
41
+ return this.addresses;
42
+ }
43
+
44
+ /**
45
+ * Sign EIP-712 typed data with all keystore addresses
46
+ * @param typedData - The complete EIP-712 typed data structure (domain, types, primaryType, message)
47
+ * @return signatures
48
+ */
49
+ public signTypedData(typedData: TypedDataDefinition): Promise<Signature[]> {
50
+ return Promise.all(this.addresses.map(address => this.makeJsonRpcSignTypedDataRequest(address, typedData)));
51
+ }
52
+
53
+ /**
54
+ * Sign EIP-712 typed data with a specific address
55
+ * @param address - The address of the signer to use
56
+ * @param typedData - The complete EIP-712 typed data structure (domain, types, primaryType, message)
57
+ * @returns signature for the specified address
58
+ * @throws Error if the address is not found in the keystore or signing fails
59
+ */
60
+ public async signTypedDataWithAddress(address: EthAddress, typedData: TypedDataDefinition): Promise<Signature> {
61
+ if (!this.addresses.some(addr => addr.equals(address))) {
62
+ throw new Error(`Address ${address.toString()} not found in keystore`);
63
+ }
64
+
65
+ return await this.makeJsonRpcSignTypedDataRequest(address, typedData);
66
+ }
67
+
68
+ /**
69
+ * Sign a message with all keystore addresses using EIP-191 prefix
70
+ *
71
+ * @param message - The message to sign
72
+ * @return signatures
73
+ */
74
+ public signMessage(message: Buffer32): Promise<Signature[]> {
75
+ return Promise.all(this.addresses.map(address => this.makeJsonRpcSignRequest(address, message)));
76
+ }
77
+
78
+ /**
79
+ * Sign a message with a specific address using EIP-191 prefix
80
+ * @param address - The address of the signer to use
81
+ * @param message - The message to sign
82
+ * @returns signature for the specified address
83
+ * @throws Error if the address is not found in the keystore or signing fails
84
+ */
85
+ public async signMessageWithAddress(address: EthAddress, message: Buffer32): Promise<Signature> {
86
+ if (!this.addresses.some(addr => addr.equals(address))) {
87
+ throw new Error(`Address ${address.toString()} not found in keystore`);
88
+ }
89
+ return await this.makeJsonRpcSignRequest(address, message);
90
+ }
91
+
92
+ /**
93
+ * Make a JSON-RPC sign request to Web3Signer using eth_sign
94
+ * @param address - The Ethereum address to sign with
95
+ * @param data - The data to sign
96
+ * @returns The signature
97
+ */
98
+ private async makeJsonRpcSignRequest(address: EthAddress, data: Buffer32): Promise<Signature> {
99
+ const url = this.baseUrl;
100
+
101
+ // Use JSON-RPC eth_sign method which automatically applies Ethereum message prefixing
102
+ const body = {
103
+ jsonrpc: '2.0',
104
+ method: 'eth_sign',
105
+ params: [
106
+ address.toString(), // Ethereum address as identifier
107
+ data.toString(), // Raw data to sign (eth_sign will apply Ethereum message prefix)
108
+ ],
109
+ id: 1,
110
+ };
111
+
112
+ const response = await fetch(url, {
113
+ method: 'POST',
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ },
117
+ body: JSON.stringify(body),
118
+ });
119
+
120
+ if (!response.ok) {
121
+ const errorText = await response.text();
122
+ throw new Error(`Web3Signer request failed: ${response.status} ${response.statusText} - ${errorText}`);
123
+ }
124
+
125
+ const result = await response.json();
126
+
127
+ // Handle JSON-RPC response format
128
+ if (result.error) {
129
+ throw new Error(`Web3Signer JSON-RPC error: ${result.error.code} - ${result.error.message}`);
130
+ }
131
+
132
+ if (!result.result) {
133
+ throw new Error('Invalid response from Web3Signer: no result found');
134
+ }
135
+
136
+ let signatureHex = result.result;
137
+
138
+ // Ensure the signature has the 0x prefix
139
+ if (!signatureHex.startsWith('0x')) {
140
+ signatureHex = '0x' + signatureHex;
141
+ }
142
+
143
+ // Parse the signature from the hex string
144
+ return normalizeSignature(Signature.fromString(signatureHex as `0x${string}`));
145
+ }
146
+
147
+ private async makeJsonRpcSignTypedDataRequest(
148
+ address: EthAddress,
149
+ typedData: TypedDataDefinition,
150
+ ): Promise<Signature> {
151
+ const url = this.baseUrl;
152
+
153
+ const body = {
154
+ jsonrpc: '2.0',
155
+ method: 'eth_signTypedData',
156
+ params: [address.toString(), JSON.stringify(typedData)],
157
+ id: 1,
158
+ };
159
+
160
+ const response = await fetch(url, {
161
+ method: 'POST',
162
+ headers: {
163
+ 'Content-Type': 'application/json',
164
+ },
165
+ body: JSON.stringify(body),
166
+ });
167
+
168
+ if (!response.ok) {
169
+ const errorText = await response.text();
170
+ throw new Error(`Web3Signer request failed: ${response.status} ${response.statusText} - ${errorText}`);
171
+ }
172
+
173
+ const result = await response.json();
174
+
175
+ if (result.error) {
176
+ throw new Error(`Web3Signer JSON-RPC error: ${result.error.code} - ${result.error.message}`);
177
+ }
178
+
179
+ if (!result.result) {
180
+ throw new Error('Invalid response from Web3Signer: no result found');
181
+ }
182
+
183
+ let signatureHex = result.result;
184
+
185
+ // Ensure the signature has the 0x prefix
186
+ if (!signatureHex.startsWith('0x')) {
187
+ signatureHex = '0x' + signatureHex;
188
+ }
189
+
190
+ return normalizeSignature(Signature.fromString(signatureHex as `0x${string}`));
191
+ }
192
+ }
package/src/metrics.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { BlockProposal } from '@aztec/stdlib/p2p';
2
2
  import {
3
3
  Attributes,
4
- type Gauge,
4
+ type Histogram,
5
5
  Metrics,
6
6
  type TelemetryClient,
7
7
  type UpDownCounter,
@@ -9,8 +9,14 @@ import {
9
9
  } from '@aztec/telemetry-client';
10
10
 
11
11
  export class ValidatorMetrics {
12
- private reExecutionTime: Gauge;
13
12
  private failedReexecutionCounter: UpDownCounter;
13
+ private successfulAttestationsCount: UpDownCounter;
14
+ private failedAttestationsBadProposalCount: UpDownCounter;
15
+ private failedAttestationsNodeIssueCount: UpDownCounter;
16
+
17
+ private reexMana: Histogram;
18
+ private reexTx: Histogram;
19
+ private reexDuration: Histogram;
14
20
 
15
21
  constructor(telemetryClient: TelemetryClient) {
16
22
  const meter = telemetryClient.getMeter('Validator');
@@ -21,30 +27,75 @@ export class ValidatorMetrics {
21
27
  valueType: ValueType.INT,
22
28
  });
23
29
 
24
- this.reExecutionTime = meter.createGauge(Metrics.VALIDATOR_RE_EXECUTION_TIME, {
25
- description: 'The time taken to re-execute a transaction',
26
- unit: 'ms',
30
+ this.successfulAttestationsCount = meter.createUpDownCounter(Metrics.VALIDATOR_ATTESTATION_SUCCESS_COUNT, {
31
+ description: 'The number of successful attestations',
32
+ valueType: ValueType.INT,
33
+ });
34
+
35
+ this.failedAttestationsBadProposalCount = meter.createUpDownCounter(
36
+ Metrics.VALIDATOR_ATTESTATION_FAILED_BAD_PROPOSAL_COUNT,
37
+ {
38
+ description: 'The number of failed attestations due to invalid block proposals',
39
+ valueType: ValueType.INT,
40
+ },
41
+ );
42
+
43
+ this.failedAttestationsNodeIssueCount = meter.createUpDownCounter(
44
+ Metrics.VALIDATOR_ATTESTATION_FAILED_NODE_ISSUE_COUNT,
45
+ {
46
+ description: 'The number of failed attestations due to node issues (timeout, missing data, etc.)',
47
+ valueType: ValueType.INT,
48
+ },
49
+ );
50
+
51
+ this.reexMana = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_MANA, {
52
+ description: 'The mana consumed by blocks',
27
53
  valueType: ValueType.DOUBLE,
54
+ unit: 'Mmana',
55
+ });
56
+
57
+ this.reexTx = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_TX_COUNT, {
58
+ description: 'The number of txs in a block proposal',
59
+ valueType: ValueType.INT,
60
+ unit: 'tx',
28
61
  });
29
- }
30
62
 
31
- public reExecutionTimer(): () => void {
32
- const start = performance.now();
33
- return () => {
34
- const end = performance.now();
35
- this.recordReExecutionTime(end - start);
36
- };
63
+ this.reexDuration = meter.createGauge(Metrics.VALIDATOR_RE_EXECUTION_TIME, {
64
+ description: 'The time taken to re-execute a transaction',
65
+ unit: 'ms',
66
+ valueType: ValueType.INT,
67
+ });
37
68
  }
38
69
 
39
- public recordReExecutionTime(time: number) {
40
- this.reExecutionTime.record(time);
70
+ public recordReex(time: number, txs: number, mManaTotal: number) {
71
+ this.reexDuration.record(Math.ceil(time));
72
+ this.reexTx.record(txs);
73
+ this.reexMana.record(mManaTotal);
41
74
  }
42
75
 
43
- public async recordFailedReexecution(proposal: BlockProposal) {
76
+ public recordFailedReexecution(proposal: BlockProposal) {
77
+ const proposer = proposal.getSender();
44
78
  this.failedReexecutionCounter.add(1, {
45
79
  [Attributes.STATUS]: 'failed',
46
- [Attributes.BLOCK_NUMBER]: proposal.payload.header.globalVariables.blockNumber.toString(),
47
- [Attributes.BLOCK_PROPOSER]: (await proposal.getSender())?.toString(),
80
+ [Attributes.BLOCK_PROPOSER]: proposer?.toString() ?? 'unknown',
81
+ });
82
+ }
83
+
84
+ public incSuccessfulAttestations(num: number) {
85
+ this.successfulAttestationsCount.add(num);
86
+ }
87
+
88
+ public incFailedAttestationsBadProposal(num: number, reason: string, inCommittee: boolean) {
89
+ this.failedAttestationsBadProposalCount.add(num, {
90
+ [Attributes.ERROR_TYPE]: reason,
91
+ [Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
92
+ });
93
+ }
94
+
95
+ public incFailedAttestationsNodeIssue(num: number, reason: string, inCommittee: boolean) {
96
+ this.failedAttestationsNodeIssueCount.add(num, {
97
+ [Attributes.ERROR_TYPE]: reason,
98
+ [Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
48
99
  });
49
100
  }
50
101
  }