@aztec/aztec-node 3.0.0-canary.a9708bd → 3.0.0-devnet.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
- import { countWhile, filterAsync } from '@aztec/foundation/collection';
1
+ import { countWhile, filterAsync, fromEntries, getEntries, mapValues } from '@aztec/foundation/collection';
2
2
  import { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { RunningPromise } from '@aztec/foundation/running-promise';
5
5
  import { L2TipsMemoryStore } from '@aztec/kv-store/stores';
6
6
  import { OffenseType, WANT_TO_SLASH_EVENT } from '@aztec/slasher';
7
- import { L2BlockStream, getAttestationsFromPublishedL2Block } from '@aztec/stdlib/block';
8
- import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
7
+ import { L2BlockStream, getAttestationInfoFromPublishedL2Block } from '@aztec/stdlib/block';
8
+ import { getEpochAtSlot, getSlotRangeForEpoch, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
9
9
  import EventEmitter from 'node:events';
10
10
  export class Sentinel extends EventEmitter {
11
11
  epochCache;
@@ -55,7 +55,7 @@ export class Sentinel extends EventEmitter {
55
55
  this.slotNumberToBlock.set(block.block.header.getSlot(), {
56
56
  blockNumber: block.block.number,
57
57
  archive: block.block.archive.root.toString(),
58
- attestors: getAttestationsFromPublishedL2Block(block).map((att)=>att.getSender())
58
+ attestors: getAttestationInfoFromPublishedL2Block(block).filter((a)=>a.status === 'recovered-from-signature').map((a)=>a.address)
59
59
  });
60
60
  }
61
61
  // Prune the archive map to only keep at most N entries
@@ -82,18 +82,17 @@ export class Sentinel extends EventEmitter {
82
82
  });
83
83
  return;
84
84
  }
85
- const epoch = getEpochAtSlot(block.header.getSlot(), await this.archiver.getL1Constants());
85
+ // TODO(palla/slash): We should only be computing proven performance if this is
86
+ // a full proof epoch and not a partial one, otherwise we'll end up with skewed stats.
87
+ const epoch = getEpochAtSlot(block.header.getSlot(), this.epochCache.getL1Constants());
86
88
  this.logger.debug(`Computing proven performance for epoch ${epoch}`);
87
89
  const performance = await this.computeProvenPerformance(epoch);
88
90
  this.logger.info(`Computed proven performance for epoch ${epoch}`, performance);
89
- await this.updateProvenPerformance(epoch, performance);
91
+ await this.store.updateProvenPerformance(epoch, performance);
90
92
  await this.handleProvenPerformance(epoch, performance);
91
93
  }
92
94
  async computeProvenPerformance(epoch) {
93
- const headers = await this.archiver.getBlockHeadersForEpoch(epoch);
94
- const provenSlots = headers.map((h)=>h.getSlot());
95
- const fromSlot = provenSlots[0];
96
- const toSlot = provenSlots[provenSlots.length - 1];
95
+ const [fromSlot, toSlot] = getSlotRangeForEpoch(epoch, this.epochCache.getL1Constants());
97
96
  const { committee } = await this.epochCache.getCommittee(fromSlot);
98
97
  if (!committee) {
99
98
  this.logger.trace(`No committee found for slot ${fromSlot}`);
@@ -101,36 +100,25 @@ export class Sentinel extends EventEmitter {
101
100
  }
102
101
  const stats = await this.computeStats({
103
102
  fromSlot,
104
- toSlot
103
+ toSlot,
104
+ validators: committee
105
105
  });
106
- this.logger.debug(`Stats for epoch ${epoch}`, stats);
107
- const performance = {};
108
- for (const validator of Object.keys(stats.stats)){
109
- let address;
110
- try {
111
- address = EthAddress.fromString(validator);
112
- } catch (e) {
113
- this.logger.error(`Invalid validator address ${validator}`, e);
114
- continue;
115
- }
116
- if (!committee.find((v)=>v.equals(address))) {
117
- continue;
118
- }
119
- let missed = 0;
120
- for (const history of stats.stats[validator].history){
121
- if (provenSlots.includes(history.slot) && history.status === 'attestation-missed') {
122
- missed++;
123
- }
124
- }
125
- performance[address.toString()] = {
126
- missed,
127
- total: provenSlots.length
128
- };
129
- }
130
- return performance;
131
- }
132
- updateProvenPerformance(epoch, performance) {
133
- return this.store.updateProvenPerformance(epoch, performance);
106
+ this.logger.debug(`Stats for epoch ${epoch}`, {
107
+ ...stats,
108
+ fromSlot,
109
+ toSlot,
110
+ epoch
111
+ });
112
+ // Note that we are NOT using the total slots in the epoch as `total` here, since we only
113
+ // compute missed attestations over the blocks that had a proposal in them. So, let's say
114
+ // we have an epoch with 10 slots, but only 5 had a block proposal. A validator that was
115
+ // offline, assuming they were not picked as proposer, will then be reported as having missed
116
+ // 5/5 attestations. If we used the total, they'd be reported as 5/10, which would probably
117
+ // allow them to avoid being slashed.
118
+ return mapValues(stats.stats, (stat)=>({
119
+ missed: stat.missedAttestations.count + stat.missedProposals.count,
120
+ total: stat.missedAttestations.total + stat.missedProposals.total
121
+ }));
134
122
  }
135
123
  /**
136
124
  * Checks if a validator has been inactive for the specified number of consecutive epochs for which we have data on it.
@@ -152,9 +140,10 @@ export class Sentinel extends EventEmitter {
152
140
  return allPerformance.sort((a, b)=>Number(b.epoch - a.epoch)).filter((p)=>p.epoch < currentEpoch).slice(0, requiredConsecutiveEpochs).every((p)=>p.missed / p.total >= this.config.slashInactivityTargetPercentage);
153
141
  }
154
142
  async handleProvenPerformance(epoch, performance) {
155
- const inactiveValidators = Object.entries(performance).filter(([_, { missed, total }])=>{
156
- return missed / total >= this.config.slashInactivityTargetPercentage;
157
- }).map(([address])=>address);
143
+ if (this.config.slashInactivityPenalty === 0n) {
144
+ return;
145
+ }
146
+ const inactiveValidators = getEntries(performance).filter(([_, { missed, total }])=>missed / total >= this.config.slashInactivityTargetPercentage).map(([address])=>address);
158
147
  this.logger.debug(`Found ${inactiveValidators.length} inactive validators in epoch ${epoch}`, {
159
148
  inactiveValidators,
160
149
  epoch,
@@ -169,7 +158,7 @@ export class Sentinel extends EventEmitter {
169
158
  epochOrSlot: epoch
170
159
  }));
171
160
  if (criminals.length > 0) {
172
- this.logger.info(`Identified ${criminals.length} validators to slash due to inactivity in at least ${epochThreshold} consecutive epochs`, {
161
+ this.logger.verbose(`Identified ${criminals.length} validators to slash due to inactivity in at least ${epochThreshold} consecutive epochs`, {
173
162
  ...args,
174
163
  epochThreshold
175
164
  });
@@ -267,14 +256,18 @@ export class Sentinel extends EventEmitter {
267
256
  // (contains the ones synced from mined blocks, which we may have missed from p2p).
268
257
  const block = this.slotNumberToBlock.get(slot);
269
258
  const p2pAttested = await this.p2p.getAttestationsForSlot(slot, block?.archive);
259
+ // Filter out attestations with invalid signatures
260
+ const p2pAttestors = p2pAttested.map((a)=>a.getSender()).filter((s)=>s !== undefined);
270
261
  const attestors = new Set([
271
- ...p2pAttested.map((a)=>a.getSender().toString()),
262
+ ...p2pAttestors.map((a)=>a.toString()),
272
263
  ...block?.attestors.map((a)=>a.toString()) ?? []
273
- ]);
274
- // We assume that there was a block proposal if at least one of the validators attested to it.
264
+ ].filter((addr)=>proposer.toString() !== addr));
265
+ // We assume that there was a block proposal if at least one of the validators (other than the proposer) attested to it.
275
266
  // It could be the case that every single validator failed, and we could differentiate it by having
276
267
  // this node re-execute every block proposal it sees and storing it in the attestation pool.
277
268
  // But we'll leave that corner case out to reduce pressure on the node.
269
+ // TODO(palla/slash): This breaks if a given node has more than one validator in the current committee,
270
+ // since they will attest to their own proposal it even if it's not re-executable.
278
271
  const blockStatus = block ? 'mined' : attestors.size > 0 ? 'proposed' : 'missed';
279
272
  this.logger.debug(`Block for slot ${slot} was ${blockStatus}`, {
280
273
  ...block,
@@ -315,18 +308,17 @@ export class Sentinel extends EventEmitter {
315
308
  /** Push the status for each slot for each validator. */ updateValidators(slot, stats) {
316
309
  return this.store.updateValidators(slot, stats);
317
310
  }
318
- /** Computes stats to be returned based on stored data. */ async computeStats({ fromSlot: _fromSlot, toSlot: _toSlot } = {}) {
319
- const histories = await this.store.getHistories();
311
+ /** Computes stats to be returned based on stored data. */ async computeStats({ fromSlot, toSlot, validators } = {}) {
312
+ const histories = validators ? fromEntries(await Promise.all(validators.map(async (v)=>[
313
+ v.toString(),
314
+ await this.store.getHistory(v)
315
+ ]))) : await this.store.getHistories();
320
316
  const slotNow = this.epochCache.getEpochAndSlotNow().slot;
321
- const fromSlot = _fromSlot ?? (this.lastProcessedSlot ?? slotNow) - BigInt(this.store.getHistoryLength());
322
- const toSlot = _toSlot ?? this.lastProcessedSlot ?? slotNow;
323
- const result = {};
324
- for (const [address, history] of Object.entries(histories)){
325
- const validatorAddress = address;
326
- result[validatorAddress] = this.computeStatsForValidator(validatorAddress, history, fromSlot, toSlot);
327
- }
317
+ fromSlot ??= (this.lastProcessedSlot ?? slotNow) - BigInt(this.store.getHistoryLength());
318
+ toSlot ??= this.lastProcessedSlot ?? slotNow;
319
+ const stats = mapValues(histories, (history, address)=>this.computeStatsForValidator(address, history ?? [], fromSlot, toSlot));
328
320
  return {
329
- stats: result,
321
+ stats,
330
322
  lastProcessedSlot: this.lastProcessedSlot,
331
323
  initialSlot: this.initialSlot,
332
324
  slotWindow: this.store.getHistoryLength()
@@ -357,25 +349,32 @@ export class Sentinel extends EventEmitter {
357
349
  computeStatsForValidator(address, allHistory, fromSlot, toSlot) {
358
350
  let history = fromSlot ? allHistory.filter((h)=>h.slot >= fromSlot) : allHistory;
359
351
  history = toSlot ? history.filter((h)=>h.slot <= toSlot) : history;
352
+ const lastProposal = history.filter((h)=>h.status === 'block-proposed' || h.status === 'block-mined').at(-1);
353
+ const lastAttestation = history.filter((h)=>h.status === 'attestation-sent').at(-1);
360
354
  return {
361
355
  address: EthAddress.fromString(address),
362
- lastProposal: this.computeFromSlot(history.filter((h)=>h.status === 'block-proposed' || h.status === 'block-mined').at(-1)?.slot),
363
- lastAttestation: this.computeFromSlot(history.filter((h)=>h.status === 'attestation-sent').at(-1)?.slot),
356
+ lastProposal: this.computeFromSlot(lastProposal?.slot),
357
+ lastAttestation: this.computeFromSlot(lastAttestation?.slot),
364
358
  totalSlots: history.length,
365
- missedProposals: this.computeMissed(history, 'block', 'block-missed'),
366
- missedAttestations: this.computeMissed(history, 'attestation', 'attestation-missed'),
359
+ missedProposals: this.computeMissed(history, 'block', [
360
+ 'block-missed'
361
+ ]),
362
+ missedAttestations: this.computeMissed(history, 'attestation', [
363
+ 'attestation-missed'
364
+ ]),
367
365
  history
368
366
  };
369
367
  }
370
368
  computeMissed(history, computeOverPrefix, filter) {
371
- const relevantHistory = history.filter((h)=>h.status.startsWith(computeOverPrefix));
372
- const filteredHistory = relevantHistory.filter((h)=>h.status === filter);
369
+ const relevantHistory = history.filter((h)=>!computeOverPrefix || h.status.startsWith(computeOverPrefix));
370
+ const filteredHistory = relevantHistory.filter((h)=>filter.includes(h.status));
373
371
  return {
374
372
  currentStreak: countWhile([
375
373
  ...relevantHistory
376
- ].reverse(), (h)=>h.status === filter),
374
+ ].reverse(), (h)=>filter.includes(h.status)),
377
375
  rate: relevantHistory.length === 0 ? undefined : filteredHistory.length / relevantHistory.length,
378
- count: filteredHistory.length
376
+ count: filteredHistory.length,
377
+ total: relevantHistory.length
379
378
  };
380
379
  }
381
380
  computeFromSlot(slot) {
@@ -9,8 +9,10 @@ export declare class SentinelStore {
9
9
  private readonly provenMap;
10
10
  constructor(store: AztecAsyncKVStore, config: {
11
11
  historyLength: number;
12
+ historicProvenPerformanceLength: number;
12
13
  });
13
14
  getHistoryLength(): number;
15
+ getHistoricProvenPerformanceLength(): number;
14
16
  updateProvenPerformance(epoch: bigint, performance: ValidatorsEpochPerformance): Promise<void>;
15
17
  getProvenPerformance(who: EthAddress): Promise<{
16
18
  missed: number;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sentinel/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAElC,qBAAa,aAAa;IAWtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IAXhB,gBAAuB,cAAc,KAAK;IAG1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAIlE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;gBAGvD,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;IAMpC,gBAAgB;IAIV,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B;IAQ9E,oBAAoB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;YAKjG,sCAAsC;IA6BvC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,qBAAqB,GAAG,SAAS,CAAC;YAUhG,0BAA0B;IAY3B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAQtE,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAKzF,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,gBAAgB;CAgBzB"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sentinel/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAElC,qBAAa,aAAa;IAWtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IAXhB,gBAAuB,cAAc,KAAK;IAG1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAIlE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;gBAGvD,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,+BAA+B,EAAE,MAAM,CAAA;KAAE;IAM7E,gBAAgB;IAIhB,kCAAkC;IAI5B,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B;IAQ9E,oBAAoB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;YAKjG,sCAAsC;IA6BvC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,qBAAqB,GAAG,SAAS,CAAC;YAUhG,0BAA0B;IAY3B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAQtE,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAKzF,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,gBAAgB;CAgBzB"}
@@ -18,6 +18,9 @@ export class SentinelStore {
18
18
  getHistoryLength() {
19
19
  return this.config.historyLength;
20
20
  }
21
+ getHistoricProvenPerformanceLength() {
22
+ return this.config.historicProvenPerformanceLength;
23
+ }
21
24
  async updateProvenPerformance(epoch, performance) {
22
25
  await this.store.transactionAsync(async ()=>{
23
26
  for (const [who, { missed, total }] of Object.entries(performance)){
@@ -53,8 +56,8 @@ export class SentinelStore {
53
56
  // This should be sorted by epoch, but just in case.
54
57
  // Since we keep the size small, this is not a big deal.
55
58
  currentPerformance.sort((a, b)=>Number(a.epoch - b.epoch));
56
- // keep the most recent `historyLength` entries.
57
- const performanceToKeep = currentPerformance.slice(-this.config.historyLength);
59
+ // keep the most recent `historicProvenPerformanceLength` entries.
60
+ const performanceToKeep = currentPerformance.slice(-this.config.historicProvenPerformanceLength);
58
61
  await this.provenMap.set(who.toString(), this.serializePerformance(performanceToKeep));
59
62
  }
60
63
  async updateValidators(slot, statuses) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/aztec-node",
3
- "version": "3.0.0-canary.a9708bd",
3
+ "version": "3.0.0-devnet.2",
4
4
  "main": "dest/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -66,33 +66,33 @@
66
66
  ]
67
67
  },
68
68
  "dependencies": {
69
- "@aztec/archiver": "3.0.0-canary.a9708bd",
70
- "@aztec/bb-prover": "3.0.0-canary.a9708bd",
71
- "@aztec/blob-sink": "3.0.0-canary.a9708bd",
72
- "@aztec/constants": "3.0.0-canary.a9708bd",
73
- "@aztec/epoch-cache": "3.0.0-canary.a9708bd",
74
- "@aztec/ethereum": "3.0.0-canary.a9708bd",
75
- "@aztec/foundation": "3.0.0-canary.a9708bd",
76
- "@aztec/kv-store": "3.0.0-canary.a9708bd",
77
- "@aztec/l1-artifacts": "3.0.0-canary.a9708bd",
78
- "@aztec/merkle-tree": "3.0.0-canary.a9708bd",
79
- "@aztec/node-keystore": "3.0.0-canary.a9708bd",
80
- "@aztec/node-lib": "3.0.0-canary.a9708bd",
81
- "@aztec/noir-protocol-circuits-types": "3.0.0-canary.a9708bd",
82
- "@aztec/p2p": "3.0.0-canary.a9708bd",
83
- "@aztec/protocol-contracts": "3.0.0-canary.a9708bd",
84
- "@aztec/prover-client": "3.0.0-canary.a9708bd",
85
- "@aztec/sequencer-client": "3.0.0-canary.a9708bd",
86
- "@aztec/simulator": "3.0.0-canary.a9708bd",
87
- "@aztec/slasher": "3.0.0-canary.a9708bd",
88
- "@aztec/stdlib": "3.0.0-canary.a9708bd",
89
- "@aztec/telemetry-client": "3.0.0-canary.a9708bd",
90
- "@aztec/validator-client": "3.0.0-canary.a9708bd",
91
- "@aztec/world-state": "3.0.0-canary.a9708bd",
69
+ "@aztec/archiver": "3.0.0-devnet.2",
70
+ "@aztec/bb-prover": "3.0.0-devnet.2",
71
+ "@aztec/blob-sink": "3.0.0-devnet.2",
72
+ "@aztec/constants": "3.0.0-devnet.2",
73
+ "@aztec/epoch-cache": "3.0.0-devnet.2",
74
+ "@aztec/ethereum": "3.0.0-devnet.2",
75
+ "@aztec/foundation": "3.0.0-devnet.2",
76
+ "@aztec/kv-store": "3.0.0-devnet.2",
77
+ "@aztec/l1-artifacts": "3.0.0-devnet.2",
78
+ "@aztec/merkle-tree": "3.0.0-devnet.2",
79
+ "@aztec/node-keystore": "3.0.0-devnet.2",
80
+ "@aztec/node-lib": "3.0.0-devnet.2",
81
+ "@aztec/noir-protocol-circuits-types": "3.0.0-devnet.2",
82
+ "@aztec/p2p": "3.0.0-devnet.2",
83
+ "@aztec/protocol-contracts": "3.0.0-devnet.2",
84
+ "@aztec/prover-client": "3.0.0-devnet.2",
85
+ "@aztec/sequencer-client": "3.0.0-devnet.2",
86
+ "@aztec/simulator": "3.0.0-devnet.2",
87
+ "@aztec/slasher": "3.0.0-devnet.2",
88
+ "@aztec/stdlib": "3.0.0-devnet.2",
89
+ "@aztec/telemetry-client": "3.0.0-devnet.2",
90
+ "@aztec/validator-client": "3.0.0-devnet.2",
91
+ "@aztec/world-state": "3.0.0-devnet.2",
92
92
  "koa": "^2.16.1",
93
- "koa-router": "^12.0.0",
93
+ "koa-router": "^13.1.1",
94
94
  "tslib": "^2.4.0",
95
- "viem": "2.23.7"
95
+ "viem": "npm:@spalladino/viem@2.38.2-eip7594.0"
96
96
  },
97
97
  "devDependencies": {
98
98
  "@jest/globals": "^30.0.0",
@@ -6,16 +6,12 @@ import {
6
6
  l1ContractAddressesMapping,
7
7
  } from '@aztec/ethereum';
8
8
  import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config';
9
+ import { EthAddress } from '@aztec/foundation/eth-address';
9
10
  import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
10
11
  import {
11
- type AztecAddressHex,
12
- type EthAddressHex,
13
- type EthPrivateKey,
14
- type EthRemoteSignerAccount,
15
- type Hex,
16
12
  type KeyStore,
17
- type KeyStoreConfig,
18
13
  type ValidatorKeyStore,
14
+ ethPrivateKeySchema,
19
15
  keyStoreConfigMappings,
20
16
  } from '@aztec/node-keystore';
21
17
  import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib/config';
@@ -50,7 +46,6 @@ export type AztecNodeConfig = ArchiverConfig &
50
46
  Pick<ProverClientUserConfig, 'bbBinaryPath' | 'bbWorkingDirectory' | 'realProofs'> &
51
47
  P2PConfig &
52
48
  DataStoreConfig &
53
- KeyStoreConfig &
54
49
  SentinelConfig &
55
50
  SharedNodeConfig &
56
51
  GenesisStateConfig &
@@ -97,7 +92,7 @@ export function getConfigEnvVars(): AztecNodeConfig {
97
92
 
98
93
  type ConfigRequiredToBuildKeyStore = TxSenderConfig & SequencerClientConfig & SharedNodeConfig & ValidatorClientConfig;
99
94
 
100
- function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore) {
95
+ function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore): KeyStore | undefined {
101
96
  const validatorKeyStores: ValidatorKeyStore[] = [];
102
97
 
103
98
  if (
@@ -108,22 +103,13 @@ function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore) {
108
103
  ) {
109
104
  return undefined;
110
105
  }
111
- const coinbase = config.coinbase ? config.coinbase.toString() : config.validatorAddresses[0].toString();
112
- const feeRecipient = config.feeRecipient ? config.feeRecipient.toString() : AztecAddress.ZERO.toString();
113
-
114
- const publisherAddresses =
115
- config.publisherAddresses && config.publisherAddresses.length > 0
116
- ? config.publisherAddresses.map(k => k.toChecksumString() as EthRemoteSignerAccount)
117
- : [];
118
-
119
- const attestors = config.validatorAddresses.map(k => k.toChecksumString() as EthRemoteSignerAccount);
120
106
 
121
107
  validatorKeyStores.push({
122
- attester: attestors,
123
- feeRecipient: feeRecipient as AztecAddressHex,
124
- coinbase: coinbase as EthAddressHex,
108
+ attester: config.validatorAddresses,
109
+ feeRecipient: config.feeRecipient ?? AztecAddress.ZERO,
110
+ coinbase: config.coinbase ?? config.validatorAddresses[0],
125
111
  remoteSigner: config.web3SignerUrl,
126
- publisher: publisherAddresses,
112
+ publisher: config.publisherAddresses ?? [],
127
113
  });
128
114
 
129
115
  const keyStore: KeyStore = {
@@ -136,30 +122,26 @@ function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore) {
136
122
  return keyStore;
137
123
  }
138
124
 
139
- function createKeyStoreFromPrivateKeys(config: ConfigRequiredToBuildKeyStore) {
125
+ function createKeyStoreFromPrivateKeys(config: ConfigRequiredToBuildKeyStore): KeyStore | undefined {
140
126
  const validatorKeyStores: ValidatorKeyStore[] = [];
141
- const ethPrivateKeys: EthPrivateKey[] = [];
142
- const validatorKeys = config.validatorPrivateKeys ? config.validatorPrivateKeys.getValue() : [];
143
- for (let i = 0; i < validatorKeys.length; i++) {
144
- const key = validatorKeys[i];
145
- const ethPrivateKey: EthPrivateKey = key as Hex<32>;
146
- ethPrivateKeys.push(ethPrivateKey);
147
- }
127
+ const ethPrivateKeys = config.validatorPrivateKeys
128
+ ? config.validatorPrivateKeys.getValue().map(x => ethPrivateKeySchema.parse(x))
129
+ : [];
148
130
 
149
131
  if (!ethPrivateKeys.length) {
150
132
  return undefined;
151
133
  }
152
- const coinbase = config.coinbase ? config.coinbase.toString() : privateKeyToAddress(ethPrivateKeys[0]);
153
- const feeRecipient = config.feeRecipient ? config.feeRecipient.toString() : AztecAddress.ZERO.toString();
134
+ const coinbase = config.coinbase ?? EthAddress.fromString(privateKeyToAddress(ethPrivateKeys[0]));
135
+ const feeRecipient = config.feeRecipient ?? AztecAddress.ZERO;
154
136
 
155
137
  const publisherKeys = config.publisherPrivateKeys
156
- ? config.publisherPrivateKeys.map(k => k.getValue() as EthPrivateKey)
138
+ ? config.publisherPrivateKeys.map(k => ethPrivateKeySchema.parse(k.getValue()))
157
139
  : [];
158
140
 
159
141
  validatorKeyStores.push({
160
142
  attester: ethPrivateKeys,
161
- feeRecipient: feeRecipient as AztecAddressHex,
162
- coinbase: coinbase as EthAddressHex,
143
+ feeRecipient: feeRecipient,
144
+ coinbase: coinbase,
163
145
  remoteSigner: undefined,
164
146
  publisher: publisherKeys,
165
147
  });
@@ -174,7 +156,9 @@ function createKeyStoreFromPrivateKeys(config: ConfigRequiredToBuildKeyStore) {
174
156
  return keyStore;
175
157
  }
176
158
 
177
- export function createKeyStoreForValidator(config: TxSenderConfig & SequencerClientConfig & SharedNodeConfig) {
159
+ export function createKeyStoreForValidator(
160
+ config: TxSenderConfig & SequencerClientConfig & SharedNodeConfig,
161
+ ): KeyStore | undefined {
178
162
  if (config.web3SignerUrl !== undefined && config.web3SignerUrl.length > 0) {
179
163
  return createKeyStoreFromWeb3Signer(config);
180
164
  }