@aztec/aztec-node 0.87.2 → 0.87.3-nightly.20250528
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/aztec-node/config.d.ts +2 -1
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +2 -0
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +20 -7
- package/dest/sentinel/factory.d.ts +2 -1
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/factory.js +1 -1
- package/dest/sentinel/sentinel.d.ts +18 -5
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +101 -14
- package/dest/sentinel/store.d.ts +13 -3
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +73 -6
- package/package.json +23 -22
- package/src/aztec-node/config.ts +4 -1
- package/src/aztec-node/server.ts +28 -9
- package/src/sentinel/factory.ts +3 -2
- package/src/sentinel/sentinel.ts +114 -6
- package/src/sentinel/store.ts +83 -7
|
@@ -6,6 +6,7 @@ import { type SharedNodeConfig } from '@aztec/node-lib/config';
|
|
|
6
6
|
import { type P2PConfig } from '@aztec/p2p/config';
|
|
7
7
|
import { type ProverClientUserConfig } from '@aztec/prover-client/config';
|
|
8
8
|
import { type SequencerClientConfig, sequencerClientConfigMappings } from '@aztec/sequencer-client/config';
|
|
9
|
+
import { type SlasherConfig } from '@aztec/slasher';
|
|
9
10
|
import { type NodeRPCConfig } from '@aztec/stdlib/config';
|
|
10
11
|
import { type ValidatorClientConfig } from '@aztec/validator-client/config';
|
|
11
12
|
import { type WorldStateConfig } from '@aztec/world-state/config';
|
|
@@ -14,7 +15,7 @@ export { sequencerClientConfigMappings, type SequencerClientConfig };
|
|
|
14
15
|
/**
|
|
15
16
|
* The configuration the aztec node.
|
|
16
17
|
*/
|
|
17
|
-
export type AztecNodeConfig = ArchiverConfig & SequencerClientConfig & ValidatorClientConfig & ProverClientUserConfig & WorldStateConfig & Pick<ProverClientUserConfig, 'bbBinaryPath' | 'bbWorkingDirectory' | 'realProofs'> & P2PConfig & DataStoreConfig & SentinelConfig & SharedNodeConfig & GenesisStateConfig & NodeRPCConfig & {
|
|
18
|
+
export type AztecNodeConfig = ArchiverConfig & SequencerClientConfig & ValidatorClientConfig & ProverClientUserConfig & WorldStateConfig & Pick<ProverClientUserConfig, 'bbBinaryPath' | 'bbWorkingDirectory' | 'realProofs'> & P2PConfig & DataStoreConfig & SentinelConfig & SharedNodeConfig & GenesisStateConfig & NodeRPCConfig & SlasherConfig & {
|
|
18
19
|
/** L1 contracts addresses */
|
|
19
20
|
l1Contracts: L1ContractAddresses;
|
|
20
21
|
/** Whether the validator is disabled for this node */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/aztec-node/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,wBAAwB,CAAC;AACrF,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EAGzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,kBAAkB,EAA8C,MAAM,0BAA0B,CAAC;AAC/G,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,KAAK,gBAAgB,EAA4B,MAAM,wBAAwB,CAAC;AACzF,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,sBAAsB,EAA8B,MAAM,6BAA6B,CAAC;AACtG,OAAO,EAAE,KAAK,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,KAAK,aAAa,EAAyB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,KAAK,qBAAqB,EAAiC,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,KAAK,gBAAgB,EAA4B,MAAM,2BAA2B,CAAC;AAE5F,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,uBAAuB,CAAC;AAEpF,OAAO,EAAE,6BAA6B,EAAE,KAAK,qBAAqB,EAAE,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,cAAc,GAC1C,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,gBAAgB,GAChB,IAAI,CAAC,sBAAsB,EAAE,cAAc,GAAG,oBAAoB,GAAG,YAAY,CAAC,GAClF,SAAS,GACT,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,aAAa,GAAG;IACd,6BAA6B;IAC7B,WAAW,EAAE,mBAAmB,CAAC;IACjC,sDAAsD;IACtD,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEJ,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/aztec-node/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,wBAAwB,CAAC;AACrF,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EAGzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,kBAAkB,EAA8C,MAAM,0BAA0B,CAAC;AAC/G,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,KAAK,gBAAgB,EAA4B,MAAM,wBAAwB,CAAC;AACzF,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,sBAAsB,EAA8B,MAAM,6BAA6B,CAAC;AACtG,OAAO,EAAE,KAAK,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,KAAK,aAAa,EAAyB,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,KAAK,aAAa,EAAyB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,KAAK,qBAAqB,EAAiC,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,KAAK,gBAAgB,EAA4B,MAAM,2BAA2B,CAAC;AAE5F,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,uBAAuB,CAAC;AAEpF,OAAO,EAAE,6BAA6B,EAAE,KAAK,qBAAqB,EAAE,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,cAAc,GAC1C,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,gBAAgB,GAChB,IAAI,CAAC,sBAAsB,EAAE,cAAc,GAAG,oBAAoB,GAAG,YAAY,CAAC,GAClF,SAAS,GACT,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,aAAa,GACb,aAAa,GAAG;IACd,6BAA6B;IAC7B,WAAW,EAAE,mBAAmB,CAAC;IACjC,sDAAsD;IACtD,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEJ,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,eAAe,CAsBvE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,eAAe,CAElD"}
|
|
@@ -6,6 +6,7 @@ import { sharedNodeConfigMappings } from '@aztec/node-lib/config';
|
|
|
6
6
|
import { p2pConfigMappings } from '@aztec/p2p/config';
|
|
7
7
|
import { proverClientConfigMappings } from '@aztec/prover-client/config';
|
|
8
8
|
import { sequencerClientConfigMappings } from '@aztec/sequencer-client/config';
|
|
9
|
+
import { slasherConfigMappings } from '@aztec/slasher';
|
|
9
10
|
import { nodeRpcConfigMappings } from '@aztec/stdlib/config';
|
|
10
11
|
import { validatorClientConfigMappings } from '@aztec/validator-client/config';
|
|
11
12
|
import { worldStateConfigMappings } from '@aztec/world-state/config';
|
|
@@ -23,6 +24,7 @@ export const aztecNodeConfigMappings = {
|
|
|
23
24
|
...sharedNodeConfigMappings,
|
|
24
25
|
...genesisStateConfigMappings,
|
|
25
26
|
...nodeRpcConfigMappings,
|
|
27
|
+
...slasherConfigMappings,
|
|
26
28
|
l1Contracts: {
|
|
27
29
|
description: 'The deployed L1 contract addresses',
|
|
28
30
|
nested: l1ContractAddressesMapping
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/aztec-node/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,uBAAuB,EAAwB,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC7B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/aztec-node/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,8 @@ import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
|
9
9
|
import { createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
10
10
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
11
11
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
12
|
-
import { RegistryContract, createEthereumChain } from '@aztec/ethereum';
|
|
12
|
+
import { RegistryContract, createEthereumChain, createExtendedL1Client } from '@aztec/ethereum';
|
|
13
|
+
import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
13
14
|
import { compactArray } from '@aztec/foundation/collection';
|
|
14
15
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
15
16
|
import { Fr } from '@aztec/foundation/fields';
|
|
@@ -25,8 +26,9 @@ import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree';
|
|
|
25
26
|
import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
|
|
26
27
|
import { createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
|
|
27
28
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
28
|
-
import { GlobalVariableBuilder, SequencerClient,
|
|
29
|
+
import { GlobalVariableBuilder, SequencerClient, createValidatorForAcceptingTxs } from '@aztec/sequencer-client';
|
|
29
30
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
31
|
+
import { EpochPruneWatcher, SlasherClient } from '@aztec/slasher';
|
|
30
32
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
31
33
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
32
34
|
import { tryStop } from '@aztec/stdlib/interfaces/server';
|
|
@@ -125,6 +127,8 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
125
127
|
...config.l1Contracts,
|
|
126
128
|
...l1ContractsAddresses
|
|
127
129
|
};
|
|
130
|
+
const l1Client = createExtendedL1Client(config.l1RpcUrls, config.publisherPrivateKey, ethereumChain.chainInfo);
|
|
131
|
+
const l1TxUtils = new L1TxUtilsWithBlobs(l1Client, log, config);
|
|
128
132
|
const rollup = getContract({
|
|
129
133
|
address: l1ContractsAddresses.rollupAddress.toString(),
|
|
130
134
|
abi: RollupAbi,
|
|
@@ -155,8 +159,15 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
155
159
|
await worldStateSynchronizer.start();
|
|
156
160
|
// Start p2p. Note that it depends on world state to be running.
|
|
157
161
|
await p2pClient.start();
|
|
158
|
-
const
|
|
159
|
-
|
|
162
|
+
const watchers = [];
|
|
163
|
+
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
164
|
+
if (validatorsSentinel) {
|
|
165
|
+
watchers.push(validatorsSentinel);
|
|
166
|
+
}
|
|
167
|
+
const epochPruneWatcher = new EpochPruneWatcher(archiver, epochCache, config.slashPrunePenalty);
|
|
168
|
+
watchers.push(epochPruneWatcher);
|
|
169
|
+
const slasherClient = await SlasherClient.new(config, config.l1Contracts, l1TxUtils, watchers, dateProvider);
|
|
170
|
+
await slasherClient.start();
|
|
160
171
|
const validatorClient = createValidatorClient(config, {
|
|
161
172
|
p2pClient,
|
|
162
173
|
telemetry,
|
|
@@ -164,12 +175,14 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
164
175
|
epochCache,
|
|
165
176
|
blockSource: archiver
|
|
166
177
|
});
|
|
167
|
-
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
168
|
-
await validatorsSentinel?.start();
|
|
169
178
|
log.verbose(`All Aztec Node subsystems synced`);
|
|
170
179
|
// now create the sequencer
|
|
171
180
|
const sequencer = config.disableValidator ? undefined : await SequencerClient.new(config, {
|
|
181
|
+
// if deps were provided, they should override the defaults,
|
|
182
|
+
// or things that we created in this function
|
|
172
183
|
...deps,
|
|
184
|
+
epochCache,
|
|
185
|
+
l1TxUtils,
|
|
173
186
|
validatorClient,
|
|
174
187
|
p2pClient,
|
|
175
188
|
worldStateSynchronizer,
|
|
@@ -365,7 +378,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
365
378
|
*/ async stop() {
|
|
366
379
|
this.log.info(`Stopping`);
|
|
367
380
|
await this.txQueue.end();
|
|
368
|
-
await this.validatorsSentinel?.stop();
|
|
381
|
+
// await this.validatorsSentinel?.stop(); <- The slasher client will stop this
|
|
369
382
|
await this.sequencer?.stop();
|
|
370
383
|
await this.p2pClient.stop();
|
|
371
384
|
await this.worldStateSynchronizer.stop();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
2
|
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
3
3
|
import type { P2PClient } from '@aztec/p2p';
|
|
4
|
+
import type { SlasherConfig } from '@aztec/slasher/config';
|
|
4
5
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
5
6
|
import type { SentinelConfig } from './config.js';
|
|
6
7
|
import { Sentinel } from './sentinel.js';
|
|
7
|
-
export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, config: SentinelConfig & DataStoreConfig, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
|
|
8
|
+
export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, config: SentinelConfig & DataStoreConfig & SlasherConfig, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
|
|
8
9
|
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,cAAc,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,aAAa,EACxD,MAAM,yCAAgC,GACrC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAa/B"}
|
package/dest/sentinel/factory.js
CHANGED
|
@@ -11,5 +11,5 @@ export async function createSentinel(epochCache, archiver, p2p, config, logger =
|
|
|
11
11
|
const sentinelStore = new SentinelStore(kvStore, {
|
|
12
12
|
historyLength: storeHistoryLength
|
|
13
13
|
});
|
|
14
|
-
return new Sentinel(epochCache, archiver, p2p, sentinelStore, logger);
|
|
14
|
+
return new Sentinel(epochCache, archiver, p2p, sentinelStore, config, logger);
|
|
15
15
|
}
|
|
@@ -3,14 +3,18 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
3
3
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
4
4
|
import { type L2TipsStore } from '@aztec/kv-store/stores';
|
|
5
5
|
import type { P2PClient } from '@aztec/p2p';
|
|
6
|
+
import type { SlasherConfig, Watcher, WatcherEmitter } from '@aztec/slasher/config';
|
|
7
|
+
import { Offence } from '@aztec/slasher/config';
|
|
6
8
|
import { type L2BlockSource, L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler } from '@aztec/stdlib/block';
|
|
7
|
-
import type { ValidatorStats, ValidatorStatusHistory, ValidatorStatusInSlot, ValidatorStatusType, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
9
|
+
import type { ValidatorStats, ValidatorStatusHistory, ValidatorStatusInSlot, ValidatorStatusType, ValidatorsEpochPerformance, ValidatorsStats } from '@aztec/stdlib/validators';
|
|
8
10
|
import { SentinelStore } from './store.js';
|
|
9
|
-
|
|
11
|
+
declare const Sentinel_base: new () => WatcherEmitter;
|
|
12
|
+
export declare class Sentinel extends Sentinel_base implements L2BlockStreamEventHandler, Watcher {
|
|
10
13
|
protected epochCache: EpochCache;
|
|
11
14
|
protected archiver: L2BlockSource;
|
|
12
15
|
protected p2p: P2PClient;
|
|
13
16
|
protected store: SentinelStore;
|
|
17
|
+
protected config: Pick<SlasherConfig, 'slashInactivityCreateTargetPercentage' | 'slashInactivityCreatePenalty' | 'slashInactivitySignalTargetPercentage' | 'slashPayloadTtlSeconds'>;
|
|
14
18
|
protected logger: import("@aztec/foundation/log").Logger;
|
|
15
19
|
protected runningPromise: RunningPromise;
|
|
16
20
|
protected blockStream: L2BlockStream;
|
|
@@ -22,12 +26,17 @@ export declare class Sentinel implements L2BlockStreamEventHandler {
|
|
|
22
26
|
archive: string;
|
|
23
27
|
attestors: EthAddress[];
|
|
24
28
|
}>;
|
|
25
|
-
constructor(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, store: SentinelStore, logger?: import("@aztec/foundation/log").Logger);
|
|
29
|
+
constructor(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, store: SentinelStore, config: Pick<SlasherConfig, 'slashInactivityCreateTargetPercentage' | 'slashInactivityCreatePenalty' | 'slashInactivitySignalTargetPercentage' | 'slashPayloadTtlSeconds'>, logger?: import("@aztec/foundation/log").Logger);
|
|
26
30
|
start(): Promise<void>;
|
|
27
31
|
/** Loads initial slot and initializes blockstream. We will not process anything at or before the initial slot. */
|
|
28
32
|
protected init(): Promise<void>;
|
|
29
33
|
stop(): Promise<void>;
|
|
30
34
|
handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
|
|
35
|
+
protected handleChainProven(event: L2BlockStreamEvent): Promise<void>;
|
|
36
|
+
protected computeProvenPerformance(epoch: bigint): Promise<ValidatorsEpochPerformance>;
|
|
37
|
+
protected updateProvenPerformance(epoch: bigint, performance: ValidatorsEpochPerformance): Promise<void>;
|
|
38
|
+
protected handleProvenPerformance(performance: ValidatorsEpochPerformance): void;
|
|
39
|
+
shouldSlash(validator: `0x${string}`, _amount: bigint, _offense: Offence): Promise<boolean>;
|
|
31
40
|
/**
|
|
32
41
|
* Process data for two L2 slots ago.
|
|
33
42
|
* Note that we do not process historical data, since we rely on p2p data for processing,
|
|
@@ -52,8 +61,11 @@ export declare class Sentinel implements L2BlockStreamEventHandler {
|
|
|
52
61
|
/** Push the status for each slot for each validator. */
|
|
53
62
|
protected updateValidators(slot: bigint, stats: Record<`0x${string}`, ValidatorStatusInSlot | undefined>): Promise<void>;
|
|
54
63
|
/** Computes stats to be returned based on stored data. */
|
|
55
|
-
computeStats(
|
|
56
|
-
|
|
64
|
+
computeStats({ fromSlot: _fromSlot, toSlot: _toSlot, }?: {
|
|
65
|
+
fromSlot?: bigint;
|
|
66
|
+
toSlot?: bigint;
|
|
67
|
+
}): Promise<ValidatorsStats>;
|
|
68
|
+
protected computeStatsForValidator(address: `0x${string}`, allHistory: ValidatorStatusHistory, fromSlot?: bigint, toSlot?: bigint): ValidatorStats;
|
|
57
69
|
protected computeMissed(history: ValidatorStatusHistory, computeOverPrefix: ValidatorStatusType, filter: ValidatorStatusInSlot): {
|
|
58
70
|
currentStreak: number;
|
|
59
71
|
rate: number | undefined;
|
|
@@ -65,4 +77,5 @@ export declare class Sentinel implements L2BlockStreamEventHandler {
|
|
|
65
77
|
date: string;
|
|
66
78
|
} | undefined;
|
|
67
79
|
}
|
|
80
|
+
export {};
|
|
68
81
|
//# sourceMappingURL=sentinel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../../src/sentinel/sentinel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EACL,KAAK,aAAa,EAClB,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAE/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../../src/sentinel/sentinel.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,13 +3,16 @@ 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
|
+
import { Offence, WANT_TO_SLASH_EVENT } from '@aztec/slasher/config';
|
|
6
7
|
import { L2BlockStream, getAttestationsFromPublishedL2Block } from '@aztec/stdlib/block';
|
|
7
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
8
|
-
|
|
8
|
+
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
9
|
+
import EventEmitter from 'node:events';
|
|
10
|
+
export class Sentinel extends EventEmitter {
|
|
9
11
|
epochCache;
|
|
10
12
|
archiver;
|
|
11
13
|
p2p;
|
|
12
14
|
store;
|
|
15
|
+
config;
|
|
13
16
|
logger;
|
|
14
17
|
runningPromise;
|
|
15
18
|
blockStream;
|
|
@@ -17,13 +20,8 @@ export class Sentinel {
|
|
|
17
20
|
initialSlot;
|
|
18
21
|
lastProcessedSlot;
|
|
19
22
|
slotNumberToBlock;
|
|
20
|
-
constructor(epochCache, archiver, p2p, store, logger = createLogger('node:sentinel')){
|
|
21
|
-
this.epochCache = epochCache;
|
|
22
|
-
this.archiver = archiver;
|
|
23
|
-
this.p2p = p2p;
|
|
24
|
-
this.store = store;
|
|
25
|
-
this.logger = logger;
|
|
26
|
-
this.slotNumberToBlock = new Map();
|
|
23
|
+
constructor(epochCache, archiver, p2p, store, config, logger = createLogger('node:sentinel')){
|
|
24
|
+
super(), this.epochCache = epochCache, this.archiver = archiver, this.p2p = p2p, this.store = store, this.config = config, this.logger = logger, this.slotNumberToBlock = new Map();
|
|
27
25
|
this.l2TipsStore = new L2TipsMemoryStore();
|
|
28
26
|
const interval = epochCache.getL1Constants().ethereumSlotDuration * 1000 / 4;
|
|
29
27
|
this.runningPromise = new RunningPromise(this.work.bind(this), logger, interval);
|
|
@@ -62,8 +60,95 @@ export class Sentinel {
|
|
|
62
60
|
this.slotNumberToBlock.delete(key);
|
|
63
61
|
}
|
|
64
62
|
}
|
|
63
|
+
} else if (event.type === 'chain-proven') {
|
|
64
|
+
await this.handleChainProven(event);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
async handleChainProven(event) {
|
|
68
|
+
if (event.type !== 'chain-proven') {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const blockNumber = event.block.number;
|
|
72
|
+
const block = await this.archiver.getBlock(blockNumber);
|
|
73
|
+
if (!block) {
|
|
74
|
+
this.logger.error(`Failed to get block ${blockNumber}`, {
|
|
75
|
+
block
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const epoch = getEpochAtSlot(block.header.getSlot(), await this.archiver.getL1Constants());
|
|
80
|
+
this.logger.info(`Computing proven performance for epoch ${epoch}`);
|
|
81
|
+
const performance = await this.computeProvenPerformance(epoch);
|
|
82
|
+
this.logger.info(`Proven performance for epoch ${epoch}`, performance);
|
|
83
|
+
await this.updateProvenPerformance(epoch, performance);
|
|
84
|
+
this.handleProvenPerformance(performance);
|
|
85
|
+
}
|
|
86
|
+
async computeProvenPerformance(epoch) {
|
|
87
|
+
const headers = await this.archiver.getBlockHeadersForEpoch(epoch);
|
|
88
|
+
const provenSlots = headers.map((h)=>h.getSlot());
|
|
89
|
+
const fromSlot = provenSlots[0];
|
|
90
|
+
const toSlot = provenSlots[provenSlots.length - 1];
|
|
91
|
+
const { committee } = await this.epochCache.getCommittee(fromSlot);
|
|
92
|
+
const stats = await this.computeStats({
|
|
93
|
+
fromSlot,
|
|
94
|
+
toSlot
|
|
95
|
+
});
|
|
96
|
+
this.logger.debug(`Stats for epoch ${epoch}`, stats);
|
|
97
|
+
const performance = {};
|
|
98
|
+
for (const validator of Object.keys(stats.stats)){
|
|
99
|
+
let address;
|
|
100
|
+
try {
|
|
101
|
+
address = EthAddress.fromString(validator);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
this.logger.error(`Invalid validator address ${validator}`, e);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!committee.find((v)=>v.equals(address))) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
let missed = 0;
|
|
110
|
+
for (const history of stats.stats[validator].history){
|
|
111
|
+
if (provenSlots.includes(history.slot) && history.status === 'attestation-missed') {
|
|
112
|
+
missed++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
performance[address.toString()] = {
|
|
116
|
+
missed,
|
|
117
|
+
total: provenSlots.length
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return performance;
|
|
121
|
+
}
|
|
122
|
+
updateProvenPerformance(epoch, performance) {
|
|
123
|
+
return this.store.updateProvenPerformance(epoch, performance);
|
|
124
|
+
}
|
|
125
|
+
handleProvenPerformance(performance) {
|
|
126
|
+
const criminals = Object.entries(performance).filter(([_, { missed, total }])=>{
|
|
127
|
+
return missed / total >= this.config.slashInactivityCreateTargetPercentage;
|
|
128
|
+
}).map(([address])=>address);
|
|
129
|
+
const amounts = Array(criminals.length).fill(this.config.slashInactivityCreatePenalty);
|
|
130
|
+
const offenses = Array(criminals.length).fill(Offence.INACTIVITY);
|
|
131
|
+
this.logger.info(`Criminals: ${criminals.length}`, {
|
|
132
|
+
criminals,
|
|
133
|
+
amounts,
|
|
134
|
+
offenses
|
|
135
|
+
});
|
|
136
|
+
if (criminals.length > 0) {
|
|
137
|
+
this.emit(WANT_TO_SLASH_EVENT, {
|
|
138
|
+
validators: criminals,
|
|
139
|
+
amounts,
|
|
140
|
+
offenses
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async shouldSlash(validator, _amount, _offense) {
|
|
145
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
146
|
+
const ttlL2Slots = this.config.slashPayloadTtlSeconds / l1Constants.slotDuration;
|
|
147
|
+
const ttlEpochs = BigInt(Math.ceil(ttlL2Slots / l1Constants.epochDuration));
|
|
148
|
+
const currentEpoch = this.epochCache.getEpochAndSlotNow().epoch;
|
|
149
|
+
const performance = await this.store.getProvenPerformance(validator);
|
|
150
|
+
return performance.filter((p)=>p.epoch >= currentEpoch - ttlEpochs).findIndex((p)=>p.missed / p.total >= this.config.slashInactivitySignalTargetPercentage) !== -1;
|
|
151
|
+
}
|
|
67
152
|
/**
|
|
68
153
|
* Process data for two L2 slots ago.
|
|
69
154
|
* Note that we do not process historical data, since we rely on p2p data for processing,
|
|
@@ -203,14 +288,15 @@ export class Sentinel {
|
|
|
203
288
|
/** Push the status for each slot for each validator. */ updateValidators(slot, stats) {
|
|
204
289
|
return this.store.updateValidators(slot, stats);
|
|
205
290
|
}
|
|
206
|
-
/** Computes stats to be returned based on stored data. */ async computeStats() {
|
|
291
|
+
/** Computes stats to be returned based on stored data. */ async computeStats({ fromSlot: _fromSlot, toSlot: _toSlot } = {}) {
|
|
207
292
|
const histories = await this.store.getHistories();
|
|
208
293
|
const slotNow = this.epochCache.getEpochAndSlotNow().slot;
|
|
209
|
-
const fromSlot = (this.lastProcessedSlot ?? slotNow) - BigInt(this.store.getHistoryLength());
|
|
294
|
+
const fromSlot = _fromSlot ?? (this.lastProcessedSlot ?? slotNow) - BigInt(this.store.getHistoryLength());
|
|
295
|
+
const toSlot = _toSlot ?? this.lastProcessedSlot ?? slotNow;
|
|
210
296
|
const result = {};
|
|
211
297
|
for (const [address, history] of Object.entries(histories)){
|
|
212
298
|
const validatorAddress = address;
|
|
213
|
-
result[validatorAddress] = this.computeStatsForValidator(validatorAddress, history, fromSlot);
|
|
299
|
+
result[validatorAddress] = this.computeStatsForValidator(validatorAddress, history, fromSlot, toSlot);
|
|
214
300
|
}
|
|
215
301
|
return {
|
|
216
302
|
stats: result,
|
|
@@ -219,8 +305,9 @@ export class Sentinel {
|
|
|
219
305
|
slotWindow: this.store.getHistoryLength()
|
|
220
306
|
};
|
|
221
307
|
}
|
|
222
|
-
computeStatsForValidator(address, allHistory, fromSlot) {
|
|
223
|
-
|
|
308
|
+
computeStatsForValidator(address, allHistory, fromSlot, toSlot) {
|
|
309
|
+
let history = fromSlot ? allHistory.filter((h)=>h.slot >= fromSlot) : allHistory;
|
|
310
|
+
history = toSlot ? history.filter((h)=>h.slot <= toSlot) : history;
|
|
224
311
|
return {
|
|
225
312
|
address: EthAddress.fromString(address),
|
|
226
313
|
lastProposal: this.computeFromSlot(history.filter((h)=>h.status === 'block-proposed' || h.status === 'block-mined').at(-1)?.slot),
|
package/dest/sentinel/store.d.ts
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
2
|
-
import type { ValidatorStatusHistory, ValidatorStatusInSlot } from '@aztec/stdlib/validators';
|
|
2
|
+
import type { ValidatorStatusHistory, ValidatorStatusInSlot, ValidatorsEpochPerformance } from '@aztec/stdlib/validators';
|
|
3
3
|
export declare class SentinelStore {
|
|
4
4
|
private store;
|
|
5
5
|
private config;
|
|
6
|
-
static readonly SCHEMA_VERSION =
|
|
7
|
-
private readonly
|
|
6
|
+
static readonly SCHEMA_VERSION = 2;
|
|
7
|
+
private readonly historyMap;
|
|
8
|
+
private readonly provenMap;
|
|
8
9
|
constructor(store: AztecAsyncKVStore, config: {
|
|
9
10
|
historyLength: number;
|
|
10
11
|
});
|
|
11
12
|
getHistoryLength(): number;
|
|
13
|
+
updateProvenPerformance(epoch: bigint, performance: ValidatorsEpochPerformance): Promise<void>;
|
|
14
|
+
getProvenPerformance(who: `0x${string}`): Promise<{
|
|
15
|
+
missed: number;
|
|
16
|
+
total: number;
|
|
17
|
+
epoch: bigint;
|
|
18
|
+
}[]>;
|
|
19
|
+
private pushValidatorProvenPerformanceForEpoch;
|
|
12
20
|
updateValidators(slot: bigint, statuses: Record<`0x${string}`, ValidatorStatusInSlot | undefined>): Promise<void>;
|
|
13
21
|
private pushValidatorStatusForSlot;
|
|
14
22
|
getHistories(): Promise<Record<`0x${string}`, ValidatorStatusHistory>>;
|
|
15
23
|
private getHistory;
|
|
24
|
+
private serializePerformance;
|
|
25
|
+
private deserializePerformance;
|
|
16
26
|
private serializeHistory;
|
|
17
27
|
private deserializeHistory;
|
|
18
28
|
private statusToNumber;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sentinel/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sentinel/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAIlC,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;IAW9E,oBAAoB,CAAC,GAAG,EAAE,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;YAKpG,sCAAsC;IA6BvC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,qBAAqB,GAAG,SAAS,CAAC;YAUhG,0BAA0B;IAU3B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC;YAQrE,UAAU;IAKxB,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,gBAAgB;CAgBzB"}
|
package/dest/sentinel/store.js
CHANGED
|
@@ -1,17 +1,65 @@
|
|
|
1
1
|
import { BufferReader, numToUInt8, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
2
|
+
import { isAddress } from 'viem';
|
|
2
3
|
export class SentinelStore {
|
|
3
4
|
store;
|
|
4
5
|
config;
|
|
5
|
-
static SCHEMA_VERSION =
|
|
6
|
-
map
|
|
6
|
+
static SCHEMA_VERSION = 2;
|
|
7
|
+
// a map from validator address to their ValidatorStatusHistory
|
|
8
|
+
historyMap;
|
|
9
|
+
// a map from validator address to their historical proven epoch performance
|
|
10
|
+
// e.g. { validator: [{ epoch: 1, missed: 1, total: 10 }, { epoch: 2, missed: 3, total: 7 }, ...] }
|
|
11
|
+
provenMap;
|
|
7
12
|
constructor(store, config){
|
|
8
13
|
this.store = store;
|
|
9
14
|
this.config = config;
|
|
10
|
-
this.
|
|
15
|
+
this.historyMap = store.openMap('sentinel-validator-status');
|
|
16
|
+
this.provenMap = store.openMap('sentinel-validator-proven');
|
|
11
17
|
}
|
|
12
18
|
getHistoryLength() {
|
|
13
19
|
return this.config.historyLength;
|
|
14
20
|
}
|
|
21
|
+
async updateProvenPerformance(epoch, performance) {
|
|
22
|
+
await this.store.transactionAsync(async ()=>{
|
|
23
|
+
for (const [who, { missed, total }] of Object.entries(performance)){
|
|
24
|
+
if (!isAddress(who)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
await this.pushValidatorProvenPerformanceForEpoch({
|
|
28
|
+
who,
|
|
29
|
+
missed,
|
|
30
|
+
total,
|
|
31
|
+
epoch
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async getProvenPerformance(who) {
|
|
37
|
+
const currentPerformanceBuffer = await this.provenMap.getAsync(who);
|
|
38
|
+
return currentPerformanceBuffer ? this.deserializePerformance(currentPerformanceBuffer) : [];
|
|
39
|
+
}
|
|
40
|
+
async pushValidatorProvenPerformanceForEpoch({ who, missed, total, epoch }) {
|
|
41
|
+
const currentPerformance = await this.getProvenPerformance(who);
|
|
42
|
+
const existingIndex = currentPerformance.findIndex((p)=>p.epoch === epoch);
|
|
43
|
+
if (existingIndex !== -1) {
|
|
44
|
+
currentPerformance[existingIndex] = {
|
|
45
|
+
missed,
|
|
46
|
+
total,
|
|
47
|
+
epoch
|
|
48
|
+
};
|
|
49
|
+
} else {
|
|
50
|
+
currentPerformance.push({
|
|
51
|
+
missed,
|
|
52
|
+
total,
|
|
53
|
+
epoch
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// This should be sorted by epoch, but just in case.
|
|
57
|
+
// Since we keep the size small, this is not a big deal.
|
|
58
|
+
currentPerformance.sort((a, b)=>Number(a.epoch - b.epoch));
|
|
59
|
+
// keep the most recent `historyLength` entries.
|
|
60
|
+
const performanceToKeep = currentPerformance.slice(-this.config.historyLength);
|
|
61
|
+
await this.provenMap.set(who, this.serializePerformance(performanceToKeep));
|
|
62
|
+
}
|
|
15
63
|
async updateValidators(slot, statuses) {
|
|
16
64
|
await this.store.transactionAsync(async ()=>{
|
|
17
65
|
for (const [who, status] of Object.entries(statuses)){
|
|
@@ -30,19 +78,38 @@ export class SentinelStore {
|
|
|
30
78
|
status
|
|
31
79
|
}
|
|
32
80
|
].slice(-this.config.historyLength);
|
|
33
|
-
await this.
|
|
81
|
+
await this.historyMap.set(who, this.serializeHistory(newHistory));
|
|
34
82
|
}
|
|
35
83
|
async getHistories() {
|
|
36
84
|
const histories = {};
|
|
37
|
-
for await (const [address, history] of this.
|
|
85
|
+
for await (const [address, history] of this.historyMap.entriesAsync()){
|
|
38
86
|
histories[address] = this.deserializeHistory(history);
|
|
39
87
|
}
|
|
40
88
|
return histories;
|
|
41
89
|
}
|
|
42
90
|
async getHistory(address) {
|
|
43
|
-
const data = await this.
|
|
91
|
+
const data = await this.historyMap.getAsync(address);
|
|
44
92
|
return data && this.deserializeHistory(data);
|
|
45
93
|
}
|
|
94
|
+
serializePerformance(performance) {
|
|
95
|
+
return serializeToBuffer(performance.map((p)=>[
|
|
96
|
+
numToUInt32BE(Number(p.epoch)),
|
|
97
|
+
numToUInt32BE(p.missed),
|
|
98
|
+
numToUInt32BE(p.total)
|
|
99
|
+
]));
|
|
100
|
+
}
|
|
101
|
+
deserializePerformance(buffer) {
|
|
102
|
+
const reader = new BufferReader(buffer);
|
|
103
|
+
const performance = [];
|
|
104
|
+
while(!reader.isEmpty()){
|
|
105
|
+
performance.push({
|
|
106
|
+
epoch: BigInt(reader.readNumber()),
|
|
107
|
+
missed: reader.readNumber(),
|
|
108
|
+
total: reader.readNumber()
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return performance;
|
|
112
|
+
}
|
|
46
113
|
serializeHistory(history) {
|
|
47
114
|
return serializeToBuffer(history.map((h)=>[
|
|
48
115
|
numToUInt32BE(Number(h.slot)),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/aztec-node",
|
|
3
|
-
"version": "0.87.
|
|
3
|
+
"version": "0.87.3-nightly.20250528",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -61,27 +61,28 @@
|
|
|
61
61
|
]
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@aztec/archiver": "0.87.
|
|
65
|
-
"@aztec/bb-prover": "0.87.
|
|
66
|
-
"@aztec/blob-sink": "0.87.
|
|
67
|
-
"@aztec/constants": "0.87.
|
|
68
|
-
"@aztec/epoch-cache": "0.87.
|
|
69
|
-
"@aztec/ethereum": "0.87.
|
|
70
|
-
"@aztec/foundation": "0.87.
|
|
71
|
-
"@aztec/kv-store": "0.87.
|
|
72
|
-
"@aztec/l1-artifacts": "0.87.
|
|
73
|
-
"@aztec/merkle-tree": "0.87.
|
|
74
|
-
"@aztec/node-lib": "0.87.
|
|
75
|
-
"@aztec/noir-protocol-circuits-types": "0.87.
|
|
76
|
-
"@aztec/p2p": "0.87.
|
|
77
|
-
"@aztec/protocol-contracts": "0.87.
|
|
78
|
-
"@aztec/prover-client": "0.87.
|
|
79
|
-
"@aztec/sequencer-client": "0.87.
|
|
80
|
-
"@aztec/simulator": "0.87.
|
|
81
|
-
"@aztec/
|
|
82
|
-
"@aztec/
|
|
83
|
-
"@aztec/
|
|
84
|
-
"@aztec/
|
|
64
|
+
"@aztec/archiver": "0.87.3-nightly.20250528",
|
|
65
|
+
"@aztec/bb-prover": "0.87.3-nightly.20250528",
|
|
66
|
+
"@aztec/blob-sink": "0.87.3-nightly.20250528",
|
|
67
|
+
"@aztec/constants": "0.87.3-nightly.20250528",
|
|
68
|
+
"@aztec/epoch-cache": "0.87.3-nightly.20250528",
|
|
69
|
+
"@aztec/ethereum": "0.87.3-nightly.20250528",
|
|
70
|
+
"@aztec/foundation": "0.87.3-nightly.20250528",
|
|
71
|
+
"@aztec/kv-store": "0.87.3-nightly.20250528",
|
|
72
|
+
"@aztec/l1-artifacts": "0.87.3-nightly.20250528",
|
|
73
|
+
"@aztec/merkle-tree": "0.87.3-nightly.20250528",
|
|
74
|
+
"@aztec/node-lib": "0.87.3-nightly.20250528",
|
|
75
|
+
"@aztec/noir-protocol-circuits-types": "0.87.3-nightly.20250528",
|
|
76
|
+
"@aztec/p2p": "0.87.3-nightly.20250528",
|
|
77
|
+
"@aztec/protocol-contracts": "0.87.3-nightly.20250528",
|
|
78
|
+
"@aztec/prover-client": "0.87.3-nightly.20250528",
|
|
79
|
+
"@aztec/sequencer-client": "0.87.3-nightly.20250528",
|
|
80
|
+
"@aztec/simulator": "0.87.3-nightly.20250528",
|
|
81
|
+
"@aztec/slasher": "0.87.3-nightly.20250528",
|
|
82
|
+
"@aztec/stdlib": "0.87.3-nightly.20250528",
|
|
83
|
+
"@aztec/telemetry-client": "0.87.3-nightly.20250528",
|
|
84
|
+
"@aztec/validator-client": "0.87.3-nightly.20250528",
|
|
85
|
+
"@aztec/world-state": "0.87.3-nightly.20250528",
|
|
85
86
|
"koa": "^2.16.1",
|
|
86
87
|
"koa-router": "^12.0.0",
|
|
87
88
|
"tslib": "^2.4.0",
|
package/src/aztec-node/config.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib
|
|
|
11
11
|
import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config';
|
|
12
12
|
import { type ProverClientUserConfig, proverClientConfigMappings } from '@aztec/prover-client/config';
|
|
13
13
|
import { type SequencerClientConfig, sequencerClientConfigMappings } from '@aztec/sequencer-client/config';
|
|
14
|
+
import { type SlasherConfig, slasherConfigMappings } from '@aztec/slasher';
|
|
14
15
|
import { type NodeRPCConfig, nodeRpcConfigMappings } from '@aztec/stdlib/config';
|
|
15
16
|
import { type ValidatorClientConfig, validatorClientConfigMappings } from '@aztec/validator-client/config';
|
|
16
17
|
import { type WorldStateConfig, worldStateConfigMappings } from '@aztec/world-state/config';
|
|
@@ -33,7 +34,8 @@ export type AztecNodeConfig = ArchiverConfig &
|
|
|
33
34
|
SentinelConfig &
|
|
34
35
|
SharedNodeConfig &
|
|
35
36
|
GenesisStateConfig &
|
|
36
|
-
NodeRPCConfig &
|
|
37
|
+
NodeRPCConfig &
|
|
38
|
+
SlasherConfig & {
|
|
37
39
|
/** L1 contracts addresses */
|
|
38
40
|
l1Contracts: L1ContractAddresses;
|
|
39
41
|
/** Whether the validator is disabled for this node */
|
|
@@ -52,6 +54,7 @@ export const aztecNodeConfigMappings: ConfigMappingsType<AztecNodeConfig> = {
|
|
|
52
54
|
...sharedNodeConfigMappings,
|
|
53
55
|
...genesisStateConfigMappings,
|
|
54
56
|
...nodeRpcConfigMappings,
|
|
57
|
+
...slasherConfigMappings,
|
|
55
58
|
l1Contracts: {
|
|
56
59
|
description: 'The deployed L1 contract addresses',
|
|
57
60
|
nested: l1ContractAddressesMapping,
|
package/src/aztec-node/server.ts
CHANGED
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
type PUBLIC_DATA_TREE_HEIGHT,
|
|
11
11
|
} from '@aztec/constants';
|
|
12
12
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
type L1ContractAddresses,
|
|
15
|
+
RegistryContract,
|
|
16
|
+
createEthereumChain,
|
|
17
|
+
createExtendedL1Client,
|
|
18
|
+
} from '@aztec/ethereum';
|
|
19
|
+
import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
14
20
|
import { compactArray } from '@aztec/foundation/collection';
|
|
15
21
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
16
22
|
import { Fr } from '@aztec/foundation/fields';
|
|
@@ -30,10 +36,10 @@ import {
|
|
|
30
36
|
GlobalVariableBuilder,
|
|
31
37
|
SequencerClient,
|
|
32
38
|
type SequencerPublisher,
|
|
33
|
-
createSlasherClient,
|
|
34
39
|
createValidatorForAcceptingTxs,
|
|
35
40
|
} from '@aztec/sequencer-client';
|
|
36
41
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
42
|
+
import { EpochPruneWatcher, SlasherClient, type Watcher } from '@aztec/slasher';
|
|
37
43
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
38
44
|
import type { InBlock, L2Block, L2BlockNumber, L2BlockSource, PublishedL2Block } from '@aztec/stdlib/block';
|
|
39
45
|
import type {
|
|
@@ -181,7 +187,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
181
187
|
|
|
182
188
|
const publicClient = createPublicClient({
|
|
183
189
|
chain: ethereumChain.chainInfo,
|
|
184
|
-
transport: fallback(config.l1RpcUrls.map(url => http(url))),
|
|
190
|
+
transport: fallback(config.l1RpcUrls.map((url: string) => http(url))),
|
|
185
191
|
pollingInterval: config.viemPollingIntervalMS,
|
|
186
192
|
});
|
|
187
193
|
|
|
@@ -194,6 +200,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
194
200
|
// Overwrite the passed in vars.
|
|
195
201
|
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
|
|
196
202
|
|
|
203
|
+
const l1Client = createExtendedL1Client(config.l1RpcUrls, config.publisherPrivateKey, ethereumChain.chainInfo);
|
|
204
|
+
const l1TxUtils = new L1TxUtilsWithBlobs(l1Client, log, config);
|
|
205
|
+
|
|
197
206
|
const rollup = getContract({
|
|
198
207
|
address: l1ContractsAddresses.rollupAddress.toString(),
|
|
199
208
|
abi: RollupAbi,
|
|
@@ -247,8 +256,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
247
256
|
// Start p2p. Note that it depends on world state to be running.
|
|
248
257
|
await p2pClient.start();
|
|
249
258
|
|
|
250
|
-
const
|
|
251
|
-
|
|
259
|
+
const watchers: Watcher[] = [];
|
|
260
|
+
|
|
261
|
+
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
262
|
+
if (validatorsSentinel) {
|
|
263
|
+
watchers.push(validatorsSentinel);
|
|
264
|
+
}
|
|
265
|
+
const epochPruneWatcher = new EpochPruneWatcher(archiver, epochCache, config.slashPrunePenalty);
|
|
266
|
+
watchers.push(epochPruneWatcher);
|
|
267
|
+
|
|
268
|
+
const slasherClient = await SlasherClient.new(config, config.l1Contracts, l1TxUtils, watchers, dateProvider);
|
|
269
|
+
await slasherClient.start();
|
|
252
270
|
|
|
253
271
|
const validatorClient = createValidatorClient(config, {
|
|
254
272
|
p2pClient,
|
|
@@ -258,16 +276,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
258
276
|
blockSource: archiver,
|
|
259
277
|
});
|
|
260
278
|
|
|
261
|
-
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
262
|
-
await validatorsSentinel?.start();
|
|
263
|
-
|
|
264
279
|
log.verbose(`All Aztec Node subsystems synced`);
|
|
265
280
|
|
|
266
281
|
// now create the sequencer
|
|
267
282
|
const sequencer = config.disableValidator
|
|
268
283
|
? undefined
|
|
269
284
|
: await SequencerClient.new(config, {
|
|
285
|
+
// if deps were provided, they should override the defaults,
|
|
286
|
+
// or things that we created in this function
|
|
270
287
|
...deps,
|
|
288
|
+
epochCache,
|
|
289
|
+
l1TxUtils,
|
|
271
290
|
validatorClient,
|
|
272
291
|
p2pClient,
|
|
273
292
|
worldStateSynchronizer,
|
|
@@ -528,7 +547,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
|
|
|
528
547
|
public async stop() {
|
|
529
548
|
this.log.info(`Stopping`);
|
|
530
549
|
await this.txQueue.end();
|
|
531
|
-
await this.validatorsSentinel?.stop();
|
|
550
|
+
// await this.validatorsSentinel?.stop(); <- The slasher client will stop this
|
|
532
551
|
await this.sequencer?.stop();
|
|
533
552
|
await this.p2pClient.stop();
|
|
534
553
|
await this.worldStateSynchronizer.stop();
|
package/src/sentinel/factory.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
3
3
|
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
4
4
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
5
5
|
import type { P2PClient } from '@aztec/p2p';
|
|
6
|
+
import type { SlasherConfig } from '@aztec/slasher/config';
|
|
6
7
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
7
8
|
|
|
8
9
|
import type { SentinelConfig } from './config.js';
|
|
@@ -13,7 +14,7 @@ export async function createSentinel(
|
|
|
13
14
|
epochCache: EpochCache,
|
|
14
15
|
archiver: L2BlockSource,
|
|
15
16
|
p2p: P2PClient,
|
|
16
|
-
config: SentinelConfig & DataStoreConfig,
|
|
17
|
+
config: SentinelConfig & DataStoreConfig & SlasherConfig,
|
|
17
18
|
logger = createLogger('node:sentinel'),
|
|
18
19
|
): Promise<Sentinel | undefined> {
|
|
19
20
|
if (!config.sentinelEnabled) {
|
|
@@ -27,5 +28,5 @@ export async function createSentinel(
|
|
|
27
28
|
);
|
|
28
29
|
const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
|
|
29
30
|
const sentinelStore = new SentinelStore(kvStore, { historyLength: storeHistoryLength });
|
|
30
|
-
return new Sentinel(epochCache, archiver, p2p, sentinelStore, logger);
|
|
31
|
+
return new Sentinel(epochCache, archiver, p2p, sentinelStore, config, logger);
|
|
31
32
|
}
|
package/src/sentinel/sentinel.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
5
5
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
6
6
|
import { L2TipsMemoryStore, type L2TipsStore } from '@aztec/kv-store/stores';
|
|
7
7
|
import type { P2PClient } from '@aztec/p2p';
|
|
8
|
+
import type { SlasherConfig, Watcher, WatcherEmitter } from '@aztec/slasher/config';
|
|
9
|
+
import { Offence, WANT_TO_SLASH_EVENT } from '@aztec/slasher/config';
|
|
8
10
|
import {
|
|
9
11
|
type L2BlockSource,
|
|
10
12
|
L2BlockStream,
|
|
@@ -12,18 +14,21 @@ import {
|
|
|
12
14
|
type L2BlockStreamEventHandler,
|
|
13
15
|
getAttestationsFromPublishedL2Block,
|
|
14
16
|
} from '@aztec/stdlib/block';
|
|
15
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
17
|
+
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
16
18
|
import type {
|
|
17
19
|
ValidatorStats,
|
|
18
20
|
ValidatorStatusHistory,
|
|
19
21
|
ValidatorStatusInSlot,
|
|
20
22
|
ValidatorStatusType,
|
|
23
|
+
ValidatorsEpochPerformance,
|
|
21
24
|
ValidatorsStats,
|
|
22
25
|
} from '@aztec/stdlib/validators';
|
|
23
26
|
|
|
27
|
+
import EventEmitter from 'node:events';
|
|
28
|
+
|
|
24
29
|
import { SentinelStore } from './store.js';
|
|
25
30
|
|
|
26
|
-
export class Sentinel implements L2BlockStreamEventHandler {
|
|
31
|
+
export class Sentinel extends (EventEmitter as new () => WatcherEmitter) implements L2BlockStreamEventHandler, Watcher {
|
|
27
32
|
protected runningPromise: RunningPromise;
|
|
28
33
|
protected blockStream!: L2BlockStream;
|
|
29
34
|
protected l2TipsStore: L2TipsStore;
|
|
@@ -38,8 +43,16 @@ export class Sentinel implements L2BlockStreamEventHandler {
|
|
|
38
43
|
protected archiver: L2BlockSource,
|
|
39
44
|
protected p2p: P2PClient,
|
|
40
45
|
protected store: SentinelStore,
|
|
46
|
+
protected config: Pick<
|
|
47
|
+
SlasherConfig,
|
|
48
|
+
| 'slashInactivityCreateTargetPercentage'
|
|
49
|
+
| 'slashInactivityCreatePenalty'
|
|
50
|
+
| 'slashInactivitySignalTargetPercentage'
|
|
51
|
+
| 'slashPayloadTtlSeconds'
|
|
52
|
+
>,
|
|
41
53
|
protected logger = createLogger('node:sentinel'),
|
|
42
54
|
) {
|
|
55
|
+
super();
|
|
43
56
|
this.l2TipsStore = new L2TipsMemoryStore();
|
|
44
57
|
const interval = (epochCache.getL1Constants().ethereumSlotDuration * 1000) / 4;
|
|
45
58
|
this.runningPromise = new RunningPromise(this.work.bind(this), logger, interval);
|
|
@@ -84,7 +97,96 @@ export class Sentinel implements L2BlockStreamEventHandler {
|
|
|
84
97
|
this.slotNumberToBlock.delete(key);
|
|
85
98
|
}
|
|
86
99
|
}
|
|
100
|
+
} else if (event.type === 'chain-proven') {
|
|
101
|
+
await this.handleChainProven(event);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
protected async handleChainProven(event: L2BlockStreamEvent) {
|
|
106
|
+
if (event.type !== 'chain-proven') {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const blockNumber = event.block.number;
|
|
110
|
+
const block = await this.archiver.getBlock(blockNumber);
|
|
111
|
+
if (!block) {
|
|
112
|
+
this.logger.error(`Failed to get block ${blockNumber}`, { block });
|
|
113
|
+
return;
|
|
87
114
|
}
|
|
115
|
+
|
|
116
|
+
const epoch = getEpochAtSlot(block.header.getSlot(), await this.archiver.getL1Constants());
|
|
117
|
+
this.logger.info(`Computing proven performance for epoch ${epoch}`);
|
|
118
|
+
const performance = await this.computeProvenPerformance(epoch);
|
|
119
|
+
this.logger.info(`Proven performance for epoch ${epoch}`, performance);
|
|
120
|
+
|
|
121
|
+
await this.updateProvenPerformance(epoch, performance);
|
|
122
|
+
this.handleProvenPerformance(performance);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected async computeProvenPerformance(epoch: bigint) {
|
|
126
|
+
const headers = await this.archiver.getBlockHeadersForEpoch(epoch);
|
|
127
|
+
const provenSlots = headers.map(h => h.getSlot());
|
|
128
|
+
const fromSlot = provenSlots[0];
|
|
129
|
+
const toSlot = provenSlots[provenSlots.length - 1];
|
|
130
|
+
const { committee } = await this.epochCache.getCommittee(fromSlot);
|
|
131
|
+
const stats = await this.computeStats({ fromSlot, toSlot });
|
|
132
|
+
this.logger.debug(`Stats for epoch ${epoch}`, stats);
|
|
133
|
+
|
|
134
|
+
const performance: ValidatorsEpochPerformance = {};
|
|
135
|
+
for (const validator of Object.keys(stats.stats)) {
|
|
136
|
+
let address;
|
|
137
|
+
try {
|
|
138
|
+
address = EthAddress.fromString(validator);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
this.logger.error(`Invalid validator address ${validator}`, e);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!committee.find(v => v.equals(address))) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
let missed = 0;
|
|
147
|
+
for (const history of stats.stats[validator].history) {
|
|
148
|
+
if (provenSlots.includes(history.slot) && history.status === 'attestation-missed') {
|
|
149
|
+
missed++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
performance[address.toString()] = { missed, total: provenSlots.length };
|
|
153
|
+
}
|
|
154
|
+
return performance;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
protected updateProvenPerformance(epoch: bigint, performance: ValidatorsEpochPerformance) {
|
|
158
|
+
return this.store.updateProvenPerformance(epoch, performance);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
protected handleProvenPerformance(performance: ValidatorsEpochPerformance) {
|
|
162
|
+
const criminals = Object.entries(performance)
|
|
163
|
+
.filter(([_, { missed, total }]) => {
|
|
164
|
+
return missed / total >= this.config.slashInactivityCreateTargetPercentage;
|
|
165
|
+
})
|
|
166
|
+
.map(([address]) => address as `0x${string}`);
|
|
167
|
+
|
|
168
|
+
const amounts = Array(criminals.length).fill(this.config.slashInactivityCreatePenalty);
|
|
169
|
+
const offenses = Array(criminals.length).fill(Offence.INACTIVITY);
|
|
170
|
+
|
|
171
|
+
this.logger.info(`Criminals: ${criminals.length}`, { criminals, amounts, offenses });
|
|
172
|
+
|
|
173
|
+
if (criminals.length > 0) {
|
|
174
|
+
this.emit(WANT_TO_SLASH_EVENT, { validators: criminals, amounts, offenses });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public async shouldSlash(validator: `0x${string}`, _amount: bigint, _offense: Offence): Promise<boolean> {
|
|
179
|
+
const l1Constants = this.epochCache.getL1Constants();
|
|
180
|
+
const ttlL2Slots = this.config.slashPayloadTtlSeconds / l1Constants.slotDuration;
|
|
181
|
+
const ttlEpochs = BigInt(Math.ceil(ttlL2Slots / l1Constants.epochDuration));
|
|
182
|
+
|
|
183
|
+
const currentEpoch = this.epochCache.getEpochAndSlotNow().epoch;
|
|
184
|
+
const performance = await this.store.getProvenPerformance(validator);
|
|
185
|
+
return (
|
|
186
|
+
performance
|
|
187
|
+
.filter(p => p.epoch >= currentEpoch - ttlEpochs)
|
|
188
|
+
.findIndex(p => p.missed / p.total >= this.config.slashInactivitySignalTargetPercentage) !== -1
|
|
189
|
+
);
|
|
88
190
|
}
|
|
89
191
|
|
|
90
192
|
/**
|
|
@@ -232,14 +334,18 @@ export class Sentinel implements L2BlockStreamEventHandler {
|
|
|
232
334
|
}
|
|
233
335
|
|
|
234
336
|
/** Computes stats to be returned based on stored data. */
|
|
235
|
-
public async computeStats(
|
|
337
|
+
public async computeStats({
|
|
338
|
+
fromSlot: _fromSlot,
|
|
339
|
+
toSlot: _toSlot,
|
|
340
|
+
}: { fromSlot?: bigint; toSlot?: bigint } = {}): Promise<ValidatorsStats> {
|
|
236
341
|
const histories = await this.store.getHistories();
|
|
237
342
|
const slotNow = this.epochCache.getEpochAndSlotNow().slot;
|
|
238
|
-
const fromSlot = (this.lastProcessedSlot ?? slotNow) - BigInt(this.store.getHistoryLength());
|
|
343
|
+
const fromSlot = _fromSlot ?? (this.lastProcessedSlot ?? slotNow) - BigInt(this.store.getHistoryLength());
|
|
344
|
+
const toSlot = _toSlot ?? this.lastProcessedSlot ?? slotNow;
|
|
239
345
|
const result: Record<`0x${string}`, ValidatorStats> = {};
|
|
240
346
|
for (const [address, history] of Object.entries(histories)) {
|
|
241
347
|
const validatorAddress = address as `0x${string}`;
|
|
242
|
-
result[validatorAddress] = this.computeStatsForValidator(validatorAddress, history, fromSlot);
|
|
348
|
+
result[validatorAddress] = this.computeStatsForValidator(validatorAddress, history, fromSlot, toSlot);
|
|
243
349
|
}
|
|
244
350
|
return {
|
|
245
351
|
stats: result,
|
|
@@ -253,8 +359,10 @@ export class Sentinel implements L2BlockStreamEventHandler {
|
|
|
253
359
|
address: `0x${string}`,
|
|
254
360
|
allHistory: ValidatorStatusHistory,
|
|
255
361
|
fromSlot?: bigint,
|
|
362
|
+
toSlot?: bigint,
|
|
256
363
|
): ValidatorStats {
|
|
257
|
-
|
|
364
|
+
let history = fromSlot ? allHistory.filter(h => h.slot >= fromSlot) : allHistory;
|
|
365
|
+
history = toSlot ? history.filter(h => h.slot <= toSlot) : history;
|
|
258
366
|
return {
|
|
259
367
|
address: EthAddress.fromString(address),
|
|
260
368
|
lastProposal: this.computeFromSlot(
|
package/src/sentinel/store.ts
CHANGED
|
@@ -1,23 +1,80 @@
|
|
|
1
1
|
import { BufferReader, numToUInt8, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
2
2
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ValidatorStatusHistory,
|
|
5
|
+
ValidatorStatusInSlot,
|
|
6
|
+
ValidatorsEpochPerformance,
|
|
7
|
+
} from '@aztec/stdlib/validators';
|
|
8
|
+
|
|
9
|
+
import { isAddress } from 'viem';
|
|
4
10
|
|
|
5
11
|
export class SentinelStore {
|
|
6
|
-
public static readonly SCHEMA_VERSION =
|
|
12
|
+
public static readonly SCHEMA_VERSION = 2;
|
|
13
|
+
|
|
14
|
+
// a map from validator address to their ValidatorStatusHistory
|
|
15
|
+
private readonly historyMap: AztecAsyncMap<`0x${string}`, Buffer>;
|
|
7
16
|
|
|
8
|
-
|
|
17
|
+
// a map from validator address to their historical proven epoch performance
|
|
18
|
+
// e.g. { validator: [{ epoch: 1, missed: 1, total: 10 }, { epoch: 2, missed: 3, total: 7 }, ...] }
|
|
19
|
+
private readonly provenMap: AztecAsyncMap<`0x${string}`, Buffer>;
|
|
9
20
|
|
|
10
21
|
constructor(
|
|
11
22
|
private store: AztecAsyncKVStore,
|
|
12
23
|
private config: { historyLength: number },
|
|
13
24
|
) {
|
|
14
|
-
this.
|
|
25
|
+
this.historyMap = store.openMap('sentinel-validator-status');
|
|
26
|
+
this.provenMap = store.openMap('sentinel-validator-proven');
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
public getHistoryLength() {
|
|
18
30
|
return this.config.historyLength;
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
public async updateProvenPerformance(epoch: bigint, performance: ValidatorsEpochPerformance) {
|
|
34
|
+
await this.store.transactionAsync(async () => {
|
|
35
|
+
for (const [who, { missed, total }] of Object.entries(performance)) {
|
|
36
|
+
if (!isAddress(who)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
await this.pushValidatorProvenPerformanceForEpoch({ who, missed, total, epoch });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public async getProvenPerformance(who: `0x${string}`): Promise<{ missed: number; total: number; epoch: bigint }[]> {
|
|
45
|
+
const currentPerformanceBuffer = await this.provenMap.getAsync(who);
|
|
46
|
+
return currentPerformanceBuffer ? this.deserializePerformance(currentPerformanceBuffer) : [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private async pushValidatorProvenPerformanceForEpoch({
|
|
50
|
+
who,
|
|
51
|
+
missed,
|
|
52
|
+
total,
|
|
53
|
+
epoch,
|
|
54
|
+
}: {
|
|
55
|
+
who: `0x${string}`;
|
|
56
|
+
missed: number;
|
|
57
|
+
total: number;
|
|
58
|
+
epoch: bigint;
|
|
59
|
+
}) {
|
|
60
|
+
const currentPerformance = await this.getProvenPerformance(who);
|
|
61
|
+
const existingIndex = currentPerformance.findIndex(p => p.epoch === epoch);
|
|
62
|
+
if (existingIndex !== -1) {
|
|
63
|
+
currentPerformance[existingIndex] = { missed, total, epoch };
|
|
64
|
+
} else {
|
|
65
|
+
currentPerformance.push({ missed, total, epoch });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// This should be sorted by epoch, but just in case.
|
|
69
|
+
// Since we keep the size small, this is not a big deal.
|
|
70
|
+
currentPerformance.sort((a, b) => Number(a.epoch - b.epoch));
|
|
71
|
+
|
|
72
|
+
// keep the most recent `historyLength` entries.
|
|
73
|
+
const performanceToKeep = currentPerformance.slice(-this.config.historyLength);
|
|
74
|
+
|
|
75
|
+
await this.provenMap.set(who, this.serializePerformance(performanceToKeep));
|
|
76
|
+
}
|
|
77
|
+
|
|
21
78
|
public async updateValidators(slot: bigint, statuses: Record<`0x${string}`, ValidatorStatusInSlot | undefined>) {
|
|
22
79
|
await this.store.transactionAsync(async () => {
|
|
23
80
|
for (const [who, status] of Object.entries(statuses)) {
|
|
@@ -35,22 +92,41 @@ export class SentinelStore {
|
|
|
35
92
|
) {
|
|
36
93
|
const currentHistory = (await this.getHistory(who)) ?? [];
|
|
37
94
|
const newHistory = [...currentHistory, { slot, status }].slice(-this.config.historyLength);
|
|
38
|
-
await this.
|
|
95
|
+
await this.historyMap.set(who, this.serializeHistory(newHistory));
|
|
39
96
|
}
|
|
40
97
|
|
|
41
98
|
public async getHistories(): Promise<Record<`0x${string}`, ValidatorStatusHistory>> {
|
|
42
99
|
const histories: Record<`0x${string}`, ValidatorStatusHistory> = {};
|
|
43
|
-
for await (const [address, history] of this.
|
|
100
|
+
for await (const [address, history] of this.historyMap.entriesAsync()) {
|
|
44
101
|
histories[address] = this.deserializeHistory(history);
|
|
45
102
|
}
|
|
46
103
|
return histories;
|
|
47
104
|
}
|
|
48
105
|
|
|
49
106
|
private async getHistory(address: `0x${string}`): Promise<ValidatorStatusHistory | undefined> {
|
|
50
|
-
const data = await this.
|
|
107
|
+
const data = await this.historyMap.getAsync(address);
|
|
51
108
|
return data && this.deserializeHistory(data);
|
|
52
109
|
}
|
|
53
110
|
|
|
111
|
+
private serializePerformance(performance: { missed: number; total: number; epoch: bigint }[]): Buffer {
|
|
112
|
+
return serializeToBuffer(
|
|
113
|
+
performance.map(p => [numToUInt32BE(Number(p.epoch)), numToUInt32BE(p.missed), numToUInt32BE(p.total)]),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private deserializePerformance(buffer: Buffer): { missed: number; total: number; epoch: bigint }[] {
|
|
118
|
+
const reader = new BufferReader(buffer);
|
|
119
|
+
const performance: { missed: number; total: number; epoch: bigint }[] = [];
|
|
120
|
+
while (!reader.isEmpty()) {
|
|
121
|
+
performance.push({
|
|
122
|
+
epoch: BigInt(reader.readNumber()),
|
|
123
|
+
missed: reader.readNumber(),
|
|
124
|
+
total: reader.readNumber(),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return performance;
|
|
128
|
+
}
|
|
129
|
+
|
|
54
130
|
private serializeHistory(history: ValidatorStatusHistory): Buffer {
|
|
55
131
|
return serializeToBuffer(
|
|
56
132
|
history.map(h => [numToUInt32BE(Number(h.slot)), numToUInt8(this.statusToNumber(h.status))]),
|