@aztec/slasher 2.0.0-rc.8 → 2.0.2-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/empire_slasher_client.d.ts +3 -2
- package/dest/empire_slasher_client.d.ts.map +1 -1
- package/dest/empire_slasher_client.js +4 -3
- package/dest/factory/create_implementation.js +6 -4
- package/dest/slash_offenses_collector.d.ts.map +1 -1
- package/dest/slash_offenses_collector.js +1 -2
- package/dest/slasher_client_facade.d.ts.map +1 -1
- package/dest/slasher_client_facade.js +1 -0
- package/dest/tally_slasher_client.d.ts +3 -2
- package/dest/tally_slasher_client.d.ts.map +1 -1
- package/dest/tally_slasher_client.js +4 -3
- package/dest/test/dummy_watcher.d.ts +11 -0
- package/dest/test/dummy_watcher.d.ts.map +1 -0
- package/dest/test/dummy_watcher.js +14 -0
- package/dest/watcher.d.ts +2 -0
- package/dest/watcher.d.ts.map +1 -1
- package/dest/watchers/attestations_block_watcher.d.ts +5 -2
- package/dest/watchers/attestations_block_watcher.d.ts.map +1 -1
- package/dest/watchers/attestations_block_watcher.js +30 -21
- package/dest/watchers/epoch_prune_watcher.d.ts +5 -6
- package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -1
- package/dest/watchers/epoch_prune_watcher.js +15 -3
- package/package.json +9 -9
- package/src/empire_slasher_client.ts +3 -3
- package/src/factory/create_implementation.ts +5 -0
- package/src/slash_offenses_collector.ts +1 -2
- package/src/slasher_client_facade.ts +1 -0
- package/src/tally_slasher_client.ts +3 -3
- package/src/test/dummy_watcher.ts +21 -0
- package/src/watcher.ts +3 -0
- package/src/watchers/attestations_block_watcher.ts +37 -25
- package/src/watchers/epoch_prune_watcher.ts +22 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmpireSlashingProposerContract, RollupContract } from '@aztec/ethereum';
|
|
1
|
+
import { EmpireSlashingProposerContract, RollupContract, SlasherContract } from '@aztec/ethereum';
|
|
2
2
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
3
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
4
4
|
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
@@ -84,6 +84,7 @@ export declare class EmpireSlasherClient implements ProposerSlashActionProvider,
|
|
|
84
84
|
private settings;
|
|
85
85
|
private slashFactoryContract;
|
|
86
86
|
private slashingProposer;
|
|
87
|
+
private slasher;
|
|
87
88
|
private rollup;
|
|
88
89
|
private dateProvider;
|
|
89
90
|
private offensesStore;
|
|
@@ -94,7 +95,7 @@ export declare class EmpireSlasherClient implements ProposerSlashActionProvider,
|
|
|
94
95
|
private overridePayloadActive;
|
|
95
96
|
private offensesCollector;
|
|
96
97
|
private roundMonitor;
|
|
97
|
-
constructor(config: EmpireSlasherConfig, settings: EmpireSlasherSettings, slashFactoryContract: SlashFactoryContract, slashingProposer: EmpireSlashingProposerContract, rollup: RollupContract, watchers: Watcher[], dateProvider: DateProvider, offensesStore: SlasherOffensesStore, payloadsStore: SlasherPayloadsStore, log?: import("@aztec/foundation/log").Logger);
|
|
98
|
+
constructor(config: EmpireSlasherConfig, settings: EmpireSlasherSettings, slashFactoryContract: SlashFactoryContract, slashingProposer: EmpireSlashingProposerContract, slasher: SlasherContract, rollup: RollupContract, watchers: Watcher[], dateProvider: DateProvider, offensesStore: SlasherOffensesStore, payloadsStore: SlasherPayloadsStore, log?: import("@aztec/foundation/log").Logger);
|
|
98
99
|
start(): Promise<void>;
|
|
99
100
|
/**
|
|
100
101
|
* Allows consumers to stop the instance of the slasher client.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empire_slasher_client.d.ts","sourceRoot":"","sources":["../src/empire_slasher_client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"empire_slasher_client.d.ts","sourceRoot":"","sources":["../src/empire_slasher_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,6 +65,7 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
65
65
|
settings;
|
|
66
66
|
slashFactoryContract;
|
|
67
67
|
slashingProposer;
|
|
68
|
+
slasher;
|
|
68
69
|
rollup;
|
|
69
70
|
dateProvider;
|
|
70
71
|
offensesStore;
|
|
@@ -75,11 +76,12 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
75
76
|
overridePayloadActive;
|
|
76
77
|
offensesCollector;
|
|
77
78
|
roundMonitor;
|
|
78
|
-
constructor(config, settings, slashFactoryContract, slashingProposer, rollup, watchers, dateProvider, offensesStore, payloadsStore, log = createLogger('slasher:empire')){
|
|
79
|
+
constructor(config, settings, slashFactoryContract, slashingProposer, slasher, rollup, watchers, dateProvider, offensesStore, payloadsStore, log = createLogger('slasher:empire')){
|
|
79
80
|
this.config = config;
|
|
80
81
|
this.settings = settings;
|
|
81
82
|
this.slashFactoryContract = slashFactoryContract;
|
|
82
83
|
this.slashingProposer = slashingProposer;
|
|
84
|
+
this.slasher = slasher;
|
|
83
85
|
this.rollup = rollup;
|
|
84
86
|
this.dateProvider = dateProvider;
|
|
85
87
|
this.offensesStore = offensesStore;
|
|
@@ -310,8 +312,7 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
310
312
|
continue;
|
|
311
313
|
}
|
|
312
314
|
// Check if the slash payload is vetoed
|
|
313
|
-
const
|
|
314
|
-
const isVetoed = await slasherContract.isPayloadVetoed(payload.payload);
|
|
315
|
+
const isVetoed = await this.slasher.isPayloadVetoed(payload.payload);
|
|
315
316
|
if (isVetoed) {
|
|
316
317
|
this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed, skipping execution`);
|
|
317
318
|
toRemove.push(payload);
|
|
@@ -25,7 +25,7 @@ async function createEmpireSlasher(config, rollup, slashingProposer, slashFactor
|
|
|
25
25
|
if (slashingProposer.type !== 'empire') {
|
|
26
26
|
throw new Error('Slashing proposer contract is not of type Empire');
|
|
27
27
|
}
|
|
28
|
-
const [slashingExecutionDelayInRounds, slashingPayloadLifetimeInRounds, slashingRoundSize, slashingQuorumSize, epochDuration, proofSubmissionEpochs, l1GenesisTime, slotDuration, l1StartBlock] = await Promise.all([
|
|
28
|
+
const [slashingExecutionDelayInRounds, slashingPayloadLifetimeInRounds, slashingRoundSize, slashingQuorumSize, epochDuration, proofSubmissionEpochs, l1GenesisTime, slotDuration, l1StartBlock, slasher] = await Promise.all([
|
|
29
29
|
slashingProposer.getExecutionDelayInRounds(),
|
|
30
30
|
slashingProposer.getLifetimeInRounds(),
|
|
31
31
|
slashingProposer.getRoundSize(),
|
|
@@ -34,7 +34,8 @@ async function createEmpireSlasher(config, rollup, slashingProposer, slashFactor
|
|
|
34
34
|
rollup.getProofSubmissionEpochs(),
|
|
35
35
|
rollup.getL1GenesisTime(),
|
|
36
36
|
rollup.getSlotDuration(),
|
|
37
|
-
rollup.getL1StartBlock()
|
|
37
|
+
rollup.getL1StartBlock(),
|
|
38
|
+
rollup.getSlasherContract()
|
|
38
39
|
]);
|
|
39
40
|
const settings = {
|
|
40
41
|
slashingExecutionDelayInRounds: Number(slashingExecutionDelayInRounds),
|
|
@@ -56,16 +57,17 @@ async function createEmpireSlasher(config, rollup, slashingProposer, slashFactor
|
|
|
56
57
|
...settings,
|
|
57
58
|
slashOffenseExpirationRounds: config.slashOffenseExpirationRounds
|
|
58
59
|
});
|
|
59
|
-
return new EmpireSlasherClient(config, settings, slashFactoryContract, slashingProposer, rollup, watchers, dateProvider, offensesStore, payloadsStore, logger);
|
|
60
|
+
return new EmpireSlasherClient(config, settings, slashFactoryContract, slashingProposer, slasher, rollup, watchers, dateProvider, offensesStore, payloadsStore, logger);
|
|
60
61
|
}
|
|
61
62
|
async function createTallySlasher(config, rollup, slashingProposer, watchers, dateProvider, epochCache, kvStore, logger = createLogger('slasher')) {
|
|
62
63
|
if (slashingProposer.type !== 'tally') {
|
|
63
64
|
throw new Error('Slashing proposer contract is not of type tally');
|
|
64
65
|
}
|
|
65
66
|
const settings = await getTallySlasherSettings(rollup, slashingProposer);
|
|
67
|
+
const slasher = await rollup.getSlasherContract();
|
|
66
68
|
const offensesStore = new SlasherOffensesStore(kvStore, {
|
|
67
69
|
...settings,
|
|
68
70
|
slashOffenseExpirationRounds: config.slashOffenseExpirationRounds
|
|
69
71
|
});
|
|
70
|
-
return new TallySlasherClient(config, settings, slashingProposer, rollup, watchers, epochCache, dateProvider, offensesStore, logger);
|
|
72
|
+
return new TallySlasherClient(config, settings, slashingProposer, slasher, rollup, watchers, epochCache, dateProvider, offensesStore, logger);
|
|
71
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slash_offenses_collector.d.ts","sourceRoot":"","sources":["../src/slash_offenses_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAgB,KAAK,iBAAiB,EAAqB,MAAM,wBAAwB,CAAC;AAEjG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAuB,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC,CAAC;AACpG,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CACnD,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,GAAG;IAAE,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAAE,CACrG,CAAC;AAEF;;;;GAIG;AACH,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsB;gBAGpC,MAAM,EAAE,4BAA4B,EACpC,QAAQ,EAAE,8BAA8B,EACxC,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,oBAAoB,EACnC,GAAG,yCAA6C;IAG5D,KAAK;IAeL,IAAI;IAWX;;;;OAIG;IACU,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE;IA+BtD;;;OAGG;IACU,cAAc,CAAC,KAAK,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"slash_offenses_collector.d.ts","sourceRoot":"","sources":["../src/slash_offenses_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAgB,KAAK,iBAAiB,EAAqB,MAAM,wBAAwB,CAAC;AAEjG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAuB,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC,CAAC;AACpG,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CACnD,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,GAAG;IAAE,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAAE,CACrG,CAAC;AAEF;;;;GAIG;AACH,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsB;gBAGpC,MAAM,EAAE,4BAA4B,EACpC,QAAQ,EAAE,8BAA8B,EACxC,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,oBAAoB,EACnC,GAAG,yCAA6C;IAG5D,KAAK;IAeL,IAAI;IAWX;;;;OAIG;IACU,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE;IA+BtD;;;OAGG;IACU,cAAc,CAAC,KAAK,EAAE,MAAM;IAOzC;;;OAGG;IACI,aAAa,CAAC,QAAQ,EAAE,iBAAiB,EAAE;IAKlD,8GAA8G;IAC9G,OAAO,CAAC,iBAAiB;CAI1B"}
|
|
@@ -73,10 +73,9 @@ import { WANT_TO_SLASH_EVENT } from './watcher.js';
|
|
|
73
73
|
* Triggered on a time basis when we enter a new slashing round.
|
|
74
74
|
* Clears expired offenses from stores.
|
|
75
75
|
*/ async handleNewRound(round) {
|
|
76
|
-
this.log.verbose(`Clearing expired offenses for new slashing round ${round}`);
|
|
77
76
|
const cleared = await this.offensesStore.clearExpiredOffenses(round);
|
|
78
77
|
if (cleared && cleared > 0) {
|
|
79
|
-
this.log.
|
|
78
|
+
this.log.debug(`Cleared ${cleared} expired offenses for round ${round}`);
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slasher_client_facade.d.ts","sourceRoot":"","sources":["../src/slasher_client_facade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG9F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAK9D,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAZhB,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,OAAO,CAA2B;gBAGhC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG;QAAE,oBAAoB,EAAE,MAAM,CAAA;KAAE,EAC1E,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,UAAU,EACpB,mBAAmB,EAAE,UAAU,GAAG,SAAS,EAC3C,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,gBAAgB,EACzB,MAAM,yCAA0B;IAG7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,SAAS,IAAI,aAAa;IAI1B,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"slasher_client_facade.d.ts","sourceRoot":"","sources":["../src/slasher_client_facade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG9F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,sBAAsB;IAK9D,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAZhB,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,OAAO,CAA2B;gBAGhC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG;QAAE,oBAAoB,EAAE,MAAM,CAAA;KAAE,EAC1E,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,UAAU,EACpB,mBAAmB,EAAE,UAAU,GAAG,SAAS,EAC3C,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,gBAAgB,EACzB,MAAM,yCAA0B;IAG7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,SAAS,IAAI,aAAa;IAI1B,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;IAMlD,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAIhD,sBAAsB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAI1D,kBAAkB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAIxC,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAI7E,OAAO,CAAC,mBAAmB;YAcb,mBAAmB;CAMlC"}
|
|
@@ -50,6 +50,7 @@ import { createSlasherImplementation } from './factory/create_implementation.js'
|
|
|
50
50
|
...config
|
|
51
51
|
};
|
|
52
52
|
this.client?.updateConfig(config);
|
|
53
|
+
this.watchers.forEach((watcher)=>watcher.updateConfig?.(config));
|
|
53
54
|
}
|
|
54
55
|
getSlashPayloads() {
|
|
55
56
|
return this.client?.getSlashPayloads() ?? Promise.reject(new Error('Slasher client not initialized'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import { RollupContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
|
|
2
|
+
import { RollupContract, SlasherContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
|
|
3
3
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
4
4
|
import type { Prettify } from '@aztec/foundation/types';
|
|
5
5
|
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
@@ -58,6 +58,7 @@ export declare class TallySlasherClient implements ProposerSlashActionProvider,
|
|
|
58
58
|
private config;
|
|
59
59
|
private settings;
|
|
60
60
|
private tallySlashingProposer;
|
|
61
|
+
private slasher;
|
|
61
62
|
private rollup;
|
|
62
63
|
private epochCache;
|
|
63
64
|
private dateProvider;
|
|
@@ -66,7 +67,7 @@ export declare class TallySlasherClient implements ProposerSlashActionProvider,
|
|
|
66
67
|
protected unwatchCallbacks: (() => void)[];
|
|
67
68
|
protected roundMonitor: SlashRoundMonitor;
|
|
68
69
|
protected offensesCollector: SlashOffensesCollector;
|
|
69
|
-
constructor(config: TallySlasherClientConfig, settings: TallySlasherSettings, tallySlashingProposer: TallySlashingProposerContract, rollup: RollupContract, watchers: Watcher[], epochCache: EpochCache, dateProvider: DateProvider, offensesStore: SlasherOffensesStore, log?: import("@aztec/aztec.js").Logger);
|
|
70
|
+
constructor(config: TallySlasherClientConfig, settings: TallySlasherSettings, tallySlashingProposer: TallySlashingProposerContract, slasher: SlasherContract, rollup: RollupContract, watchers: Watcher[], epochCache: EpochCache, dateProvider: DateProvider, offensesStore: SlasherOffensesStore, log?: import("@aztec/aztec.js").Logger);
|
|
70
71
|
start(): Promise<void>;
|
|
71
72
|
/**
|
|
72
73
|
* Stop the tally slasher client
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,7 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
40
40
|
config;
|
|
41
41
|
settings;
|
|
42
42
|
tallySlashingProposer;
|
|
43
|
+
slasher;
|
|
43
44
|
rollup;
|
|
44
45
|
epochCache;
|
|
45
46
|
dateProvider;
|
|
@@ -48,10 +49,11 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
48
49
|
unwatchCallbacks;
|
|
49
50
|
roundMonitor;
|
|
50
51
|
offensesCollector;
|
|
51
|
-
constructor(config, settings, tallySlashingProposer, rollup, watchers, epochCache, dateProvider, offensesStore, log = createLogger('slasher:consensus')){
|
|
52
|
+
constructor(config, settings, tallySlashingProposer, slasher, rollup, watchers, epochCache, dateProvider, offensesStore, log = createLogger('slasher:consensus')){
|
|
52
53
|
this.config = config;
|
|
53
54
|
this.settings = settings;
|
|
54
55
|
this.tallySlashingProposer = tallySlashingProposer;
|
|
56
|
+
this.slasher = slasher;
|
|
55
57
|
this.rollup = rollup;
|
|
56
58
|
this.epochCache = epochCache;
|
|
57
59
|
this.dateProvider = dateProvider;
|
|
@@ -156,8 +158,7 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
|
|
|
156
158
|
}
|
|
157
159
|
// Check if the slash payload is vetoed
|
|
158
160
|
const payload = await this.tallySlashingProposer.getPayload(executableRound);
|
|
159
|
-
const
|
|
160
|
-
const isVetoed = await slasherContract.isPayloadVetoed(payload.address);
|
|
161
|
+
const isVetoed = await this.slasher.isPayloadVetoed(payload.address);
|
|
161
162
|
if (isVetoed) {
|
|
162
163
|
this.log.warn(`Round ${executableRound} payload is vetoed (skipping execution)`, {
|
|
163
164
|
payloadAddress: payload.address.toString(),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
2
|
+
import { type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
3
|
+
declare const DummyWatcher_base: new () => WatcherEmitter;
|
|
4
|
+
export declare class DummyWatcher extends DummyWatcher_base implements Watcher {
|
|
5
|
+
updateConfig(_config: Partial<SlasherConfig>): void;
|
|
6
|
+
start(): Promise<void>;
|
|
7
|
+
stop(): Promise<void>;
|
|
8
|
+
triggerSlash(args: WantToSlashArgs[]): void;
|
|
9
|
+
}
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=dummy_watcher.d.ts.map
|
|
@@ -0,0 +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;iCAE1D,UAAU,cAAc;AAA3E,qBAAa,YAAa,SAAQ,iBAA2C,YAAW,OAAO;IACtF,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC;IAE5C,KAAK;IAIL,IAAI;IAIJ,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE;CAG5C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
3
|
+
export class DummyWatcher extends EventEmitter {
|
|
4
|
+
updateConfig(_config) {}
|
|
5
|
+
start() {
|
|
6
|
+
return Promise.resolve();
|
|
7
|
+
}
|
|
8
|
+
stop() {
|
|
9
|
+
return Promise.resolve();
|
|
10
|
+
}
|
|
11
|
+
triggerSlash(args) {
|
|
12
|
+
this.emit(WANT_TO_SLASH_EVENT, args);
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dest/watcher.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
2
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
3
3
|
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
4
|
+
import type { SlasherConfig } from './config.js';
|
|
4
5
|
export declare const WANT_TO_SLASH_EVENT: "want-to-slash";
|
|
5
6
|
export interface WantToSlashArgs {
|
|
6
7
|
validator: EthAddress;
|
|
@@ -15,5 +16,6 @@ export type WatcherEmitter = TypedEventEmitter<WatcherEventMap>;
|
|
|
15
16
|
export type Watcher = WatcherEmitter & {
|
|
16
17
|
start?: () => Promise<void>;
|
|
17
18
|
stop?: () => Promise<void>;
|
|
19
|
+
updateConfig: (config: Partial<SlasherConfig>) => void;
|
|
18
20
|
};
|
|
19
21
|
//# sourceMappingURL=watcher.d.ts.map
|
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,eAAO,MAAM,mBAAmB,EAAG,eAAwB,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;
|
|
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,EAAG,eAAwB,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"}
|
|
@@ -2,6 +2,8 @@ import { EpochCache } from '@aztec/epoch-cache';
|
|
|
2
2
|
import { 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
|
+
declare const AttestationsBlockWatcherConfigKeys: readonly ["slashAttestDescendantOfInvalidPenalty", "slashProposeInvalidAttestationsPenalty"];
|
|
6
|
+
type AttestationsBlockWatcherConfig = Pick<SlasherConfig, (typeof AttestationsBlockWatcherConfigKeys)[number]>;
|
|
5
7
|
declare const AttestationsBlockWatcher_base: new () => WatcherEmitter;
|
|
6
8
|
/**
|
|
7
9
|
* This watcher is responsible for detecting invalid blocks and creating slashing arguments for offenders.
|
|
@@ -12,12 +14,13 @@ declare const AttestationsBlockWatcher_base: new () => WatcherEmitter;
|
|
|
12
14
|
export declare class AttestationsBlockWatcher extends AttestationsBlockWatcher_base implements Watcher {
|
|
13
15
|
private l2BlockSource;
|
|
14
16
|
private epochCache;
|
|
15
|
-
private config;
|
|
16
17
|
private log;
|
|
17
18
|
private maxInvalidBlocks;
|
|
18
19
|
private invalidArchiveRoots;
|
|
20
|
+
private config;
|
|
19
21
|
private boundHandleInvalidBlock;
|
|
20
|
-
constructor(l2BlockSource: L2BlockSourceEventEmitter, epochCache: EpochCache, config:
|
|
22
|
+
constructor(l2BlockSource: L2BlockSourceEventEmitter, epochCache: EpochCache, config: AttestationsBlockWatcherConfig);
|
|
23
|
+
updateConfig(newConfig: Partial<AttestationsBlockWatcherConfig>): void;
|
|
21
24
|
start(): Promise<void>;
|
|
22
25
|
stop(): Promise<void>;
|
|
23
26
|
private handleInvalidBlock;
|
|
@@ -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;AAGhD,OAAO,EAGL,KAAK,yBAAyB,EAG/B,MAAM,qBAAqB,CAAC;AAK7B,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;6CAQhD,UAAU,cAAc;AANvF;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,6BAA2C,YAAW,OAAO;IAuBvG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,UAAU;IAvBpB,OAAO,CAAC,GAAG,CAAsD;IAGjE,OAAO,CAAC,gBAAgB,CAAO;IAG/B,OAAO,CAAC,mBAAmB,CAA0B;IAErD,OAAO,CAAC,MAAM,CAAiC;IAE/C,OAAO,CAAC,uBAAuB,CAS7B;gBAGQ,aAAa,EAAE,yBAAyB,EACxC,UAAU,EAAE,UAAU,EAC9B,MAAM,EAAE,8BAA8B;IAOjC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,8BAA8B,CAAC;IAK/D,KAAK;IAKL,IAAI;IAQX,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,+BAA+B;IAwBvC,OAAO,CAAC,aAAa;IA4BrB,OAAO,CAAC,gCAAgC;IAaxC,OAAO,CAAC,eAAe;CASxB"}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { merge, pick } from '@aztec/foundation/collection';
|
|
1
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
3
|
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
3
4
|
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
4
5
|
import EventEmitter from 'node:events';
|
|
5
6
|
import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
7
|
+
const AttestationsBlockWatcherConfigKeys = [
|
|
8
|
+
'slashAttestDescendantOfInvalidPenalty',
|
|
9
|
+
'slashProposeInvalidAttestationsPenalty'
|
|
10
|
+
];
|
|
6
11
|
/**
|
|
7
12
|
* This watcher is responsible for detecting invalid blocks and creating slashing arguments for offenders.
|
|
8
13
|
* An invalid block is one that doesn't have enough attestations or has incorrect attestations.
|
|
@@ -11,26 +16,30 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
11
16
|
*/ export class AttestationsBlockWatcher extends EventEmitter {
|
|
12
17
|
l2BlockSource;
|
|
13
18
|
epochCache;
|
|
14
|
-
config;
|
|
15
19
|
log;
|
|
16
20
|
// Only keep track of the last N invalid blocks
|
|
17
21
|
maxInvalidBlocks;
|
|
18
22
|
// All invalid archive roots seen
|
|
19
23
|
invalidArchiveRoots;
|
|
24
|
+
config;
|
|
20
25
|
boundHandleInvalidBlock;
|
|
21
26
|
constructor(l2BlockSource, epochCache, config){
|
|
22
|
-
super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.
|
|
27
|
+
super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.log = createLogger('attestations-block-watcher'), this.maxInvalidBlocks = 100, this.invalidArchiveRoots = new Set(), this.boundHandleInvalidBlock = (event)=>{
|
|
23
28
|
try {
|
|
24
29
|
this.handleInvalidBlock(event);
|
|
25
30
|
} catch (err) {
|
|
26
31
|
this.log.error('Error handling invalid block', err, {
|
|
27
|
-
...event.validationResult
|
|
28
|
-
...event.validationResult.block.l1,
|
|
32
|
+
...event.validationResult,
|
|
29
33
|
reason: event.validationResult.reason
|
|
30
34
|
});
|
|
31
35
|
}
|
|
32
36
|
};
|
|
33
|
-
this.
|
|
37
|
+
this.config = pick(config, ...AttestationsBlockWatcherConfigKeys);
|
|
38
|
+
this.log.info('AttestationsBlockWatcher initialized');
|
|
39
|
+
}
|
|
40
|
+
updateConfig(newConfig) {
|
|
41
|
+
this.config = merge(this.config, pick(newConfig, ...AttestationsBlockWatcherConfigKeys));
|
|
42
|
+
this.log.verbose('AttestationsBlockWatcher config updated', this.config);
|
|
34
43
|
}
|
|
35
44
|
start() {
|
|
36
45
|
this.l2BlockSource.on(L2BlockSourceEvents.InvalidAttestationsBlockDetected, this.boundHandleInvalidBlock);
|
|
@@ -42,14 +51,14 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
42
51
|
}
|
|
43
52
|
handleInvalidBlock(event) {
|
|
44
53
|
const { validationResult } = event;
|
|
45
|
-
const block = validationResult.block
|
|
54
|
+
const block = validationResult.block;
|
|
46
55
|
// Check if we already have processed this block, archiver may emit the same event multiple times
|
|
47
|
-
if (this.invalidArchiveRoots.has(block.archive.
|
|
48
|
-
this.log.trace(`Already processed invalid block ${block.
|
|
56
|
+
if (this.invalidArchiveRoots.has(block.archive.toString())) {
|
|
57
|
+
this.log.trace(`Already processed invalid block ${block.blockNumber}`);
|
|
49
58
|
return;
|
|
50
59
|
}
|
|
51
|
-
this.log.verbose(`Detected invalid block ${block.
|
|
52
|
-
...block
|
|
60
|
+
this.log.verbose(`Detected invalid block ${block.blockNumber}`, {
|
|
61
|
+
...block,
|
|
53
62
|
reason: validationResult.valid === false ? validationResult.reason : 'unknown'
|
|
54
63
|
});
|
|
55
64
|
// Store the invalid block
|
|
@@ -61,11 +70,11 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
61
70
|
}
|
|
62
71
|
slashAttestorsOnAncestorInvalid(validationResult) {
|
|
63
72
|
const block = validationResult.block;
|
|
64
|
-
const parentArchive = block.
|
|
65
|
-
if (this.invalidArchiveRoots.has(
|
|
66
|
-
const attestors = validationResult.
|
|
67
|
-
this.log.info(`Want to slash attestors of block ${block.
|
|
68
|
-
...block
|
|
73
|
+
const parentArchive = block.lastArchive.toString();
|
|
74
|
+
if (this.invalidArchiveRoots.has(parentArchive)) {
|
|
75
|
+
const attestors = validationResult.attestors;
|
|
76
|
+
this.log.info(`Want to slash attestors of block ${block.blockNumber} built on invalid block`, {
|
|
77
|
+
...block,
|
|
69
78
|
...attestors,
|
|
70
79
|
parentArchive
|
|
71
80
|
});
|
|
@@ -73,14 +82,14 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
73
82
|
validator: attestor,
|
|
74
83
|
amount: this.config.slashAttestDescendantOfInvalidPenalty,
|
|
75
84
|
offenseType: OffenseType.ATTESTED_DESCENDANT_OF_INVALID,
|
|
76
|
-
epochOrSlot: block.
|
|
85
|
+
epochOrSlot: BigInt(block.slotNumber)
|
|
77
86
|
})));
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
89
|
slashProposer(validationResult) {
|
|
81
90
|
const { reason, block } = validationResult;
|
|
82
|
-
const blockNumber = block.
|
|
83
|
-
const slot = block.
|
|
91
|
+
const blockNumber = block.blockNumber;
|
|
92
|
+
const slot = BigInt(block.slotNumber);
|
|
84
93
|
const proposer = this.epochCache.getProposerFromEpochCommittee(validationResult, slot);
|
|
85
94
|
if (!proposer) {
|
|
86
95
|
this.log.warn(`No proposer found for block ${blockNumber} at slot ${slot}`);
|
|
@@ -92,10 +101,10 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
92
101
|
validator: proposer,
|
|
93
102
|
amount,
|
|
94
103
|
offenseType: offense,
|
|
95
|
-
epochOrSlot:
|
|
104
|
+
epochOrSlot: slot
|
|
96
105
|
};
|
|
97
106
|
this.log.info(`Want to slash proposer of block ${blockNumber} due to ${reason}`, {
|
|
98
|
-
...block
|
|
107
|
+
...block,
|
|
99
108
|
...args
|
|
100
109
|
});
|
|
101
110
|
this.emit(WANT_TO_SLASH_EVENT, [
|
|
@@ -116,7 +125,7 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
116
125
|
}
|
|
117
126
|
}
|
|
118
127
|
addInvalidBlock(block) {
|
|
119
|
-
this.invalidArchiveRoots.add(block.
|
|
128
|
+
this.invalidArchiveRoots.add(block.archive.toString());
|
|
120
129
|
// Prune old entries if we exceed the maximum
|
|
121
130
|
if (this.invalidArchiveRoots.size > this.maxInvalidBlocks) {
|
|
122
131
|
const oldestKey = this.invalidArchiveRoots.keys().next().value;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
2
|
import { L2Block, type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
|
|
3
|
-
import type { IFullNodeBlockBuilder, ITxProvider, MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
|
|
3
|
+
import type { IFullNodeBlockBuilder, ITxProvider, MerkleTreeWriteOperations, SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
4
4
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
5
5
|
import { type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
slashDataWithholdingPenalty: bigint;
|
|
9
|
-
};
|
|
6
|
+
declare const EpochPruneWatcherPenaltiesConfigKeys: readonly ["slashPrunePenalty", "slashDataWithholdingPenalty"];
|
|
7
|
+
type EpochPruneWatcherPenalties = Pick<SlasherConfig, (typeof EpochPruneWatcherPenaltiesConfigKeys)[number]>;
|
|
10
8
|
declare const EpochPruneWatcher_base: new () => WatcherEmitter;
|
|
11
9
|
/**
|
|
12
10
|
* This watcher is responsible for detecting chain prunes and creating slashing arguments for the committee.
|
|
@@ -20,12 +18,13 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
|
|
|
20
18
|
private epochCache;
|
|
21
19
|
private txProvider;
|
|
22
20
|
private blockBuilder;
|
|
23
|
-
private penalties;
|
|
24
21
|
private log;
|
|
25
22
|
private boundHandlePruneL2Blocks;
|
|
23
|
+
private penalties;
|
|
26
24
|
constructor(l2BlockSource: L2BlockSourceEventEmitter, l1ToL2MessageSource: L1ToL2MessageSource, epochCache: EpochCache, txProvider: Pick<ITxProvider, 'getAvailableTxs'>, blockBuilder: IFullNodeBlockBuilder, penalties: EpochPruneWatcherPenalties);
|
|
27
25
|
start(): Promise<void>;
|
|
28
26
|
stop(): Promise<void>;
|
|
27
|
+
updateConfig(config: Partial<SlasherConfig>): void;
|
|
29
28
|
private handlePruneL2Blocks;
|
|
30
29
|
validateBlocks(blocks: L2Block[]): Promise<void>;
|
|
31
30
|
validateBlock(blockFromL1: L2Block, fork: MerkleTreeWriteOperations): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAEL,OAAO,EAEP,KAAK,yBAAyB,EAE/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACX,yBAAyB,EACzB,aAAa,EACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAWnE,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;sCAQrD,UAAU,cAAc;AANhF;;;;;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,YAAY;IAZtB,OAAO,CAAC,GAAG,CAA+C;IAG1D,OAAO,CAAC,wBAAwB,CAAuC;IAEvE,OAAO,CAAC,SAAS,CAA6B;gBAGpC,aAAa,EAAE,yBAAyB,EACxC,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAChD,YAAY,EAAE,qBAAqB,EAC3C,SAAS,EAAE,0BAA0B;IAShC,KAAK;IAKL,IAAI;IAKJ,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;IAKzD,OAAO,CAAC,mBAAmB;IA+Cd,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAchD,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;YAgClF,qBAAqB;IASnC,OAAO,CAAC,wBAAwB;CAgBjC"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { merge, pick } from '@aztec/foundation/collection';
|
|
1
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
3
|
import { L2BlockSourceEvents } from '@aztec/stdlib/block';
|
|
3
4
|
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
4
5
|
import { ReExFailedTxsError, ReExStateMismatchError, TransactionsNotAvailableError, ValidatorError } from '@aztec/stdlib/validators';
|
|
5
6
|
import EventEmitter from 'node:events';
|
|
6
7
|
import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
8
|
+
const EpochPruneWatcherPenaltiesConfigKeys = [
|
|
9
|
+
'slashPrunePenalty',
|
|
10
|
+
'slashDataWithholdingPenalty'
|
|
11
|
+
];
|
|
7
12
|
/**
|
|
8
13
|
* This watcher is responsible for detecting chain prunes and creating slashing arguments for the committee.
|
|
9
14
|
* It only wants to slash if:
|
|
@@ -15,12 +20,13 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
15
20
|
epochCache;
|
|
16
21
|
txProvider;
|
|
17
22
|
blockBuilder;
|
|
18
|
-
penalties;
|
|
19
23
|
log;
|
|
20
24
|
// Store bound function reference for proper listener removal
|
|
21
25
|
boundHandlePruneL2Blocks;
|
|
26
|
+
penalties;
|
|
22
27
|
constructor(l2BlockSource, l1ToL2MessageSource, epochCache, txProvider, blockBuilder, penalties){
|
|
23
|
-
super(), this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.epochCache = epochCache, this.txProvider = txProvider, this.blockBuilder = blockBuilder, this.
|
|
28
|
+
super(), this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.epochCache = epochCache, this.txProvider = txProvider, this.blockBuilder = blockBuilder, this.log = createLogger('epoch-prune-watcher'), this.boundHandlePruneL2Blocks = this.handlePruneL2Blocks.bind(this);
|
|
29
|
+
this.penalties = pick(penalties, ...EpochPruneWatcherPenaltiesConfigKeys);
|
|
24
30
|
this.log.verbose(`EpochPruneWatcher initialized with penalties: valid epoch pruned=${penalties.slashPrunePenalty} data withholding=${penalties.slashDataWithholdingPenalty}`);
|
|
25
31
|
}
|
|
26
32
|
start() {
|
|
@@ -31,6 +37,10 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
31
37
|
this.l2BlockSource.removeListener(L2BlockSourceEvents.L2PruneDetected, this.boundHandlePruneL2Blocks);
|
|
32
38
|
return Promise.resolve();
|
|
33
39
|
}
|
|
40
|
+
updateConfig(config) {
|
|
41
|
+
this.penalties = merge(this.penalties, pick(config, ...EpochPruneWatcherPenaltiesConfigKeys));
|
|
42
|
+
this.log.verbose('EpochPruneWatcher config updated', this.penalties);
|
|
43
|
+
}
|
|
34
44
|
handlePruneL2Blocks(event) {
|
|
35
45
|
const { blocks, epochNumber } = event;
|
|
36
46
|
this.log.info(`Detected chain prune. Validating epoch ${epochNumber}`);
|
|
@@ -45,7 +55,9 @@ import { WANT_TO_SLASH_EVENT } from '../watcher.js';
|
|
|
45
55
|
return result;
|
|
46
56
|
}).catch(async (error)=>{
|
|
47
57
|
if (error instanceof TransactionsNotAvailableError) {
|
|
48
|
-
this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`,
|
|
58
|
+
this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
|
|
59
|
+
message: error.message
|
|
60
|
+
});
|
|
49
61
|
const validators = await this.getValidatorsForEpoch(epochNumber);
|
|
50
62
|
return {
|
|
51
63
|
validators,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/slasher",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -54,20 +54,20 @@
|
|
|
54
54
|
]
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@aztec/epoch-cache": "2.0.
|
|
58
|
-
"@aztec/ethereum": "2.0.
|
|
59
|
-
"@aztec/foundation": "2.0.
|
|
60
|
-
"@aztec/kv-store": "2.0.
|
|
61
|
-
"@aztec/l1-artifacts": "2.0.
|
|
62
|
-
"@aztec/stdlib": "2.0.
|
|
63
|
-
"@aztec/telemetry-client": "2.0.
|
|
57
|
+
"@aztec/epoch-cache": "2.0.2-rc.2",
|
|
58
|
+
"@aztec/ethereum": "2.0.2-rc.2",
|
|
59
|
+
"@aztec/foundation": "2.0.2-rc.2",
|
|
60
|
+
"@aztec/kv-store": "2.0.2-rc.2",
|
|
61
|
+
"@aztec/l1-artifacts": "2.0.2-rc.2",
|
|
62
|
+
"@aztec/stdlib": "2.0.2-rc.2",
|
|
63
|
+
"@aztec/telemetry-client": "2.0.2-rc.2",
|
|
64
64
|
"source-map-support": "^0.5.21",
|
|
65
65
|
"tslib": "^2.4.0",
|
|
66
66
|
"viem": "2.23.7",
|
|
67
67
|
"zod": "^3.23.8"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@aztec/aztec.js": "2.0.
|
|
70
|
+
"@aztec/aztec.js": "2.0.2-rc.2",
|
|
71
71
|
"@jest/globals": "^30.0.0",
|
|
72
72
|
"@types/jest": "^30.0.0",
|
|
73
73
|
"@types/node": "^22.15.17",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmpireSlashingProposerContract, RollupContract } from '@aztec/ethereum';
|
|
1
|
+
import { EmpireSlashingProposerContract, RollupContract, SlasherContract } from '@aztec/ethereum';
|
|
2
2
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
3
3
|
import { compactArray, filterAsync, maxBy, pick } from '@aztec/foundation/collection';
|
|
4
4
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
@@ -121,6 +121,7 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
121
121
|
private settings: EmpireSlasherSettings,
|
|
122
122
|
private slashFactoryContract: SlashFactoryContract,
|
|
123
123
|
private slashingProposer: EmpireSlashingProposerContract,
|
|
124
|
+
private slasher: SlasherContract,
|
|
124
125
|
private rollup: RollupContract,
|
|
125
126
|
watchers: Watcher[],
|
|
126
127
|
private dateProvider: DateProvider,
|
|
@@ -404,8 +405,7 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
|
|
|
404
405
|
}
|
|
405
406
|
|
|
406
407
|
// Check if the slash payload is vetoed
|
|
407
|
-
const
|
|
408
|
-
const isVetoed = await slasherContract.isPayloadVetoed(payload.payload);
|
|
408
|
+
const isVetoed = await this.slasher.isPayloadVetoed(payload.payload);
|
|
409
409
|
|
|
410
410
|
if (isVetoed) {
|
|
411
411
|
this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed, skipping execution`);
|
|
@@ -71,6 +71,7 @@ async function createEmpireSlasher(
|
|
|
71
71
|
l1GenesisTime,
|
|
72
72
|
slotDuration,
|
|
73
73
|
l1StartBlock,
|
|
74
|
+
slasher,
|
|
74
75
|
] = await Promise.all([
|
|
75
76
|
slashingProposer.getExecutionDelayInRounds(),
|
|
76
77
|
slashingProposer.getLifetimeInRounds(),
|
|
@@ -81,6 +82,7 @@ async function createEmpireSlasher(
|
|
|
81
82
|
rollup.getL1GenesisTime(),
|
|
82
83
|
rollup.getSlotDuration(),
|
|
83
84
|
rollup.getL1StartBlock(),
|
|
85
|
+
rollup.getSlasherContract(),
|
|
84
86
|
]);
|
|
85
87
|
|
|
86
88
|
const settings: EmpireSlasherSettings = {
|
|
@@ -110,6 +112,7 @@ async function createEmpireSlasher(
|
|
|
110
112
|
settings,
|
|
111
113
|
slashFactoryContract,
|
|
112
114
|
slashingProposer,
|
|
115
|
+
slasher!,
|
|
113
116
|
rollup,
|
|
114
117
|
watchers,
|
|
115
118
|
dateProvider,
|
|
@@ -134,6 +137,7 @@ async function createTallySlasher(
|
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
const settings = await getTallySlasherSettings(rollup, slashingProposer);
|
|
140
|
+
const slasher = await rollup.getSlasherContract();
|
|
137
141
|
|
|
138
142
|
const offensesStore = new SlasherOffensesStore(kvStore, {
|
|
139
143
|
...settings,
|
|
@@ -144,6 +148,7 @@ async function createTallySlasher(
|
|
|
144
148
|
config,
|
|
145
149
|
settings,
|
|
146
150
|
slashingProposer,
|
|
151
|
+
slasher!,
|
|
147
152
|
rollup,
|
|
148
153
|
watchers,
|
|
149
154
|
epochCache,
|
|
@@ -95,10 +95,9 @@ export class SlashOffensesCollector {
|
|
|
95
95
|
* Clears expired offenses from stores.
|
|
96
96
|
*/
|
|
97
97
|
public async handleNewRound(round: bigint) {
|
|
98
|
-
this.log.verbose(`Clearing expired offenses for new slashing round ${round}`);
|
|
99
98
|
const cleared = await this.offensesStore.clearExpiredOffenses(round);
|
|
100
99
|
if (cleared && cleared > 0) {
|
|
101
|
-
this.log.
|
|
100
|
+
this.log.debug(`Cleared ${cleared} expired offenses for round ${round}`);
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
|
|
@@ -58,6 +58,7 @@ export class SlasherClientFacade implements SlasherClientInterface {
|
|
|
58
58
|
public updateConfig(config: Partial<SlasherConfig>): void {
|
|
59
59
|
this.config = { ...this.config, ...config };
|
|
60
60
|
this.client?.updateConfig(config);
|
|
61
|
+
this.watchers.forEach(watcher => watcher.updateConfig?.(config));
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
public getSlashPayloads(): Promise<SlashPayloadRound[]> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/aztec.js';
|
|
2
2
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
|
-
import { RollupContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
|
|
3
|
+
import { RollupContract, SlasherContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
|
|
4
4
|
import { compactArray, partition, times } from '@aztec/foundation/collection';
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { sleep } from '@aztec/foundation/sleep';
|
|
@@ -88,6 +88,7 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
|
|
|
88
88
|
private config: TallySlasherClientConfig,
|
|
89
89
|
private settings: TallySlasherSettings,
|
|
90
90
|
private tallySlashingProposer: TallySlashingProposerContract,
|
|
91
|
+
private slasher: SlasherContract,
|
|
91
92
|
private rollup: RollupContract,
|
|
92
93
|
watchers: Watcher[],
|
|
93
94
|
private epochCache: EpochCache,
|
|
@@ -212,8 +213,7 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
|
|
|
212
213
|
|
|
213
214
|
// Check if the slash payload is vetoed
|
|
214
215
|
const payload = await this.tallySlashingProposer.getPayload(executableRound);
|
|
215
|
-
const
|
|
216
|
-
const isVetoed = await slasherContract.isPayloadVetoed(payload.address);
|
|
216
|
+
const isVetoed = await this.slasher.isPayloadVetoed(payload.address);
|
|
217
217
|
if (isVetoed) {
|
|
218
218
|
this.log.warn(`Round ${executableRound} payload is vetoed (skipping execution)`, {
|
|
219
219
|
payloadAddress: payload.address.toString(),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
2
|
+
|
|
3
|
+
import EventEmitter from 'node:events';
|
|
4
|
+
|
|
5
|
+
import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
6
|
+
|
|
7
|
+
export class DummyWatcher extends (EventEmitter as new () => WatcherEmitter) implements Watcher {
|
|
8
|
+
public updateConfig(_config: Partial<SlasherConfig>) {}
|
|
9
|
+
|
|
10
|
+
public start() {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public stop() {
|
|
15
|
+
return Promise.resolve();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public triggerSlash(args: WantToSlashArgs[]) {
|
|
19
|
+
this.emit(WANT_TO_SLASH_EVENT, args);
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/watcher.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
2
2
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
3
3
|
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
4
4
|
|
|
5
|
+
import type { SlasherConfig } from './config.js';
|
|
6
|
+
|
|
5
7
|
export const WANT_TO_SLASH_EVENT = 'want-to-slash' as const;
|
|
6
8
|
|
|
7
9
|
export interface WantToSlashArgs {
|
|
@@ -21,4 +23,5 @@ export type WatcherEmitter = TypedEventEmitter<WatcherEventMap>;
|
|
|
21
23
|
export type Watcher = WatcherEmitter & {
|
|
22
24
|
start?: () => Promise<void>;
|
|
23
25
|
stop?: () => Promise<void>;
|
|
26
|
+
updateConfig: (config: Partial<SlasherConfig>) => void;
|
|
24
27
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
+
import { merge, pick } from '@aztec/foundation/collection';
|
|
2
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import {
|
|
4
5
|
type InvalidBlockDetectedEvent,
|
|
6
|
+
type L2BlockInfo,
|
|
5
7
|
type L2BlockSourceEventEmitter,
|
|
6
8
|
L2BlockSourceEvents,
|
|
7
|
-
PublishedL2Block,
|
|
8
9
|
type ValidateBlockNegativeResult,
|
|
9
10
|
} from '@aztec/stdlib/block';
|
|
10
11
|
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
@@ -14,6 +15,13 @@ import EventEmitter from 'node:events';
|
|
|
14
15
|
import type { SlasherConfig } from '../config.js';
|
|
15
16
|
import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
16
17
|
|
|
18
|
+
const AttestationsBlockWatcherConfigKeys = [
|
|
19
|
+
'slashAttestDescendantOfInvalidPenalty',
|
|
20
|
+
'slashProposeInvalidAttestationsPenalty',
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
type AttestationsBlockWatcherConfig = Pick<SlasherConfig, (typeof AttestationsBlockWatcherConfigKeys)[number]>;
|
|
24
|
+
|
|
17
25
|
/**
|
|
18
26
|
* This watcher is responsible for detecting invalid blocks and creating slashing arguments for offenders.
|
|
19
27
|
* An invalid block is one that doesn't have enough attestations or has incorrect attestations.
|
|
@@ -29,13 +37,14 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
29
37
|
// All invalid archive roots seen
|
|
30
38
|
private invalidArchiveRoots: Set<string> = new Set();
|
|
31
39
|
|
|
40
|
+
private config: AttestationsBlockWatcherConfig;
|
|
41
|
+
|
|
32
42
|
private boundHandleInvalidBlock = (event: InvalidBlockDetectedEvent) => {
|
|
33
43
|
try {
|
|
34
44
|
this.handleInvalidBlock(event);
|
|
35
45
|
} catch (err) {
|
|
36
46
|
this.log.error('Error handling invalid block', err, {
|
|
37
|
-
...event.validationResult
|
|
38
|
-
...event.validationResult.block.l1,
|
|
47
|
+
...event.validationResult,
|
|
39
48
|
reason: event.validationResult.reason,
|
|
40
49
|
});
|
|
41
50
|
}
|
|
@@ -44,13 +53,16 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
44
53
|
constructor(
|
|
45
54
|
private l2BlockSource: L2BlockSourceEventEmitter,
|
|
46
55
|
private epochCache: EpochCache,
|
|
47
|
-
|
|
48
|
-
SlasherConfig,
|
|
49
|
-
'slashAttestDescendantOfInvalidPenalty' | 'slashProposeInvalidAttestationsPenalty'
|
|
50
|
-
>,
|
|
56
|
+
config: AttestationsBlockWatcherConfig,
|
|
51
57
|
) {
|
|
52
58
|
super();
|
|
53
|
-
this.
|
|
59
|
+
this.config = pick(config, ...AttestationsBlockWatcherConfigKeys);
|
|
60
|
+
this.log.info('AttestationsBlockWatcher initialized');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public updateConfig(newConfig: Partial<AttestationsBlockWatcherConfig>) {
|
|
64
|
+
this.config = merge(this.config, pick(newConfig, ...AttestationsBlockWatcherConfigKeys));
|
|
65
|
+
this.log.verbose('AttestationsBlockWatcher config updated', this.config);
|
|
54
66
|
}
|
|
55
67
|
|
|
56
68
|
public start() {
|
|
@@ -68,16 +80,16 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
68
80
|
|
|
69
81
|
private handleInvalidBlock(event: InvalidBlockDetectedEvent): void {
|
|
70
82
|
const { validationResult } = event;
|
|
71
|
-
const block = validationResult.block
|
|
83
|
+
const block = validationResult.block;
|
|
72
84
|
|
|
73
85
|
// Check if we already have processed this block, archiver may emit the same event multiple times
|
|
74
|
-
if (this.invalidArchiveRoots.has(block.archive.
|
|
75
|
-
this.log.trace(`Already processed invalid block ${block.
|
|
86
|
+
if (this.invalidArchiveRoots.has(block.archive.toString())) {
|
|
87
|
+
this.log.trace(`Already processed invalid block ${block.blockNumber}`);
|
|
76
88
|
return;
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
this.log.verbose(`Detected invalid block ${block.
|
|
80
|
-
...block
|
|
91
|
+
this.log.verbose(`Detected invalid block ${block.blockNumber}`, {
|
|
92
|
+
...block,
|
|
81
93
|
reason: validationResult.valid === false ? validationResult.reason : 'unknown',
|
|
82
94
|
});
|
|
83
95
|
|
|
@@ -94,11 +106,11 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
94
106
|
private slashAttestorsOnAncestorInvalid(validationResult: ValidateBlockNegativeResult) {
|
|
95
107
|
const block = validationResult.block;
|
|
96
108
|
|
|
97
|
-
const parentArchive = block.
|
|
98
|
-
if (this.invalidArchiveRoots.has(
|
|
99
|
-
const attestors = validationResult.
|
|
100
|
-
this.log.info(`Want to slash attestors of block ${block.
|
|
101
|
-
...block
|
|
109
|
+
const parentArchive = block.lastArchive.toString();
|
|
110
|
+
if (this.invalidArchiveRoots.has(parentArchive)) {
|
|
111
|
+
const attestors = validationResult.attestors;
|
|
112
|
+
this.log.info(`Want to slash attestors of block ${block.blockNumber} built on invalid block`, {
|
|
113
|
+
...block,
|
|
102
114
|
...attestors,
|
|
103
115
|
parentArchive,
|
|
104
116
|
});
|
|
@@ -109,7 +121,7 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
109
121
|
validator: attestor,
|
|
110
122
|
amount: this.config.slashAttestDescendantOfInvalidPenalty,
|
|
111
123
|
offenseType: OffenseType.ATTESTED_DESCENDANT_OF_INVALID,
|
|
112
|
-
epochOrSlot: block.
|
|
124
|
+
epochOrSlot: BigInt(block.slotNumber),
|
|
113
125
|
})),
|
|
114
126
|
);
|
|
115
127
|
}
|
|
@@ -117,8 +129,8 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
117
129
|
|
|
118
130
|
private slashProposer(validationResult: ValidateBlockNegativeResult) {
|
|
119
131
|
const { reason, block } = validationResult;
|
|
120
|
-
const blockNumber = block.
|
|
121
|
-
const slot = block.
|
|
132
|
+
const blockNumber = block.blockNumber;
|
|
133
|
+
const slot = BigInt(block.slotNumber);
|
|
122
134
|
const proposer = this.epochCache.getProposerFromEpochCommittee(validationResult, slot);
|
|
123
135
|
|
|
124
136
|
if (!proposer) {
|
|
@@ -132,11 +144,11 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
132
144
|
validator: proposer,
|
|
133
145
|
amount,
|
|
134
146
|
offenseType: offense,
|
|
135
|
-
epochOrSlot:
|
|
147
|
+
epochOrSlot: slot,
|
|
136
148
|
};
|
|
137
149
|
|
|
138
150
|
this.log.info(`Want to slash proposer of block ${blockNumber} due to ${reason}`, {
|
|
139
|
-
...block
|
|
151
|
+
...block,
|
|
140
152
|
...args,
|
|
141
153
|
});
|
|
142
154
|
|
|
@@ -156,8 +168,8 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
|
|
|
156
168
|
}
|
|
157
169
|
}
|
|
158
170
|
|
|
159
|
-
private addInvalidBlock(block:
|
|
160
|
-
this.invalidArchiveRoots.add(block.
|
|
171
|
+
private addInvalidBlock(block: L2BlockInfo) {
|
|
172
|
+
this.invalidArchiveRoots.add(block.archive.toString());
|
|
161
173
|
|
|
162
174
|
// Prune old entries if we exceed the maximum
|
|
163
175
|
if (this.invalidArchiveRoots.size > this.maxInvalidBlocks) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Tx } from '@aztec/aztec.js';
|
|
2
2
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
3
|
+
import { merge, pick } from '@aztec/foundation/collection';
|
|
3
4
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import {
|
|
5
6
|
EthAddress,
|
|
@@ -8,7 +9,12 @@ import {
|
|
|
8
9
|
type L2BlockSourceEventEmitter,
|
|
9
10
|
L2BlockSourceEvents,
|
|
10
11
|
} from '@aztec/stdlib/block';
|
|
11
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
IFullNodeBlockBuilder,
|
|
14
|
+
ITxProvider,
|
|
15
|
+
MerkleTreeWriteOperations,
|
|
16
|
+
SlasherConfig,
|
|
17
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
12
18
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
13
19
|
import { OffenseType } from '@aztec/stdlib/slashing';
|
|
14
20
|
import {
|
|
@@ -22,10 +28,9 @@ import EventEmitter from 'node:events';
|
|
|
22
28
|
|
|
23
29
|
import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
31
|
+
const EpochPruneWatcherPenaltiesConfigKeys = ['slashPrunePenalty', 'slashDataWithholdingPenalty'] as const;
|
|
32
|
+
|
|
33
|
+
type EpochPruneWatcherPenalties = Pick<SlasherConfig, (typeof EpochPruneWatcherPenaltiesConfigKeys)[number]>;
|
|
29
34
|
|
|
30
35
|
/**
|
|
31
36
|
* This watcher is responsible for detecting chain prunes and creating slashing arguments for the committee.
|
|
@@ -39,15 +44,18 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
39
44
|
// Store bound function reference for proper listener removal
|
|
40
45
|
private boundHandlePruneL2Blocks = this.handlePruneL2Blocks.bind(this);
|
|
41
46
|
|
|
47
|
+
private penalties: EpochPruneWatcherPenalties;
|
|
48
|
+
|
|
42
49
|
constructor(
|
|
43
50
|
private l2BlockSource: L2BlockSourceEventEmitter,
|
|
44
51
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
45
52
|
private epochCache: EpochCache,
|
|
46
53
|
private txProvider: Pick<ITxProvider, 'getAvailableTxs'>,
|
|
47
54
|
private blockBuilder: IFullNodeBlockBuilder,
|
|
48
|
-
|
|
55
|
+
penalties: EpochPruneWatcherPenalties,
|
|
49
56
|
) {
|
|
50
57
|
super();
|
|
58
|
+
this.penalties = pick(penalties, ...EpochPruneWatcherPenaltiesConfigKeys);
|
|
51
59
|
this.log.verbose(
|
|
52
60
|
`EpochPruneWatcher initialized with penalties: valid epoch pruned=${penalties.slashPrunePenalty} data withholding=${penalties.slashDataWithholdingPenalty}`,
|
|
53
61
|
);
|
|
@@ -63,6 +71,11 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
63
71
|
return Promise.resolve();
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
public updateConfig(config: Partial<SlasherConfig>): void {
|
|
75
|
+
this.penalties = merge(this.penalties, pick(config, ...EpochPruneWatcherPenaltiesConfigKeys));
|
|
76
|
+
this.log.verbose('EpochPruneWatcher config updated', this.penalties);
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
private handlePruneL2Blocks(event: L2BlockPruneEvent): void {
|
|
67
80
|
const { blocks, epochNumber } = event;
|
|
68
81
|
this.log.info(`Detected chain prune. Validating epoch ${epochNumber}`);
|
|
@@ -80,7 +93,9 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
|
|
|
80
93
|
})
|
|
81
94
|
.catch(async error => {
|
|
82
95
|
if (error instanceof TransactionsNotAvailableError) {
|
|
83
|
-
this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`,
|
|
96
|
+
this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
|
|
97
|
+
message: error.message,
|
|
98
|
+
});
|
|
84
99
|
const validators = await this.getValidatorsForEpoch(epochNumber);
|
|
85
100
|
return {
|
|
86
101
|
validators,
|