@aztec/slasher 0.0.1-commit.b655e406 → 0.0.1-commit.c2595eba
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/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +16 -16
- package/dest/empire_slasher_client.d.ts +6 -5
- package/dest/empire_slasher_client.d.ts.map +1 -1
- package/dest/empire_slasher_client.js +1 -1
- package/dest/factory/create_facade.d.ts +3 -2
- package/dest/factory/create_facade.d.ts.map +1 -1
- package/dest/factory/create_facade.js +1 -1
- package/dest/factory/create_implementation.d.ts +3 -3
- package/dest/factory/create_implementation.d.ts.map +1 -1
- package/dest/factory/get_settings.d.ts +2 -2
- package/dest/factory/get_settings.d.ts.map +1 -1
- package/dest/factory/index.d.ts +1 -1
- package/dest/generated/slasher-defaults.d.ts +19 -0
- package/dest/generated/slasher-defaults.d.ts.map +1 -0
- package/dest/generated/slasher-defaults.js +19 -0
- package/dest/index.d.ts +1 -1
- package/dest/null_slasher_client.d.ts +3 -2
- package/dest/null_slasher_client.d.ts.map +1 -1
- package/dest/slash_offenses_collector.d.ts +1 -1
- package/dest/slash_offenses_collector.d.ts.map +1 -1
- package/dest/slash_offenses_collector.js +5 -1
- package/dest/slash_round_monitor.d.ts +5 -4
- package/dest/slash_round_monitor.d.ts.map +1 -1
- package/dest/slasher_client_facade.d.ts +4 -3
- package/dest/slasher_client_facade.d.ts.map +1 -1
- package/dest/slasher_client_interface.d.ts +3 -2
- package/dest/slasher_client_interface.d.ts.map +1 -1
- package/dest/stores/offenses_store.d.ts +1 -1
- package/dest/stores/offenses_store.d.ts.map +1 -1
- package/dest/stores/payloads_store.d.ts +2 -2
- package/dest/stores/payloads_store.d.ts.map +1 -1
- package/dest/stores/schema_version.d.ts +1 -1
- package/dest/tally_slasher_client.d.ts +5 -9
- package/dest/tally_slasher_client.d.ts.map +1 -1
- package/dest/tally_slasher_client.js +11 -3
- package/dest/test/dummy_watcher.d.ts +1 -1
- package/dest/test/dummy_watcher.d.ts.map +1 -1
- package/dest/watcher.d.ts +1 -1
- package/dest/watcher.d.ts.map +1 -1
- package/dest/watchers/attestations_block_watcher.d.ts +7 -6
- package/dest/watchers/attestations_block_watcher.d.ts.map +1 -1
- package/dest/watchers/attestations_block_watcher.js +42 -35
- package/dest/watchers/epoch_prune_watcher.d.ts +7 -6
- package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -1
- package/dest/watchers/epoch_prune_watcher.js +36 -13
- package/package.json +17 -14
- package/src/config.ts +17 -16
- package/src/empire_slasher_client.ts +6 -5
- package/src/factory/create_facade.ts +3 -2
- package/src/factory/create_implementation.ts +1 -1
- package/src/factory/get_settings.ts +1 -1
- package/src/generated/slasher-defaults.ts +21 -0
- package/src/null_slasher_client.ts +2 -1
- package/src/slash_offenses_collector.ts +5 -1
- package/src/slash_round_monitor.ts +3 -2
- package/src/slasher_client_facade.ts +3 -2
- package/src/slasher_client_interface.ts +2 -1
- package/src/tally_slasher_client.ts +21 -7
- package/src/watcher.ts +1 -1
- package/src/watchers/attestations_block_watcher.ts +59 -45
- package/src/watchers/epoch_prune_watcher.ts +51 -22
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dummy_watcher.d.ts","sourceRoot":"","sources":["../../src/test/dummy_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAIrE,OAAO,EAAuB,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC
|
|
1
|
+
{"version":3,"file":"dummy_watcher.d.ts","sourceRoot":"","sources":["../../src/test/dummy_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAIrE,OAAO,EAAuB,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;;AAE7G,qBAAa,YAAa,SAAQ,iBAA2C,YAAW,OAAO;IACtF,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,QAAI;IAEhD,KAAK,kBAEX;IAEM,IAAI,kBAEV;IAEM,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,QAE1C;CACF"}
|
package/dest/watcher.d.ts
CHANGED
|
@@ -18,4 +18,4 @@ export type Watcher = WatcherEmitter & {
|
|
|
18
18
|
stop?: () => Promise<void>;
|
|
19
19
|
updateConfig: (config: Partial<SlasherConfig>) => void;
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=
|
|
21
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dhdGNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQzNELE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRXJELE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVqRCxlQUFPLE1BQU0sbUJBQW1CLGlCQUEyQixDQUFDO0FBRTVELE1BQU0sV0FBVyxlQUFlO0lBQzlCLFNBQVMsRUFBRSxVQUFVLENBQUM7SUFDdEIsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUNmLFdBQVcsRUFBRSxXQUFXLENBQUM7SUFDekIsV0FBVyxFQUFFLE1BQU0sQ0FBQztDQUNyQjtBQUdELE1BQU0sV0FBVyxlQUFlO0lBQzlCLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRSxlQUFlLEVBQUUsS0FBSyxJQUFJLENBQUM7Q0FDMUQ7QUFFRCxNQUFNLE1BQU0sY0FBYyxHQUFHLGlCQUFpQixDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBRWhFLE1BQU0sTUFBTSxPQUFPLEdBQUcsY0FBYyxHQUFHO0lBQ3JDLEtBQUssQ0FBQyxFQUFFLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzVCLElBQUksQ0FBQyxFQUFFLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzNCLFlBQVksRUFBRSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsYUFBYSxDQUFDLEtBQUssSUFBSSxDQUFDO0NBQ3hELENBQUMifQ==
|
package/dest/watcher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,mBAAmB,iBAA2B,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,UAAU,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,eAAe;IAC9B,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,MAAM,cAAc,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAEhE,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG;IACrC,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC;CACxD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import { type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
2
|
+
import { type InvalidCheckpointDetectedEvent, type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
3
3
|
import type { SlasherConfig } from '../config.js';
|
|
4
4
|
import { type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
5
5
|
declare const AttestationsBlockWatcherConfigKeys: readonly ["slashAttestDescendantOfInvalidPenalty", "slashProposeInvalidAttestationsPenalty"];
|
|
@@ -15,19 +15,20 @@ export declare class AttestationsBlockWatcher extends AttestationsBlockWatcher_b
|
|
|
15
15
|
private l2BlockSource;
|
|
16
16
|
private epochCache;
|
|
17
17
|
private log;
|
|
18
|
-
private
|
|
18
|
+
private maxInvalidCheckpoints;
|
|
19
19
|
private invalidArchiveRoots;
|
|
20
20
|
private config;
|
|
21
|
-
private
|
|
21
|
+
private boundHandleInvalidCheckpoint;
|
|
22
22
|
constructor(l2BlockSource: L2BlockSourceEventEmitter, epochCache: EpochCache, config: AttestationsBlockWatcherConfig);
|
|
23
23
|
updateConfig(newConfig: Partial<AttestationsBlockWatcherConfig>): void;
|
|
24
24
|
start(): Promise<void>;
|
|
25
25
|
stop(): Promise<void>;
|
|
26
|
-
|
|
26
|
+
/** Event handler for invalid checkpoints as reported by the archiver. Public for testing purposes. */
|
|
27
|
+
handleInvalidCheckpoint(event: InvalidCheckpointDetectedEvent): void;
|
|
27
28
|
private slashAttestorsOnAncestorInvalid;
|
|
28
29
|
private slashProposer;
|
|
29
30
|
private getOffenseFromInvalidationReason;
|
|
30
|
-
private
|
|
31
|
+
private addInvalidCheckpoint;
|
|
31
32
|
}
|
|
32
33
|
export {};
|
|
33
|
-
//# sourceMappingURL=
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXR0ZXN0YXRpb25zX2Jsb2NrX3dhdGNoZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy93YXRjaGVycy9hdHRlc3RhdGlvbnNfYmxvY2tfd2F0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFJaEQsT0FBTyxFQUNMLEtBQUssOEJBQThCLEVBQ25DLEtBQUsseUJBQXlCLEVBRy9CLE1BQU0scUJBQXFCLENBQUM7QUFNN0IsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2xELE9BQU8sRUFBNkMsS0FBSyxPQUFPLEVBQUUsS0FBSyxjQUFjLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFN0csUUFBQSxNQUFNLGtDQUFrQyw4RkFHOUIsQ0FBQztBQUVYLEtBQUssOEJBQThCLEdBQUcsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLE9BQU8sa0NBQWtDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDOztBQUUvRzs7Ozs7R0FLRztBQUNILHFCQUFhLHdCQUF5QixTQUFRLDZCQUEyQyxZQUFXLE9BQU87SUF1QnZHLE9BQU8sQ0FBQyxhQUFhO0lBQ3JCLE9BQU8sQ0FBQyxVQUFVO0lBdkJwQixPQUFPLENBQUMsR0FBRyxDQUFzRDtJQUdqRSxPQUFPLENBQUMscUJBQXFCLENBQU87SUFHcEMsT0FBTyxDQUFDLG1CQUFtQixDQUEwQjtJQUVyRCxPQUFPLENBQUMsTUFBTSxDQUFpQztJQUUvQyxPQUFPLENBQUMsNEJBQTRCLENBU2xDO0lBRUYsWUFDVSxhQUFhLEVBQUUseUJBQXlCLEVBQ3hDLFVBQVUsRUFBRSxVQUFVLEVBQzlCLE1BQU0sRUFBRSw4QkFBOEIsRUFLdkM7SUFFTSxZQUFZLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyw4QkFBOEIsQ0FBQyxRQUdyRTtJQUVNLEtBQUssa0JBTVg7SUFFTSxJQUFJLGtCQU1WO0lBRUQsc0dBQXNHO0lBQy9GLHVCQUF1QixDQUFDLEtBQUssRUFBRSw4QkFBOEIsR0FBRyxJQUFJLENBdUIxRTtJQUVELE9BQU8sQ0FBQywrQkFBK0I7SUEyQnZDLE9BQU8sQ0FBQyxhQUFhO0lBa0NyQixPQUFPLENBQUMsZ0NBQWdDO0lBYXhDLE9BQU8sQ0FBQyxvQkFBb0I7Q0FTN0IifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attestations_block_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/attestations_block_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"attestations_block_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/attestations_block_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIhD,OAAO,EACL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAG/B,MAAM,qBAAqB,CAAC;AAM7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,kCAAkC,8FAG9B,CAAC;AAEX,KAAK,8BAA8B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,kCAAkC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;AAE/G;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,6BAA2C,YAAW,OAAO;IAuBvG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,UAAU;IAvBpB,OAAO,CAAC,GAAG,CAAsD;IAGjE,OAAO,CAAC,qBAAqB,CAAO;IAGpC,OAAO,CAAC,mBAAmB,CAA0B;IAErD,OAAO,CAAC,MAAM,CAAiC;IAE/C,OAAO,CAAC,4BAA4B,CASlC;IAEF,YACU,aAAa,EAAE,yBAAyB,EACxC,UAAU,EAAE,UAAU,EAC9B,MAAM,EAAE,8BAA8B,EAKvC;IAEM,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,8BAA8B,CAAC,QAGrE;IAEM,KAAK,kBAMX;IAEM,IAAI,kBAMV;IAED,sGAAsG;IAC/F,uBAAuB,CAAC,KAAK,EAAE,8BAA8B,GAAG,IAAI,CAuB1E;IAED,OAAO,CAAC,+BAA+B;IA2BvC,OAAO,CAAC,aAAa;IAkCrB,OAAO,CAAC,gCAAgC;IAaxC,OAAO,CAAC,oBAAoB;CAS7B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
2
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
@@ -17,18 +18,18 @@ const AttestationsBlockWatcherConfigKeys = [
|
|
|
17
18
|
l2BlockSource;
|
|
18
19
|
epochCache;
|
|
19
20
|
log;
|
|
20
|
-
// Only keep track of the last N invalid
|
|
21
|
-
|
|
21
|
+
// Only keep track of the last N invalid checkpoints
|
|
22
|
+
maxInvalidCheckpoints;
|
|
22
23
|
// All invalid archive roots seen
|
|
23
24
|
invalidArchiveRoots;
|
|
24
25
|
config;
|
|
25
|
-
|
|
26
|
+
boundHandleInvalidCheckpoint;
|
|
26
27
|
constructor(l2BlockSource, epochCache, config){
|
|
27
|
-
super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.log = createLogger('attestations-block-watcher'), this.
|
|
28
|
+
super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.log = createLogger('attestations-block-watcher'), this.maxInvalidCheckpoints = 100, this.invalidArchiveRoots = new Set(), this.boundHandleInvalidCheckpoint = (event)=>{
|
|
28
29
|
try {
|
|
29
|
-
this.
|
|
30
|
+
this.handleInvalidCheckpoint(event);
|
|
30
31
|
} catch (err) {
|
|
31
|
-
this.log.error('Error handling invalid
|
|
32
|
+
this.log.error('Error handling invalid checkpoint', err, {
|
|
32
33
|
...event.validationResult,
|
|
33
34
|
reason: event.validationResult.reason
|
|
34
35
|
});
|
|
@@ -42,39 +43,39 @@ const AttestationsBlockWatcherConfigKeys = [
|
|
|
42
43
|
this.log.verbose('AttestationsBlockWatcher config updated', this.config);
|
|
43
44
|
}
|
|
44
45
|
start() {
|
|
45
|
-
this.l2BlockSource.on(L2BlockSourceEvents.
|
|
46
|
+
this.l2BlockSource.events.on(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, this.boundHandleInvalidCheckpoint);
|
|
46
47
|
return Promise.resolve();
|
|
47
48
|
}
|
|
48
49
|
stop() {
|
|
49
|
-
this.l2BlockSource.removeListener(L2BlockSourceEvents.
|
|
50
|
+
this.l2BlockSource.events.removeListener(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, this.boundHandleInvalidCheckpoint);
|
|
50
51
|
return Promise.resolve();
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
+
/** Event handler for invalid checkpoints as reported by the archiver. Public for testing purposes. */ handleInvalidCheckpoint(event) {
|
|
53
54
|
const { validationResult } = event;
|
|
54
|
-
const
|
|
55
|
-
// Check if we already have processed this
|
|
56
|
-
if (this.invalidArchiveRoots.has(
|
|
57
|
-
this.log.trace(`Already processed invalid
|
|
55
|
+
const checkpoint = validationResult.checkpoint;
|
|
56
|
+
// Check if we already have processed this checkpoint, archiver may emit the same event multiple times
|
|
57
|
+
if (this.invalidArchiveRoots.has(checkpoint.archive.toString())) {
|
|
58
|
+
this.log.trace(`Already processed invalid checkpoint ${checkpoint.checkpointNumber}`);
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
|
-
this.log.verbose(`Detected invalid
|
|
61
|
-
...
|
|
61
|
+
this.log.verbose(`Detected invalid checkpoint ${checkpoint.checkpointNumber}`, {
|
|
62
|
+
...checkpoint,
|
|
62
63
|
reason: validationResult.valid === false ? validationResult.reason : 'unknown'
|
|
63
64
|
});
|
|
64
|
-
// Store the invalid
|
|
65
|
-
this.
|
|
66
|
-
// Slash the proposer of the invalid
|
|
65
|
+
// Store the invalid checkpoint
|
|
66
|
+
this.addInvalidCheckpoint(event.validationResult.checkpoint);
|
|
67
|
+
// Slash the proposer of the invalid checkpoint
|
|
67
68
|
this.slashProposer(event.validationResult);
|
|
68
|
-
// Check if the parent of this
|
|
69
|
+
// Check if the parent of this checkpoint is invalid as well, if so, we will slash its attestors as well
|
|
69
70
|
this.slashAttestorsOnAncestorInvalid(event.validationResult);
|
|
70
71
|
}
|
|
71
72
|
slashAttestorsOnAncestorInvalid(validationResult) {
|
|
72
|
-
const
|
|
73
|
-
const parentArchive =
|
|
73
|
+
const checkpoint = validationResult.checkpoint;
|
|
74
|
+
const parentArchive = checkpoint.lastArchive.toString();
|
|
74
75
|
if (this.invalidArchiveRoots.has(parentArchive)) {
|
|
75
76
|
const attestors = validationResult.attestors;
|
|
76
|
-
this.log.info(`Want to slash attestors of
|
|
77
|
-
...
|
|
77
|
+
this.log.info(`Want to slash attestors of checkpoint ${checkpoint.checkpointNumber} built on invalid checkpoint`, {
|
|
78
|
+
...checkpoint,
|
|
78
79
|
...attestors,
|
|
79
80
|
parentArchive
|
|
80
81
|
});
|
|
@@ -82,17 +83,23 @@ const AttestationsBlockWatcherConfigKeys = [
|
|
|
82
83
|
validator: attestor,
|
|
83
84
|
amount: this.config.slashAttestDescendantOfInvalidPenalty,
|
|
84
85
|
offenseType: OffenseType.ATTESTED_DESCENDANT_OF_INVALID,
|
|
85
|
-
epochOrSlot: BigInt(
|
|
86
|
+
epochOrSlot: BigInt(SlotNumber(checkpoint.slotNumber))
|
|
86
87
|
})));
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
slashProposer(validationResult) {
|
|
90
|
-
const { reason,
|
|
91
|
-
const
|
|
92
|
-
const slot =
|
|
93
|
-
const
|
|
91
|
+
const { reason, checkpoint } = validationResult;
|
|
92
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
93
|
+
const slot = checkpoint.slotNumber;
|
|
94
|
+
const epochCommitteeInfo = {
|
|
95
|
+
committee: validationResult.committee,
|
|
96
|
+
seed: validationResult.seed,
|
|
97
|
+
epoch: validationResult.epoch,
|
|
98
|
+
isEscapeHatchOpen: false
|
|
99
|
+
};
|
|
100
|
+
const proposer = this.epochCache.getProposerFromEpochCommittee(epochCommitteeInfo, slot);
|
|
94
101
|
if (!proposer) {
|
|
95
|
-
this.log.warn(`No proposer found for
|
|
102
|
+
this.log.warn(`No proposer found for checkpoint ${checkpointNumber} at slot ${slot}`);
|
|
96
103
|
return;
|
|
97
104
|
}
|
|
98
105
|
const offense = this.getOffenseFromInvalidationReason(reason);
|
|
@@ -101,10 +108,10 @@ const AttestationsBlockWatcherConfigKeys = [
|
|
|
101
108
|
validator: proposer,
|
|
102
109
|
amount,
|
|
103
110
|
offenseType: offense,
|
|
104
|
-
epochOrSlot: slot
|
|
111
|
+
epochOrSlot: BigInt(slot)
|
|
105
112
|
};
|
|
106
|
-
this.log.info(`Want to slash proposer of
|
|
107
|
-
...
|
|
113
|
+
this.log.info(`Want to slash proposer of checkpoint ${checkpointNumber} due to ${reason}`, {
|
|
114
|
+
...checkpoint,
|
|
108
115
|
...args
|
|
109
116
|
});
|
|
110
117
|
this.emit(WANT_TO_SLASH_EVENT, [
|
|
@@ -124,10 +131,10 @@ const AttestationsBlockWatcherConfigKeys = [
|
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
}
|
|
127
|
-
|
|
128
|
-
this.invalidArchiveRoots.add(
|
|
134
|
+
addInvalidCheckpoint(checkpoint) {
|
|
135
|
+
this.invalidArchiveRoots.add(checkpoint.archive.toString());
|
|
129
136
|
// Prune old entries if we exceed the maximum
|
|
130
|
-
if (this.invalidArchiveRoots.size > this.
|
|
137
|
+
if (this.invalidArchiveRoots.size > this.maxInvalidCheckpoints) {
|
|
131
138
|
const oldestKey = this.invalidArchiveRoots.keys().next().value;
|
|
132
139
|
this.invalidArchiveRoots.delete(oldestKey);
|
|
133
140
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
3
|
import { L2Block, type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
3
|
-
import type {
|
|
4
|
-
import type
|
|
4
|
+
import type { ICheckpointsBuilder, ITxProvider, MerkleTreeWriteOperations, SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
5
|
+
import { type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
5
6
|
import { type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
6
7
|
declare const EpochPruneWatcherPenaltiesConfigKeys: readonly ["slashPrunePenalty", "slashDataWithholdingPenalty"];
|
|
7
8
|
type EpochPruneWatcherPenalties = Pick<SlasherConfig, (typeof EpochPruneWatcherPenaltiesConfigKeys)[number]>;
|
|
@@ -17,11 +18,11 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
|
|
|
17
18
|
private l1ToL2MessageSource;
|
|
18
19
|
private epochCache;
|
|
19
20
|
private txProvider;
|
|
20
|
-
private
|
|
21
|
+
private checkpointsBuilder;
|
|
21
22
|
private log;
|
|
22
23
|
private boundHandlePruneL2Blocks;
|
|
23
24
|
private penalties;
|
|
24
|
-
constructor(l2BlockSource: L2BlockSourceEventEmitter, l1ToL2MessageSource: L1ToL2MessageSource, epochCache: EpochCache, txProvider: Pick<ITxProvider, 'getAvailableTxs'>,
|
|
25
|
+
constructor(l2BlockSource: L2BlockSourceEventEmitter, l1ToL2MessageSource: L1ToL2MessageSource, epochCache: EpochCache, txProvider: Pick<ITxProvider, 'getAvailableTxs'>, checkpointsBuilder: ICheckpointsBuilder, penalties: EpochPruneWatcherPenalties);
|
|
25
26
|
start(): Promise<void>;
|
|
26
27
|
stop(): Promise<void>;
|
|
27
28
|
updateConfig(config: Partial<SlasherConfig>): void;
|
|
@@ -29,9 +30,9 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
|
|
|
29
30
|
private emitSlashForEpoch;
|
|
30
31
|
private processPruneL2Blocks;
|
|
31
32
|
validateBlocks(blocks: L2Block[]): Promise<void>;
|
|
32
|
-
validateBlock(blockFromL1: L2Block, fork: MerkleTreeWriteOperations): Promise<void>;
|
|
33
|
+
validateBlock(blockFromL1: L2Block, previousCheckpointOutHashes: Fr[], fork: MerkleTreeWriteOperations): Promise<void>;
|
|
33
34
|
private getValidatorsForEpoch;
|
|
34
35
|
private validatorsToSlashingArgs;
|
|
35
36
|
}
|
|
36
37
|
export {};
|
|
37
|
-
//# sourceMappingURL=
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfcHJ1bmVfd2F0Y2hlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dhdGNoZXJzL2Vwb2NoX3BydW5lX3dhdGNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBR2hELE9BQU8sS0FBSyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXpELE9BQU8sRUFFTCxPQUFPLEVBQ1AsS0FBSyx5QkFBeUIsRUFHL0IsTUFBTSxxQkFBcUIsQ0FBQztBQUU3QixPQUFPLEtBQUssRUFDVixtQkFBbUIsRUFDbkIsV0FBVyxFQUNYLHlCQUF5QixFQUN6QixhQUFhLEVBQ2QsTUFBTSxpQ0FBaUMsQ0FBQztBQUN6QyxPQUFPLEVBQUUsS0FBSyxtQkFBbUIsRUFBNEIsTUFBTSx5QkFBeUIsQ0FBQztBQVk3RixPQUFPLEVBQTZDLEtBQUssT0FBTyxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTdHLFFBQUEsTUFBTSxvQ0FBb0MsK0RBQWdFLENBQUM7QUFFM0csS0FBSywwQkFBMEIsR0FBRyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUMsT0FBTyxvQ0FBb0MsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7O0FBRTdHOzs7OztHQUtHO0FBQ0gscUJBQWEsaUJBQWtCLFNBQVEsc0JBQTJDLFlBQVcsT0FBTztJQVNoRyxPQUFPLENBQUMsYUFBYTtJQUNyQixPQUFPLENBQUMsbUJBQW1CO0lBQzNCLE9BQU8sQ0FBQyxVQUFVO0lBQ2xCLE9BQU8sQ0FBQyxVQUFVO0lBQ2xCLE9BQU8sQ0FBQyxrQkFBa0I7SUFaNUIsT0FBTyxDQUFDLEdBQUcsQ0FBK0M7SUFHMUQsT0FBTyxDQUFDLHdCQUF3QixDQUF1QztJQUV2RSxPQUFPLENBQUMsU0FBUyxDQUE2QjtJQUU5QyxZQUNVLGFBQWEsRUFBRSx5QkFBeUIsRUFDeEMsbUJBQW1CLEVBQUUsbUJBQW1CLEVBQ3hDLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLFVBQVUsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLGlCQUFpQixDQUFDLEVBQ2hELGtCQUFrQixFQUFFLG1CQUFtQixFQUMvQyxTQUFTLEVBQUUsMEJBQTBCLEVBT3RDO0lBRU0sS0FBSyxrQkFHWDtJQUVNLElBQUksa0JBR1Y7SUFFTSxZQUFZLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsR0FBRyxJQUFJLENBR3hEO0lBRUQsT0FBTyxDQUFDLG1CQUFtQjtZQU9iLGlCQUFpQjtZQVdqQixvQkFBb0I7SUF3QnJCLGNBQWMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQWtCNUQ7SUFFWSxhQUFhLENBQ3hCLFdBQVcsRUFBRSxPQUFPLEVBQ3BCLDJCQUEyQixFQUFFLEVBQUUsRUFBRSxFQUNqQyxJQUFJLEVBQUUseUJBQXlCLEdBQzlCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0E4Q2Y7WUFFYSxxQkFBcUI7SUFTbkMsT0FBTyxDQUFDLHdCQUF3QjtDQWdCakMifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAEL,OAAO,
|
|
1
|
+
{"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEzD,OAAO,EAEL,OAAO,EACP,KAAK,yBAAyB,EAG/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACX,yBAAyB,EACzB,aAAa,EACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,mBAAmB,EAA4B,MAAM,yBAAyB,CAAC;AAY7F,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,oCAAoC,+DAAgE,CAAC;AAE3G,KAAK,0BAA0B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,oCAAoC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;AAE7G;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,sBAA2C,YAAW,OAAO;IAShG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,kBAAkB;IAZ5B,OAAO,CAAC,GAAG,CAA+C;IAG1D,OAAO,CAAC,wBAAwB,CAAuC;IAEvE,OAAO,CAAC,SAAS,CAA6B;IAE9C,YACU,aAAa,EAAE,yBAAyB,EACxC,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAChD,kBAAkB,EAAE,mBAAmB,EAC/C,SAAS,EAAE,0BAA0B,EAOtC;IAEM,KAAK,kBAGX;IAEM,IAAI,kBAGV;IAEM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAGxD;IAED,OAAO,CAAC,mBAAmB;YAOb,iBAAiB;YAWjB,oBAAoB;IAwBrB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5D;IAEY,aAAa,CACxB,WAAW,EAAE,OAAO,EACpB,2BAA2B,EAAE,EAAE,EAAE,EACjC,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA8Cf;YAEa,qBAAqB;IASnC,OAAO,CAAC,wBAAwB;CAgBjC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
2
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
4
5
|
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
6
|
+
import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
|
|
5
7
|
import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
|
|
6
8
|
import { ReExFailedTxsError, ReExStateMismatchError, TransactionsNotAvailableError, ValidatorError } from '@aztec/stdlib/validators';
|
|
7
9
|
import EventEmitter from 'node:events';
|
|
@@ -20,22 +22,22 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
|
20
22
|
l1ToL2MessageSource;
|
|
21
23
|
epochCache;
|
|
22
24
|
txProvider;
|
|
23
|
-
|
|
25
|
+
checkpointsBuilder;
|
|
24
26
|
log;
|
|
25
27
|
// Store bound function reference for proper listener removal
|
|
26
28
|
boundHandlePruneL2Blocks;
|
|
27
29
|
penalties;
|
|
28
|
-
constructor(l2BlockSource, l1ToL2MessageSource, epochCache, txProvider,
|
|
29
|
-
super(), this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.epochCache = epochCache, this.txProvider = txProvider, this.
|
|
30
|
+
constructor(l2BlockSource, l1ToL2MessageSource, epochCache, txProvider, checkpointsBuilder, penalties){
|
|
31
|
+
super(), this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.epochCache = epochCache, this.txProvider = txProvider, this.checkpointsBuilder = checkpointsBuilder, this.log = createLogger('epoch-prune-watcher'), this.boundHandlePruneL2Blocks = this.handlePruneL2Blocks.bind(this);
|
|
30
32
|
this.penalties = pick(penalties, ...EpochPruneWatcherPenaltiesConfigKeys);
|
|
31
33
|
this.log.verbose(`EpochPruneWatcher initialized with penalties: valid epoch pruned=${penalties.slashPrunePenalty} data withholding=${penalties.slashDataWithholdingPenalty}`);
|
|
32
34
|
}
|
|
33
35
|
start() {
|
|
34
|
-
this.l2BlockSource.on(L2BlockSourceEvents.
|
|
36
|
+
this.l2BlockSource.events.on(L2BlockSourceEvents.L2PruneUnproven, this.boundHandlePruneL2Blocks);
|
|
35
37
|
return Promise.resolve();
|
|
36
38
|
}
|
|
37
39
|
stop() {
|
|
38
|
-
this.l2BlockSource.removeListener(L2BlockSourceEvents.
|
|
40
|
+
this.l2BlockSource.events.removeListener(L2BlockSourceEvents.L2PruneUnproven, this.boundHandlePruneL2Blocks);
|
|
39
41
|
return Promise.resolve();
|
|
40
42
|
}
|
|
41
43
|
updateConfig(config) {
|
|
@@ -54,14 +56,14 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
|
54
56
|
this.log.warn(`No validators found for epoch ${epochNumber} (cannot slash for ${getOffenseTypeName(offense)})`);
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
|
-
const args = this.validatorsToSlashingArgs(validators, offense,
|
|
59
|
+
const args = this.validatorsToSlashingArgs(validators, offense, epochNumber);
|
|
58
60
|
this.log.verbose(`Created slash for ${getOffenseTypeName(offense)} at epoch ${epochNumber}`, args);
|
|
59
61
|
this.emit(WANT_TO_SLASH_EVENT, args);
|
|
60
62
|
}
|
|
61
63
|
async processPruneL2Blocks(blocks, epochNumber) {
|
|
62
64
|
try {
|
|
63
65
|
const l1Constants = this.epochCache.getL1Constants();
|
|
64
|
-
const epochBlocks = blocks.filter((b)=>getEpochAtSlot(b.
|
|
66
|
+
const epochBlocks = blocks.filter((b)=>getEpochAtSlot(b.header.getSlot(), l1Constants) === epochNumber);
|
|
65
67
|
this.log.info(`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`, {
|
|
66
68
|
blocks: epochBlocks.map((b)=>b.toBlockInfo())
|
|
67
69
|
});
|
|
@@ -83,16 +85,25 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
|
83
85
|
if (blocks.length === 0) {
|
|
84
86
|
return;
|
|
85
87
|
}
|
|
86
|
-
|
|
88
|
+
let previousCheckpointOutHashes = [];
|
|
89
|
+
const fork = await this.checkpointsBuilder.getFork(BlockNumber(blocks[0].header.globalVariables.blockNumber - 1));
|
|
87
90
|
try {
|
|
88
91
|
for (const block of blocks){
|
|
89
|
-
await this.validateBlock(block, fork);
|
|
92
|
+
await this.validateBlock(block, previousCheckpointOutHashes, fork);
|
|
93
|
+
// TODO(mbps): This assumes one block per checkpoint, which is only true for now.
|
|
94
|
+
const checkpointOutHash = computeCheckpointOutHash([
|
|
95
|
+
block.body.txEffects.map((tx)=>tx.l2ToL1Msgs)
|
|
96
|
+
]);
|
|
97
|
+
previousCheckpointOutHashes = [
|
|
98
|
+
...previousCheckpointOutHashes,
|
|
99
|
+
checkpointOutHash
|
|
100
|
+
];
|
|
90
101
|
}
|
|
91
102
|
} finally{
|
|
92
103
|
await fork.close();
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
|
-
async validateBlock(blockFromL1, fork) {
|
|
106
|
+
async validateBlock(blockFromL1, previousCheckpointOutHashes, fork) {
|
|
96
107
|
this.log.debug(`Validating pruned block ${blockFromL1.header.globalVariables.blockNumber}`);
|
|
97
108
|
const txHashes = blockFromL1.body.txEffects.map((txEffect)=>txEffect.txHash);
|
|
98
109
|
// We load txs from the mempool directly, since the TxCollector running in the background has already been
|
|
@@ -102,8 +113,20 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
|
102
113
|
if (missingTxs && missingTxs.length > 0) {
|
|
103
114
|
throw new TransactionsNotAvailableError(missingTxs);
|
|
104
115
|
}
|
|
105
|
-
const
|
|
106
|
-
const
|
|
116
|
+
const checkpointNumber = CheckpointNumber.fromBlockNumber(blockFromL1.number);
|
|
117
|
+
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
118
|
+
const gv = blockFromL1.header.globalVariables;
|
|
119
|
+
const constants = {
|
|
120
|
+
chainId: gv.chainId,
|
|
121
|
+
version: gv.version,
|
|
122
|
+
slotNumber: gv.slotNumber,
|
|
123
|
+
coinbase: gv.coinbase,
|
|
124
|
+
feeRecipient: gv.feeRecipient,
|
|
125
|
+
gasFees: gv.gasFees
|
|
126
|
+
};
|
|
127
|
+
// Use checkpoint builder to validate the block
|
|
128
|
+
const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes, fork, this.log.getBindings());
|
|
129
|
+
const { block, failedTxs, numTxs } = await checkpointBuilder.buildBlock(txs, gv.blockNumber, gv.timestamp, {});
|
|
107
130
|
if (numTxs !== txs.length) {
|
|
108
131
|
// This should be detected by state mismatch, but this makes it easier to debug.
|
|
109
132
|
throw new ValidatorError(`Built block with ${numTxs} txs, expected ${txs.length}`);
|
|
@@ -129,7 +152,7 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
|
129
152
|
validator: v,
|
|
130
153
|
amount: penalty,
|
|
131
154
|
offenseType,
|
|
132
|
-
epochOrSlot
|
|
155
|
+
epochOrSlot: BigInt(epochOrSlot)
|
|
133
156
|
}));
|
|
134
157
|
}
|
|
135
158
|
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/slasher",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.c2595eba",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
7
7
|
"./config": "./dest/config.js"
|
|
8
8
|
},
|
|
9
9
|
"inherits": [
|
|
10
|
-
"../package.common.json"
|
|
10
|
+
"../package.common.json",
|
|
11
|
+
"./package.local.json"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
|
-
"build": "yarn clean && tsc
|
|
14
|
-
"build:dev": "tsc
|
|
14
|
+
"build": "yarn clean && ../scripts/tsc.sh",
|
|
15
|
+
"build:dev": "../scripts/tsc.sh --watch",
|
|
15
16
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
16
17
|
"bb": "node --no-warnings ./dest/bb/index.js",
|
|
17
|
-
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
18
|
+
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}",
|
|
19
|
+
"generate": "./scripts/generate.sh"
|
|
18
20
|
},
|
|
19
21
|
"jest": {
|
|
20
22
|
"moduleNameMapper": {
|
|
@@ -54,24 +56,25 @@
|
|
|
54
56
|
]
|
|
55
57
|
},
|
|
56
58
|
"dependencies": {
|
|
57
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
58
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
59
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
60
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
61
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
62
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
63
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
59
|
+
"@aztec/epoch-cache": "0.0.1-commit.c2595eba",
|
|
60
|
+
"@aztec/ethereum": "0.0.1-commit.c2595eba",
|
|
61
|
+
"@aztec/foundation": "0.0.1-commit.c2595eba",
|
|
62
|
+
"@aztec/kv-store": "0.0.1-commit.c2595eba",
|
|
63
|
+
"@aztec/l1-artifacts": "0.0.1-commit.c2595eba",
|
|
64
|
+
"@aztec/stdlib": "0.0.1-commit.c2595eba",
|
|
65
|
+
"@aztec/telemetry-client": "0.0.1-commit.c2595eba",
|
|
64
66
|
"source-map-support": "^0.5.21",
|
|
65
67
|
"tslib": "^2.4.0",
|
|
66
|
-
"viem": "npm:@
|
|
68
|
+
"viem": "npm:@aztec/viem@2.38.2",
|
|
67
69
|
"zod": "^3.23.8"
|
|
68
70
|
},
|
|
69
71
|
"devDependencies": {
|
|
70
|
-
"@aztec/aztec.js": "0.0.1-commit.
|
|
72
|
+
"@aztec/aztec.js": "0.0.1-commit.c2595eba",
|
|
71
73
|
"@jest/globals": "^30.0.0",
|
|
72
74
|
"@types/jest": "^30.0.0",
|
|
73
75
|
"@types/node": "^22.15.17",
|
|
74
76
|
"@types/source-map-support": "^0.5.10",
|
|
77
|
+
"@typescript/native-preview": "7.0.0-dev.20260113.1",
|
|
75
78
|
"jest": "^30.0.0",
|
|
76
79
|
"jest-mock-extended": "^4.0.0",
|
|
77
80
|
"ts-node": "^10.9.1",
|
package/src/config.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DefaultL1ContractsConfig } from '@aztec/ethereum';
|
|
2
1
|
import type { ConfigMappingsType } from '@aztec/foundation/config';
|
|
3
2
|
import {
|
|
4
3
|
bigintConfigHelper,
|
|
@@ -9,27 +8,29 @@ import {
|
|
|
9
8
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
10
9
|
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
11
10
|
|
|
11
|
+
import { slasherDefaultEnv } from './generated/slasher-defaults.js';
|
|
12
|
+
|
|
12
13
|
export type { SlasherConfig };
|
|
13
14
|
|
|
14
15
|
export const DefaultSlasherConfig: SlasherConfig = {
|
|
15
16
|
slashOverridePayload: undefined,
|
|
16
|
-
slashMinPenaltyPercentage:
|
|
17
|
-
slashMaxPenaltyPercentage:
|
|
17
|
+
slashMinPenaltyPercentage: slasherDefaultEnv.SLASH_MIN_PENALTY_PERCENTAGE,
|
|
18
|
+
slashMaxPenaltyPercentage: slasherDefaultEnv.SLASH_MAX_PENALTY_PERCENTAGE,
|
|
18
19
|
slashValidatorsAlways: [], // Empty by default
|
|
19
20
|
slashValidatorsNever: [], // Empty by default
|
|
20
|
-
slashPrunePenalty:
|
|
21
|
-
slashDataWithholdingPenalty:
|
|
22
|
-
slashInactivityTargetPercentage:
|
|
23
|
-
slashInactivityConsecutiveEpochThreshold:
|
|
24
|
-
slashBroadcastedInvalidBlockPenalty:
|
|
25
|
-
slashInactivityPenalty:
|
|
26
|
-
slashProposeInvalidAttestationsPenalty:
|
|
27
|
-
slashAttestDescendantOfInvalidPenalty:
|
|
28
|
-
slashUnknownPenalty:
|
|
29
|
-
slashOffenseExpirationRounds:
|
|
30
|
-
slashMaxPayloadSize:
|
|
31
|
-
slashGracePeriodL2Slots:
|
|
32
|
-
slashExecuteRoundsLookBack:
|
|
21
|
+
slashPrunePenalty: BigInt(slasherDefaultEnv.SLASH_PRUNE_PENALTY),
|
|
22
|
+
slashDataWithholdingPenalty: BigInt(slasherDefaultEnv.SLASH_DATA_WITHHOLDING_PENALTY),
|
|
23
|
+
slashInactivityTargetPercentage: slasherDefaultEnv.SLASH_INACTIVITY_TARGET_PERCENTAGE,
|
|
24
|
+
slashInactivityConsecutiveEpochThreshold: slasherDefaultEnv.SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD,
|
|
25
|
+
slashBroadcastedInvalidBlockPenalty: BigInt(slasherDefaultEnv.SLASH_INVALID_BLOCK_PENALTY),
|
|
26
|
+
slashInactivityPenalty: BigInt(slasherDefaultEnv.SLASH_INACTIVITY_PENALTY),
|
|
27
|
+
slashProposeInvalidAttestationsPenalty: BigInt(slasherDefaultEnv.SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY),
|
|
28
|
+
slashAttestDescendantOfInvalidPenalty: BigInt(slasherDefaultEnv.SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY),
|
|
29
|
+
slashUnknownPenalty: BigInt(slasherDefaultEnv.SLASH_UNKNOWN_PENALTY),
|
|
30
|
+
slashOffenseExpirationRounds: slasherDefaultEnv.SLASH_OFFENSE_EXPIRATION_ROUNDS,
|
|
31
|
+
slashMaxPayloadSize: slasherDefaultEnv.SLASH_MAX_PAYLOAD_SIZE,
|
|
32
|
+
slashGracePeriodL2Slots: slasherDefaultEnv.SLASH_GRACE_PERIOD_L2_SLOTS,
|
|
33
|
+
slashExecuteRoundsLookBack: slasherDefaultEnv.SLASH_EXECUTE_ROUNDS_LOOK_BACK,
|
|
33
34
|
slashSelfAllowed: false,
|
|
34
35
|
};
|
|
35
36
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { EmpireSlashingProposerContract, RollupContract, SlasherContract } from '@aztec/ethereum';
|
|
1
|
+
import { EmpireSlashingProposerContract, RollupContract, SlasherContract } from '@aztec/ethereum/contracts';
|
|
2
2
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
3
|
+
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
4
|
import { compactArray, filterAsync, maxBy, pick } from '@aztec/foundation/collection';
|
|
4
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -369,7 +370,7 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
369
370
|
* @param slotNumber - The current slot number
|
|
370
371
|
* @returns The actions to take
|
|
371
372
|
*/
|
|
372
|
-
public async getProposerActions(slotNumber:
|
|
373
|
+
public async getProposerActions(slotNumber: SlotNumber): Promise<ProposerSlashAction[]> {
|
|
373
374
|
const [executeAction, proposePayloadActions] = await Promise.all([
|
|
374
375
|
this.getExecutePayloadAction(slotNumber),
|
|
375
376
|
this.getProposePayloadActions(slotNumber),
|
|
@@ -379,7 +380,7 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
/** Returns an execute payload action if there are any payloads ready to be executed */
|
|
382
|
-
protected async getExecutePayloadAction(slotNumber:
|
|
383
|
+
protected async getExecutePayloadAction(slotNumber: SlotNumber): Promise<ProposerSlashAction | undefined> {
|
|
383
384
|
const { round } = this.roundMonitor.getRoundForSlot(slotNumber);
|
|
384
385
|
const toRemove: PayloadWithRound[] = [];
|
|
385
386
|
|
|
@@ -430,7 +431,7 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
430
431
|
}
|
|
431
432
|
|
|
432
433
|
/** Returns a vote or create payload action based on payload scoring */
|
|
433
|
-
protected async getProposePayloadActions(slotNumber:
|
|
434
|
+
protected async getProposePayloadActions(slotNumber: SlotNumber): Promise<ProposerSlashAction[]> {
|
|
434
435
|
// Compute what round we are in based on the slot number
|
|
435
436
|
const { round, votingSlot } = this.roundMonitor.getRoundForSlot(slotNumber);
|
|
436
437
|
const { slashingRoundSize: roundSize, slashingQuorumSize: quorumSize } = this.settings;
|
|
@@ -473,7 +474,7 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
473
474
|
// Find the best existing payload. We filter out those that have no chance of winning given how many voting
|
|
474
475
|
// slots are left in the round, and then filter by those we agree with.
|
|
475
476
|
const feasiblePayloads = existingPayloads.filter(
|
|
476
|
-
p => BigInt(quorumSize) - p.votes <= BigInt(roundSize) - votingSlot,
|
|
477
|
+
p => BigInt(quorumSize) - p.votes <= BigInt(roundSize) - BigInt(votingSlot),
|
|
477
478
|
);
|
|
478
479
|
const requiredOffenses = await this.getPendingUncontroversialOffensesForRound(round);
|
|
479
480
|
const agreedPayloads = await filterAsync(feasiblePayloads, p => this.agreeWithPayload(p, round, requiredOffenses));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import type { L1ReaderConfig, ViemClient } from '@aztec/ethereum';
|
|
3
2
|
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
3
|
+
import type { L1ReaderConfig } from '@aztec/ethereum/l1-reader';
|
|
4
|
+
import type { ViemClient } from '@aztec/ethereum/types';
|
|
4
5
|
import { unique } from '@aztec/foundation/collection';
|
|
5
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -30,7 +31,7 @@ export async function createSlasherFacade(
|
|
|
30
31
|
throw new Error('Cannot initialize SlasherClient without a Rollup address');
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
const kvStore = await createStore('slasher', SCHEMA_VERSION, config,
|
|
34
|
+
const kvStore = await createStore('slasher', SCHEMA_VERSION, config, logger.getBindings());
|
|
34
35
|
const rollup = new RollupContract(l1Client, l1Contracts.rollupAddress);
|
|
35
36
|
|
|
36
37
|
const slashValidatorsNever = config.slashSelfAllowed
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import type { ViemClient } from '@aztec/ethereum';
|
|
3
2
|
import {
|
|
4
3
|
EmpireSlashingProposerContract,
|
|
5
4
|
RollupContract,
|
|
6
5
|
TallySlashingProposerContract,
|
|
7
6
|
} from '@aztec/ethereum/contracts';
|
|
7
|
+
import type { ViemClient } from '@aztec/ethereum/types';
|
|
8
8
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
9
9
|
import { createLogger } from '@aztec/foundation/log';
|
|
10
10
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Auto-generated from spartan/environments/network-defaults.yml
|
|
2
|
+
// Do not edit manually - run yarn generate to regenerate
|
|
3
|
+
|
|
4
|
+
/** Default slasher configuration values from network-defaults.yml */
|
|
5
|
+
export const slasherDefaultEnv = {
|
|
6
|
+
SLASH_MIN_PENALTY_PERCENTAGE: 0.5,
|
|
7
|
+
SLASH_MAX_PENALTY_PERCENTAGE: 2,
|
|
8
|
+
SLASH_OFFENSE_EXPIRATION_ROUNDS: 4,
|
|
9
|
+
SLASH_MAX_PAYLOAD_SIZE: 50,
|
|
10
|
+
SLASH_EXECUTE_ROUNDS_LOOK_BACK: 4,
|
|
11
|
+
SLASH_PRUNE_PENALTY: 10000000000000000000,
|
|
12
|
+
SLASH_DATA_WITHHOLDING_PENALTY: 10000000000000000000,
|
|
13
|
+
SLASH_INACTIVITY_TARGET_PERCENTAGE: 0.9,
|
|
14
|
+
SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD: 1,
|
|
15
|
+
SLASH_INACTIVITY_PENALTY: 10000000000000000000,
|
|
16
|
+
SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY: 10000000000000000000,
|
|
17
|
+
SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY: 10000000000000000000,
|
|
18
|
+
SLASH_UNKNOWN_PENALTY: 10000000000000000000,
|
|
19
|
+
SLASH_INVALID_BLOCK_PENALTY: 10000000000000000000,
|
|
20
|
+
SLASH_GRACE_PERIOD_L2_SLOTS: 0,
|
|
21
|
+
} as const;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import type { Offense, ProposerSlashAction, SlashPayloadRound } from '@aztec/stdlib/slashing';
|
|
2
3
|
|
|
3
4
|
import type { SlasherConfig } from './config.js';
|
|
@@ -30,7 +31,7 @@ export class NullSlasherClient implements SlasherClientInterface {
|
|
|
30
31
|
this.config = { ...this.config, ...config };
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
public getProposerActions(_slotNumber:
|
|
34
|
+
public getProposerActions(_slotNumber: SlotNumber): Promise<ProposerSlashAction[]> {
|
|
34
35
|
return Promise.resolve([]);
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -85,7 +85,11 @@ export class SlashOffensesCollector {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
this.log.info(`Adding pending offense for validator ${arg.validator}`,
|
|
88
|
+
this.log.info(`Adding pending offense for validator ${arg.validator}`, {
|
|
89
|
+
...pendingOffense,
|
|
90
|
+
epochOrSlot: pendingOffense.epochOrSlot.toString(),
|
|
91
|
+
amount: pendingOffense.amount.toString(),
|
|
92
|
+
});
|
|
89
93
|
await this.offensesStore.addPendingOffense(pendingOffense);
|
|
90
94
|
}
|
|
91
95
|
}
|