@aztec/slasher 5.2.0-nightly.20260813 → 5.2.0-nightly.20260815

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.
@@ -22,6 +22,11 @@ declare const DataWithholdingWatcher_base: new () => WatcherEmitter;
22
22
  * The watcher ticks at quarter-eth-slot cadence (matching the Sentinel template). On boot it
23
23
  * floors processing at the current slot — restart-time gaps are accepted and not back-filled,
24
24
  * matching the Sentinel approach.
25
+ *
26
+ * Since the evidence for an offense is the absence of txs in the local pool, the probe is gated on
27
+ * p2p connectivity: while there are no connected peers, slots are skipped instead of probed, since
28
+ * missing txs then say nothing about the attesters. Nodes running with p2p disabled do not run this
29
+ * watcher at all.
25
30
  */
26
31
  export declare class DataWithholdingWatcher extends DataWithholdingWatcher_base implements Watcher {
27
32
  private readonly epochCache;
@@ -35,7 +40,8 @@ export declare class DataWithholdingWatcher extends DataWithholdingWatcher_base
35
40
  private initialSlot;
36
41
  private lastCheckedSlot;
37
42
  private config;
38
- constructor(epochCache: EpochCache, l2BlockSource: Pick<L2BlockSource, 'getCheckpoint' | 'getSyncedL2SlotNumber'>, txProvider: Pick<ITxProvider, 'hasTxs'>, p2p: Pick<P2PApi, 'getCheckpointAttestationsForSlot'>, reexecutionTracker: Pick<CheckpointReexecutionTracker, 'getTxsCollectedRecord'>, signatureContext: CoordinationSignatureContext, config: DataWithholdingWatcherConfig, log?: Logger);
43
+ private gossipDegraded;
44
+ constructor(epochCache: EpochCache, l2BlockSource: Pick<L2BlockSource, 'getCheckpoint' | 'getSyncedL2SlotNumber'>, txProvider: Pick<ITxProvider, 'hasTxs'>, p2p: Pick<P2PApi, 'getCheckpointAttestationsForSlot' | 'getP2PConnectivity'>, reexecutionTracker: Pick<CheckpointReexecutionTracker, 'getTxsCollectedRecord'>, signatureContext: CoordinationSignatureContext, config: DataWithholdingWatcherConfig, log?: Logger);
39
45
  start(): Promise<void>;
40
46
  stop(): Promise<void>;
41
47
  updateConfig(config: Partial<SlasherConfig>): void;
@@ -60,4 +66,4 @@ export declare class DataWithholdingWatcher extends DataWithholdingWatcher_base
60
66
  protected extractAttesters(published: PublishedCheckpoint): Promise<EthAddress[]>;
61
67
  }
62
68
  export {};
63
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YV93aXRoaG9sZGluZ193YXRjaGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvd2F0Y2hlcnMvZGF0YV93aXRoaG9sZGluZ193YXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBR3JELE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUVsRSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUV6RCxPQUFPLEtBQUssRUFBRSw0QkFBNEIsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ2xHLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDMUYsT0FBTyxFQUFvQixLQUFLLDRCQUE0QixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFNeEYsT0FBTyxFQUE2QyxLQUFLLE9BQU8sRUFBRSxLQUFLLGNBQWMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU3RyxRQUFBLE1BQU0sZ0NBQWdDLGdGQUFpRixDQUFDO0FBRXhILEtBQUssNEJBQTRCLEdBQUcsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLE9BQU8sZ0NBQWdDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDOztBQUUzRzs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gscUJBQWEsc0JBQXVCLFNBQVEsMkJBQTJDLFlBQVcsT0FBTztJQU9yRyxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVU7SUFDM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxhQUFhO0lBQzlCLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVTtJQUMzQixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUc7SUFDcEIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0I7SUFDbkMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0I7SUFFakMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBYnRCLE9BQU8sQ0FBQyxjQUFjLENBQWlCO0lBQ3ZDLE9BQU8sQ0FBQyxXQUFXLENBQXlCO0lBQzVDLE9BQU8sQ0FBQyxlQUFlLENBQXlCO0lBQ2hELE9BQU8sQ0FBQyxNQUFNLENBQStCO0lBRTdDLFlBQ21CLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLGFBQWEsRUFBRSxJQUFJLENBQUMsYUFBYSxFQUFFLGVBQWUsR0FBRyx1QkFBdUIsQ0FBQyxFQUM3RSxVQUFVLEVBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsRUFDdkMsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsa0NBQWtDLENBQUMsRUFDckQsa0JBQWtCLEVBQUUsSUFBSSxDQUFDLDRCQUE0QixFQUFFLHVCQUF1QixDQUFDLEVBQy9FLGdCQUFnQixFQUFFLDRCQUE0QixFQUMvRCxNQUFNLEVBQUUsNEJBQTRCLEVBQ25CLEdBQUcsR0FBRSxNQUFpRCxFQU94RTtJQUVZLEtBQUssSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBUWxDO0lBRU0sSUFBSSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFM0I7SUFFTSxZQUFZLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsR0FBRyxJQUFJLENBR3hEO0lBRUQ7OztPQUdHO0lBQ1UsSUFBSSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0E4QmpDO1lBR2EsV0FBVztJQTZFekI7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxVQUFnQixnQkFBZ0IsQ0FBQyxTQUFTLEVBQUUsbUJBQW1CLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDLENBbUJ0RjtDQUNGIn0=
69
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YV93aXRoaG9sZGluZ193YXRjaGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvd2F0Y2hlcnMvZGF0YV93aXRoaG9sZGluZ193YXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBR3JELE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUVsRSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUV6RCxPQUFPLEtBQUssRUFBRSw0QkFBNEIsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ2xHLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDMUYsT0FBTyxFQUFvQixLQUFLLDRCQUE0QixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFNeEYsT0FBTyxFQUE2QyxLQUFLLE9BQU8sRUFBRSxLQUFLLGNBQWMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU3RyxRQUFBLE1BQU0sZ0NBQWdDLGdGQUFpRixDQUFDO0FBRXhILEtBQUssNEJBQTRCLEdBQUcsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLE9BQU8sZ0NBQWdDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDOztBQUUzRzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gscUJBQWEsc0JBQXVCLFNBQVEsMkJBQTJDLFlBQVcsT0FBTztJQVFyRyxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVU7SUFDM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxhQUFhO0lBQzlCLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVTtJQUMzQixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUc7SUFDcEIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0I7SUFDbkMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0I7SUFFakMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBZHRCLE9BQU8sQ0FBQyxjQUFjLENBQWlCO0lBQ3ZDLE9BQU8sQ0FBQyxXQUFXLENBQXlCO0lBQzVDLE9BQU8sQ0FBQyxlQUFlLENBQXlCO0lBQ2hELE9BQU8sQ0FBQyxNQUFNLENBQStCO0lBQzdDLE9BQU8sQ0FBQyxjQUFjLENBQVM7SUFFL0IsWUFDbUIsVUFBVSxFQUFFLFVBQVUsRUFDdEIsYUFBYSxFQUFFLElBQUksQ0FBQyxhQUFhLEVBQUUsZUFBZSxHQUFHLHVCQUF1QixDQUFDLEVBQzdFLFVBQVUsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxFQUN2QyxHQUFHLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxrQ0FBa0MsR0FBRyxvQkFBb0IsQ0FBQyxFQUM1RSxrQkFBa0IsRUFBRSxJQUFJLENBQUMsNEJBQTRCLEVBQUUsdUJBQXVCLENBQUMsRUFDL0UsZ0JBQWdCLEVBQUUsNEJBQTRCLEVBQy9ELE1BQU0sRUFBRSw0QkFBNEIsRUFDbkIsR0FBRyxHQUFFLE1BQWlELEVBT3hFO0lBRVksS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FRbEM7SUFFTSxJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUUzQjtJQUVNLFlBQVksQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxHQUFHLElBQUksQ0FHeEQ7SUFFRDs7O09BR0c7SUFDVSxJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQXdEakM7WUFHYSxXQUFXO0lBNkV6Qjs7Ozs7Ozs7Ozs7T0FXRztJQUNILFVBQWdCLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxtQkFBbUIsR0FBRyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FtQnRGO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"data_withholding_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/data_withholding_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EAAoB,KAAK,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AAMxF,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,gCAAgC,gFAAiF,CAAC;AAExH,KAAK,4BAA4B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;AAE3G;;;;;;;;;;;;;GAaG;AACH,qBAAa,sBAAuB,SAAQ,2BAA2C,YAAW,OAAO;IAOrG,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,GAAG;IAbtB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,MAAM,CAA+B;IAE7C,YACmB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,eAAe,GAAG,uBAAuB,CAAC,EAC7E,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EACvC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,kCAAkC,CAAC,EACrD,kBAAkB,EAAE,IAAI,CAAC,4BAA4B,EAAE,uBAAuB,CAAC,EAC/E,gBAAgB,EAAE,4BAA4B,EAC/D,MAAM,EAAE,4BAA4B,EACnB,GAAG,GAAE,MAAiD,EAOxE;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAQlC;IAEM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAEM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAGxD;IAED;;;OAGG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BjC;YAGa,WAAW;IA6EzB;;;;;;;;;;;OAWG;IACH,UAAgB,gBAAgB,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmBtF;CACF"}
1
+ {"version":3,"file":"data_withholding_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/data_withholding_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAC1F,OAAO,EAAoB,KAAK,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AAMxF,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,gCAAgC,gFAAiF,CAAC;AAExH,KAAK,4BAA4B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;AAE3G;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,sBAAuB,SAAQ,2BAA2C,YAAW,OAAO;IAQrG,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,cAAc,CAAS;IAE/B,YACmB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,eAAe,GAAG,uBAAuB,CAAC,EAC7E,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EACvC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,kCAAkC,GAAG,oBAAoB,CAAC,EAC5E,kBAAkB,EAAE,IAAI,CAAC,4BAA4B,EAAE,uBAAuB,CAAC,EAC/E,gBAAgB,EAAE,4BAA4B,EAC/D,MAAM,EAAE,4BAA4B,EACnB,GAAG,GAAE,MAAiD,EAOxE;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAQlC;IAEM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAEM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAGxD;IAED;;;OAGG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAwDjC;YAGa,WAAW;IA6EzB;;;;;;;;;;;OAWG;IACH,UAAgB,gBAAgB,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAmBtF;CACF"}
@@ -24,6 +24,11 @@ const DataWithholdingWatcherConfigKeys = [
24
24
  * The watcher ticks at quarter-eth-slot cadence (matching the Sentinel template). On boot it
25
25
  * floors processing at the current slot — restart-time gaps are accepted and not back-filled,
26
26
  * matching the Sentinel approach.
27
+ *
28
+ * Since the evidence for an offense is the absence of txs in the local pool, the probe is gated on
29
+ * p2p connectivity: while there are no connected peers, slots are skipped instead of probed, since
30
+ * missing txs then say nothing about the attesters. Nodes running with p2p disabled do not run this
31
+ * watcher at all.
27
32
  */ export class DataWithholdingWatcher extends EventEmitter {
28
33
  epochCache;
29
34
  l2BlockSource;
@@ -36,8 +41,9 @@ const DataWithholdingWatcherConfigKeys = [
36
41
  initialSlot;
37
42
  lastCheckedSlot;
38
43
  config;
44
+ gossipDegraded;
39
45
  constructor(epochCache, l2BlockSource, txProvider, p2p, reexecutionTracker, signatureContext, config, log = createLogger('data-withholding-watcher')){
40
- super(), this.epochCache = epochCache, this.l2BlockSource = l2BlockSource, this.txProvider = txProvider, this.p2p = p2p, this.reexecutionTracker = reexecutionTracker, this.signatureContext = signatureContext, this.log = log;
46
+ super(), this.epochCache = epochCache, this.l2BlockSource = l2BlockSource, this.txProvider = txProvider, this.p2p = p2p, this.reexecutionTracker = reexecutionTracker, this.signatureContext = signatureContext, this.log = log, this.gossipDegraded = false;
41
47
  this.config = pick(config, ...DataWithholdingWatcherConfigKeys);
42
48
  const interval = epochCache.getL1Constants().ethereumSlotDuration * 1000 / 4;
43
49
  this.runningPromise = new RunningPromise(this.work.bind(this), log, interval);
@@ -80,6 +86,30 @@ const DataWithholdingWatcherConfigKeys = [
80
86
  if (targetSlot <= this.initialSlot) {
81
87
  return;
82
88
  }
89
+ // Missing txs are only evidence of withholding if we could have received them. Slots probed
90
+ // while gossip is down are skipped for good rather than deferred: once peers return, txs from
91
+ // those checkpoints may already have been evicted from the pool as mined, so a late probe would
92
+ // still report them missing. An unknown must not turn into an offense. Note this deliberately
93
+ // ignores whether p2p is enabled: a node with no p2p stack sees even less than a peerless one.
94
+ const connectivity = await this.p2p.getP2PConnectivity();
95
+ if (connectivity.connectedPeers === 0) {
96
+ if (!this.gossipDegraded) {
97
+ this.gossipDegraded = true;
98
+ this.log.warn(`Skipping data-withholding checks while no peers are connected`, {
99
+ targetSlot,
100
+ lastCheckedSlot: this.lastCheckedSlot
101
+ });
102
+ }
103
+ this.lastCheckedSlot = targetSlot;
104
+ return;
105
+ }
106
+ if (this.gossipDegraded) {
107
+ this.gossipDegraded = false;
108
+ this.log.info(`Resuming data-withholding checks now that peers are connected`, {
109
+ targetSlot,
110
+ connectedPeers: connectivity.connectedPeers
111
+ });
112
+ }
83
113
  const startSlot = this.lastCheckedSlot === undefined ? this.initialSlot : this.lastCheckedSlot;
84
114
  for(let slot = SlotNumber(startSlot + 1); slot <= targetSlot; slot = SlotNumber(slot + 1)){
85
115
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/slasher",
3
- "version": "5.2.0-nightly.20260813",
3
+ "version": "5.2.0-nightly.20260815",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -56,20 +56,20 @@
56
56
  ]
57
57
  },
58
58
  "dependencies": {
59
- "@aztec/epoch-cache": "5.2.0-nightly.20260813",
60
- "@aztec/ethereum": "5.2.0-nightly.20260813",
61
- "@aztec/foundation": "5.2.0-nightly.20260813",
62
- "@aztec/kv-store": "5.2.0-nightly.20260813",
63
- "@aztec/l1-artifacts": "5.2.0-nightly.20260813",
64
- "@aztec/stdlib": "5.2.0-nightly.20260813",
65
- "@aztec/telemetry-client": "5.2.0-nightly.20260813",
59
+ "@aztec/epoch-cache": "5.2.0-nightly.20260815",
60
+ "@aztec/ethereum": "5.2.0-nightly.20260815",
61
+ "@aztec/foundation": "5.2.0-nightly.20260815",
62
+ "@aztec/kv-store": "5.2.0-nightly.20260815",
63
+ "@aztec/l1-artifacts": "5.2.0-nightly.20260815",
64
+ "@aztec/stdlib": "5.2.0-nightly.20260815",
65
+ "@aztec/telemetry-client": "5.2.0-nightly.20260815",
66
66
  "source-map-support": "^0.5.21",
67
67
  "tslib": "^2.4.0",
68
68
  "viem": "npm:@aztec/viem@2.38.2",
69
69
  "zod": "^4"
70
70
  },
71
71
  "devDependencies": {
72
- "@aztec/aztec.js": "5.2.0-nightly.20260813",
72
+ "@aztec/aztec.js": "5.2.0-nightly.20260815",
73
73
  "@jest/globals": "^30.0.0",
74
74
  "@types/jest": "^30.0.0",
75
75
  "@types/node": "^22.15.17",
@@ -33,18 +33,24 @@ type DataWithholdingWatcherConfig = Pick<SlasherConfig, (typeof DataWithholdingW
33
33
  * The watcher ticks at quarter-eth-slot cadence (matching the Sentinel template). On boot it
34
34
  * floors processing at the current slot — restart-time gaps are accepted and not back-filled,
35
35
  * matching the Sentinel approach.
36
+ *
37
+ * Since the evidence for an offense is the absence of txs in the local pool, the probe is gated on
38
+ * p2p connectivity: while there are no connected peers, slots are skipped instead of probed, since
39
+ * missing txs then say nothing about the attesters. Nodes running with p2p disabled do not run this
40
+ * watcher at all.
36
41
  */
37
42
  export class DataWithholdingWatcher extends (EventEmitter as new () => WatcherEmitter) implements Watcher {
38
43
  private runningPromise: RunningPromise;
39
44
  private initialSlot: SlotNumber | undefined;
40
45
  private lastCheckedSlot: SlotNumber | undefined;
41
46
  private config: DataWithholdingWatcherConfig;
47
+ private gossipDegraded = false;
42
48
 
43
49
  constructor(
44
50
  private readonly epochCache: EpochCache,
45
51
  private readonly l2BlockSource: Pick<L2BlockSource, 'getCheckpoint' | 'getSyncedL2SlotNumber'>,
46
52
  private readonly txProvider: Pick<ITxProvider, 'hasTxs'>,
47
- private readonly p2p: Pick<P2PApi, 'getCheckpointAttestationsForSlot'>,
53
+ private readonly p2p: Pick<P2PApi, 'getCheckpointAttestationsForSlot' | 'getP2PConnectivity'>,
48
54
  private readonly reexecutionTracker: Pick<CheckpointReexecutionTracker, 'getTxsCollectedRecord'>,
49
55
  private readonly signatureContext: CoordinationSignatureContext,
50
56
  config: DataWithholdingWatcherConfig,
@@ -101,6 +107,32 @@ export class DataWithholdingWatcher extends (EventEmitter as new () => WatcherEm
101
107
  return;
102
108
  }
103
109
 
110
+ // Missing txs are only evidence of withholding if we could have received them. Slots probed
111
+ // while gossip is down are skipped for good rather than deferred: once peers return, txs from
112
+ // those checkpoints may already have been evicted from the pool as mined, so a late probe would
113
+ // still report them missing. An unknown must not turn into an offense. Note this deliberately
114
+ // ignores whether p2p is enabled: a node with no p2p stack sees even less than a peerless one.
115
+ const connectivity = await this.p2p.getP2PConnectivity();
116
+ if (connectivity.connectedPeers === 0) {
117
+ if (!this.gossipDegraded) {
118
+ this.gossipDegraded = true;
119
+ this.log.warn(`Skipping data-withholding checks while no peers are connected`, {
120
+ targetSlot,
121
+ lastCheckedSlot: this.lastCheckedSlot,
122
+ });
123
+ }
124
+ this.lastCheckedSlot = targetSlot;
125
+ return;
126
+ }
127
+
128
+ if (this.gossipDegraded) {
129
+ this.gossipDegraded = false;
130
+ this.log.info(`Resuming data-withholding checks now that peers are connected`, {
131
+ targetSlot,
132
+ connectedPeers: connectivity.connectedPeers,
133
+ });
134
+ }
135
+
104
136
  const startSlot = this.lastCheckedSlot === undefined ? this.initialSlot : this.lastCheckedSlot;
105
137
  for (let slot = SlotNumber(startSlot + 1); slot <= targetSlot; slot = SlotNumber(slot + 1)) {
106
138
  try {