@aztec/p2p 0.0.1-commit.3f296a7d2 → 0.0.1-commit.3f5453c7b

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.
Files changed (198) hide show
  1. package/dest/client/factory.d.ts +1 -1
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +5 -4
  4. package/dest/client/interface.d.ts +9 -2
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +3 -2
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +21 -8
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +14 -3
  10. package/dest/config.d.ts +4 -2
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +5 -0
  13. package/dest/errors/p2p-service.error.d.ts +9 -0
  14. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  15. package/dest/errors/p2p-service.error.js +10 -0
  16. package/dest/index.d.ts +1 -2
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +0 -1
  19. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +4 -2
  20. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool.js +8 -5
  22. package/dest/mem_pools/index.d.ts +1 -2
  23. package/dest/mem_pools/index.d.ts.map +1 -1
  24. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +1 -1
  25. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  26. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +5 -1
  27. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +5 -2
  28. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  29. package/dest/msg_validators/attestation_validator/attestation_validator.js +17 -9
  30. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +4 -2
  31. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  32. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +2 -2
  33. package/dest/msg_validators/clock_tolerance.d.ts +12 -1
  34. package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
  35. package/dest/msg_validators/clock_tolerance.js +50 -0
  36. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
  37. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  38. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
  39. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  40. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
  41. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  42. package/dest/msg_validators/proposal_validator/proposal_validator.js +16 -8
  43. package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
  44. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  45. package/dest/msg_validators/tx_validator/gas_validator.js +11 -9
  46. package/dest/services/data_store.d.ts +1 -1
  47. package/dest/services/data_store.d.ts.map +1 -1
  48. package/dest/services/data_store.js +5 -5
  49. package/dest/services/dummy_service.d.ts +6 -3
  50. package/dest/services/dummy_service.d.ts.map +1 -1
  51. package/dest/services/dummy_service.js +6 -1
  52. package/dest/services/gossipsub/topic_score_params.d.ts +13 -2
  53. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  54. package/dest/services/gossipsub/topic_score_params.js +21 -4
  55. package/dest/services/libp2p/libp2p_service.d.ts +12 -17
  56. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  57. package/dest/services/libp2p/libp2p_service.js +38 -61
  58. package/dest/services/peer-manager/peer_manager.d.ts +6 -2
  59. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  60. package/dest/services/peer-manager/peer_manager.js +33 -8
  61. package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
  62. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  63. package/dest/services/peer-manager/peer_scoring.js +32 -10
  64. package/dest/services/reqresp/interface.d.ts +14 -9
  65. package/dest/services/reqresp/interface.d.ts.map +1 -1
  66. package/dest/services/reqresp/interface.js +10 -11
  67. package/dest/services/reqresp/metrics.d.ts +1 -1
  68. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  69. package/dest/services/reqresp/metrics.js +0 -1
  70. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  71. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  72. package/dest/services/reqresp/protocols/index.js +0 -1
  73. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  74. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  75. package/dest/services/reqresp/protocols/tx.js +1 -3
  76. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  77. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  78. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  79. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  80. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  81. package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
  82. package/dest/services/reqresp/reqresp.d.ts +4 -2
  83. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  84. package/dest/services/reqresp/reqresp.js +11 -2
  85. package/dest/services/service.d.ts +5 -2
  86. package/dest/services/service.d.ts.map +1 -1
  87. package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
  88. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
  89. package/dest/services/tx_collection/file_store_tx_source.js +39 -29
  90. package/dest/services/tx_collection/tx_source.d.ts +6 -5
  91. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  92. package/dest/services/tx_collection/tx_source.js +9 -7
  93. package/dest/test-helpers/mock-pubsub.d.ts +11 -3
  94. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  95. package/dest/test-helpers/mock-pubsub.js +35 -10
  96. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  97. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  98. package/dest/test-helpers/reqresp-nodes.js +1 -2
  99. package/dest/test-helpers/testbench-utils.d.ts +1 -1
  100. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  101. package/dest/test-helpers/testbench-utils.js +1 -0
  102. package/dest/testbench/p2p_client_testbench_worker.js +63 -12
  103. package/dest/testbench/worker_client_manager.d.ts +8 -1
  104. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  105. package/dest/testbench/worker_client_manager.js +49 -1
  106. package/package.json +14 -14
  107. package/src/client/factory.ts +7 -2
  108. package/src/client/interface.ts +9 -1
  109. package/src/client/p2p_client.ts +23 -8
  110. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +15 -3
  111. package/src/config.ts +13 -1
  112. package/src/errors/p2p-service.error.ts +11 -0
  113. package/src/index.ts +0 -1
  114. package/src/mem_pools/attestation_pool/attestation_pool.ts +9 -5
  115. package/src/mem_pools/index.ts +0 -3
  116. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +7 -1
  117. package/src/msg_validators/attestation_validator/attestation_validator.ts +18 -7
  118. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +4 -1
  119. package/src/msg_validators/clock_tolerance.ts +68 -0
  120. package/src/msg_validators/proposal_validator/README.md +1 -1
  121. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +4 -1
  122. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +4 -1
  123. package/src/msg_validators/proposal_validator/proposal_validator.ts +13 -7
  124. package/src/msg_validators/tx_validator/gas_validator.ts +25 -9
  125. package/src/services/data_store.ts +5 -13
  126. package/src/services/dummy_service.ts +8 -2
  127. package/src/services/gossipsub/topic_score_params.ts +36 -4
  128. package/src/services/libp2p/libp2p_service.ts +42 -64
  129. package/src/services/peer-manager/peer_manager.ts +38 -8
  130. package/src/services/peer-manager/peer_scoring.ts +27 -5
  131. package/src/services/reqresp/interface.ts +21 -11
  132. package/src/services/reqresp/metrics.ts +0 -1
  133. package/src/services/reqresp/protocols/index.ts +0 -1
  134. package/src/services/reqresp/protocols/tx.ts +1 -3
  135. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  136. package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
  137. package/src/services/reqresp/reqresp.ts +18 -1
  138. package/src/services/service.ts +6 -1
  139. package/src/services/tx_collection/file_store_tx_source.ts +43 -31
  140. package/src/services/tx_collection/tx_source.ts +8 -7
  141. package/src/test-helpers/mock-pubsub.ts +31 -5
  142. package/src/test-helpers/reqresp-nodes.ts +2 -2
  143. package/src/test-helpers/testbench-utils.ts +1 -0
  144. package/src/testbench/p2p_client_testbench_worker.ts +67 -9
  145. package/src/testbench/worker_client_manager.ts +55 -1
  146. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  147. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  148. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  149. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  150. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  151. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  152. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  153. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  154. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  155. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  156. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  157. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
  158. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  159. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  160. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
  161. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  162. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  163. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  164. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  165. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  166. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  167. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  168. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  169. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  170. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  171. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  172. package/dest/mem_pools/tx_pool/index.js +0 -2
  173. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  174. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  175. package/dest/mem_pools/tx_pool/priority.js +0 -15
  176. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  177. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  178. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  179. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  180. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  181. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
  182. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  183. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  184. package/dest/services/reqresp/protocols/block.js +0 -32
  185. package/src/mem_pools/tx_pool/README.md +0 -270
  186. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  187. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  188. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  189. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
  190. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  191. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  192. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  193. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  194. package/src/mem_pools/tx_pool/index.ts +0 -2
  195. package/src/mem_pools/tx_pool/priority.ts +0 -20
  196. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  197. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
  198. package/src/services/reqresp/protocols/block.ts +0 -37
@@ -8,14 +8,21 @@ import {
8
8
  type ValidationResult,
9
9
  } from '@aztec/stdlib/p2p';
10
10
 
11
- import { isWithinClockTolerance } from '../clock_tolerance.js';
11
+ import { PipeliningWindow, isWithinClockTolerance } from '../clock_tolerance.js';
12
12
 
13
13
  export class CheckpointAttestationValidator implements P2PValidator<CheckpointAttestation> {
14
14
  protected epochCache: EpochCacheInterface;
15
15
  protected logger: Logger;
16
+ private readonly pipeliningWindow: PipeliningWindow;
16
17
 
17
- constructor(epochCache: EpochCacheInterface) {
18
+ constructor(
19
+ epochCache: EpochCacheInterface,
20
+ opts: {
21
+ l1PublishingTime?: number;
22
+ },
23
+ ) {
18
24
  this.epochCache = epochCache;
25
+ this.pipeliningWindow = new PipeliningWindow(epochCache, { l1PublishingTime: opts.l1PublishingTime });
19
26
  this.logger = createLogger('p2p:checkpoint-attestation-validator');
20
27
  }
21
28
 
@@ -23,19 +30,23 @@ export class CheckpointAttestationValidator implements P2PValidator<CheckpointAt
23
30
  const slotNumber = message.payload.header.slotNumber;
24
31
 
25
32
  try {
26
- // Use target slots since proposals target pipeline slots (slot + 1 when pipelining)
33
+ // Use target slots since proposals target pipeline slots (slot + 1 when pipelining).
27
34
  const { targetSlot, nextSlot } = this.epochCache.getTargetAndNextSlot();
28
35
 
29
36
  if (slotNumber !== targetSlot && slotNumber !== nextSlot) {
30
- // Check if message is for previous slot and within clock tolerance
31
- if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
37
+ // When pipelining, accept attestations for the current slot (built in the previous slot)
38
+ // until the target slot reaches its L1 publish cutoff.
39
+ if (this.pipeliningWindow.acceptsAttestation(slotNumber)) {
40
+ // Fall through to remaining validation (signature, committee, etc.)
41
+ } else if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
32
42
  this.logger.warn(
33
43
  `Checkpoint attestation slot ${slotNumber} is not current (${targetSlot}) or next (${nextSlot}) slot`,
34
44
  );
35
45
  return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
46
+ } else {
47
+ this.logger.debug(`Ignoring checkpoint attestation for previous slot ${slotNumber} within clock tolerance`);
48
+ return { result: 'ignore' };
36
49
  }
37
- this.logger.debug(`Ignoring checkpoint attestation for previous slot ${slotNumber} within clock tolerance`);
38
- return { result: 'ignore' };
39
50
  }
40
51
 
41
52
  // Verify the signature is valid
@@ -20,8 +20,11 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
20
20
  epochCache: EpochCacheInterface,
21
21
  private attestationPool: AttestationPoolApi,
22
22
  telemetryClient: TelemetryClient,
23
+ opts: {
24
+ l1PublishingTime?: number;
25
+ } = {},
23
26
  ) {
24
- super(epochCache);
27
+ super(epochCache, opts);
25
28
  this.logger = this.logger.createChild('[FISHERMAN]');
26
29
 
27
30
  const meter = telemetryClient.getMeter('FishermanAttestationValidator');
@@ -1,5 +1,6 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { DEFAULT_P2P_PROPAGATION_TIME, createPipelinedCheckpointTimingModel } from '@aztec/stdlib/timetable';
3
4
 
4
5
  /**
5
6
  * Maximum clock disparity tolerance for P2P message validation (in milliseconds).
@@ -50,3 +51,70 @@ export function isWithinClockTolerance(
50
51
 
51
52
  return elapsedMs < MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
52
53
  }
54
+
55
+ /**
56
+ * Checks if a message should be accepted under the pipelining grace period.
57
+ *
58
+ * When pipelining is enabled, `targetSlot = slotNow + 1`. A proposal built in slot N-1
59
+ * for slot N arrives when validators are in slot N, so their `targetSlot = N+1`.
60
+ * This function accepts proposals for the current wallclock slot if we're within the
61
+ * first `windowSeconds` seconds of the slot (the pipelining grace period). - see stdlib/timetable/index.ts
62
+ *
63
+ * @param messageSlot - The slot number from the received message
64
+ * @param epochCache - EpochCache to get timing and pipelining state
65
+ * @param windowSeconds - The window grace period allowed for attestations into the next slot
66
+ * @returns true if pipelining is enabled, the message is for the current slot, and we're within the grace period
67
+ */
68
+ function isWithinPipeliningWindow(
69
+ messageSlot: SlotNumber,
70
+ epochCache: EpochCacheInterface,
71
+ windowSeconds: number,
72
+ ): boolean {
73
+ if (!epochCache.isProposerPipeliningEnabled()) {
74
+ return false;
75
+ }
76
+
77
+ const currentSlot = epochCache.getSlotNow();
78
+ if (messageSlot !== currentSlot) {
79
+ return false;
80
+ }
81
+
82
+ const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
83
+ const slotStartMs = slotStartTs * 1000n;
84
+ const elapsedMs = Number(nowMs - slotStartMs);
85
+ const windowMs = windowSeconds * 1000 + MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
86
+
87
+ return elapsedMs < windowMs;
88
+ }
89
+
90
+ export class PipeliningWindow {
91
+ private readonly proposalWindowIntoTargetSlot: number;
92
+ private readonly attestationWindowIntoTargetSlot: number;
93
+
94
+ constructor(
95
+ private readonly epochCache: EpochCacheInterface,
96
+ opts: {
97
+ p2pPropagationTime?: number;
98
+ l1PublishingTime?: number;
99
+ } = {},
100
+ ) {
101
+ const l1Constants = epochCache.getL1Constants();
102
+ const checkpointTiming = createPipelinedCheckpointTimingModel({
103
+ aztecSlotDuration: l1Constants.slotDuration,
104
+ ethereumSlotDuration: l1Constants.ethereumSlotDuration,
105
+ l1PublishingTime: opts.l1PublishingTime ?? l1Constants.ethereumSlotDuration,
106
+ p2pPropagationTime: opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME,
107
+ });
108
+
109
+ this.proposalWindowIntoTargetSlot = checkpointTiming.proposalWindowIntoTargetSlot;
110
+ this.attestationWindowIntoTargetSlot = checkpointTiming.attestationWindowIntoTargetSlot;
111
+ }
112
+
113
+ public acceptsProposal(messageSlot: SlotNumber): boolean {
114
+ return isWithinPipeliningWindow(messageSlot, this.epochCache, this.proposalWindowIntoTargetSlot);
115
+ }
116
+
117
+ public acceptsAttestation(messageSlot: SlotNumber): boolean {
118
+ return isWithinPipeliningWindow(messageSlot, this.epochCache, this.attestationWindowIntoTargetSlot);
119
+ }
120
+ }
@@ -53,7 +53,7 @@ Only runs on validator nodes. Non-validator nodes use a default handler that tri
53
53
 
54
54
  **Escape hatch**: during escape hatch periods (`isEscapeHatchOpenAtSlot`), re-execution and slashing are both disabled, and the proposal is rejected locally.
55
55
 
56
- **Conditional re-execution**: rules 22-24 only run when at least one condition is true: `fishermanMode` enabled, `slashBroadcastedInvalidBlockPenalty > 0` with `validatorReexecute`, committee membership with `validatorReexecute`, `alwaysReexecuteBlockProposals`, or `blobClient.canUpload()`.
56
+ **Conditional re-execution**: rules 22-24 only run when at least one condition is true: `fishermanMode` enabled, `slashBroadcastedInvalidBlockPenalty > 0`, committee membership, `alwaysReexecuteBlockProposals`, or `blobClient.canUpload()`.
57
57
 
58
58
  **Slashing**: only `state_mismatch` and `failed_txs` trigger on-chain slashing (`OffenseType.BROADCASTED_INVALID_BLOCK_PROPOSAL`, gated by `slashBroadcastedInvalidBlockPenalty > 0`). Unknown errors during re-execution do NOT slash.
59
59
 
@@ -6,7 +6,10 @@ import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
6
6
  export class BlockProposalValidator implements P2PValidator<BlockProposal> {
7
7
  private proposalValidator: ProposalValidator;
8
8
 
9
- constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
9
+ constructor(
10
+ epochCache: EpochCacheInterface,
11
+ opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
12
+ ) {
10
13
  this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:block_proposal_validator');
11
14
  }
12
15
 
@@ -6,7 +6,10 @@ import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
6
6
  export class CheckpointProposalValidator implements P2PValidator<CheckpointProposal> {
7
7
  private proposalValidator: ProposalValidator;
8
8
 
9
- constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
9
+ constructor(
10
+ epochCache: EpochCacheInterface,
11
+ opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
12
+ ) {
10
13
  this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:checkpoint_proposal_validator');
11
14
  }
12
15
 
@@ -8,7 +8,7 @@ import {
8
8
  type ValidationResult,
9
9
  } from '@aztec/stdlib/p2p';
10
10
 
11
- import { isWithinClockTolerance } from '../clock_tolerance.js';
11
+ import { PipeliningWindow, isWithinClockTolerance } from '../clock_tolerance.js';
12
12
 
13
13
  /** Validates header-level and tx-level fields of block and checkpoint proposals. */
14
14
  export class ProposalValidator {
@@ -16,33 +16,39 @@ export class ProposalValidator {
16
16
  private logger: Logger;
17
17
  private txsPermitted: boolean;
18
18
  private maxTxsPerBlock?: number;
19
+ private pipeliningWindow: PipeliningWindow;
19
20
 
20
21
  constructor(
21
22
  epochCache: EpochCacheInterface,
22
- opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
23
+ opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
23
24
  loggerName: string,
24
25
  ) {
25
26
  this.epochCache = epochCache;
26
27
  this.txsPermitted = opts.txsPermitted;
27
28
  this.maxTxsPerBlock = opts.maxTxsPerBlock;
29
+ this.pipeliningWindow = new PipeliningWindow(epochCache, { p2pPropagationTime: opts.p2pPropagationTime });
28
30
  this.logger = createLogger(loggerName);
29
31
  }
30
32
 
31
33
  /** Validates header-level fields: slot, signature, and proposer. */
32
34
  public async validate(proposal: BlockProposal | CheckpointProposalCore): Promise<ValidationResult> {
33
35
  try {
34
- // Slot check: use target slots since proposals target pipeline slots (slot + 1 when pipelining)
36
+ // Slot check: use target slots since proposals target pipeline slots (slot + 1 when pipelining).
35
37
  const { targetSlot, nextSlot } = this.epochCache.getTargetAndNextSlot();
36
38
 
37
39
  const slotNumber = proposal.slotNumber;
38
40
  if (slotNumber !== targetSlot && slotNumber !== nextSlot) {
39
- // Check if message is for previous slot and within clock tolerance
40
- if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
41
+ // When pipelining, accept proposals for the current slot (built in the previous slot)
42
+ // if they're still within the shared proposal acceptance window.
43
+ if (this.pipeliningWindow.acceptsProposal(slotNumber)) {
44
+ // Fall through to remaining validation (signature, proposer, etc.)
45
+ } else if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
41
46
  this.logger.warn(`Penalizing peer for invalid slot number ${slotNumber}`, { targetSlot, nextSlot });
42
47
  return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
48
+ } else {
49
+ this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
50
+ return { result: 'ignore' };
43
51
  }
44
- this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
45
- return { result: 'ignore' };
46
52
  }
47
53
 
48
54
  // Signature validity
@@ -87,7 +87,12 @@ export class GasLimitsValidator<T extends HasGasLimitData> implements TxValidato
87
87
  gasLimits,
88
88
  minGasLimits,
89
89
  });
90
- return { result: 'invalid', reason: [TX_ERROR_INSUFFICIENT_GAS_LIMIT] };
90
+ return {
91
+ result: 'invalid',
92
+ reason: [
93
+ `${TX_ERROR_INSUFFICIENT_GAS_LIMIT} (required=da:${minGasLimits.daGas},l2:${minGasLimits.l2Gas} got=da:${gasLimits.daGas},l2:${gasLimits.l2Gas})`,
94
+ ],
95
+ };
91
96
  }
92
97
 
93
98
  if (gasLimits.l2Gas > this.#effectiveMaxL2Gas) {
@@ -97,7 +102,10 @@ export class GasLimitsValidator<T extends HasGasLimitData> implements TxValidato
97
102
  rollupManaLimit: this.#rollupManaLimit,
98
103
  maxBlockL2Gas: this.#maxBlockL2Gas,
99
104
  });
100
- return { result: 'invalid', reason: [TX_ERROR_GAS_LIMIT_TOO_HIGH] };
105
+ return {
106
+ result: 'invalid',
107
+ reason: [`${TX_ERROR_GAS_LIMIT_TOO_HIGH} (l2Gas=${gasLimits.l2Gas}, max=${this.#effectiveMaxL2Gas})`],
108
+ };
101
109
  }
102
110
 
103
111
  if (gasLimits.daGas > this.#effectiveMaxDAGas) {
@@ -106,7 +114,10 @@ export class GasLimitsValidator<T extends HasGasLimitData> implements TxValidato
106
114
  effectiveMaxDAGas: this.#effectiveMaxDAGas,
107
115
  maxBlockDAGas: this.#maxBlockDAGas,
108
116
  });
109
- return { result: 'invalid', reason: [TX_ERROR_GAS_LIMIT_TOO_HIGH] };
117
+ return {
118
+ result: 'invalid',
119
+ reason: [`${TX_ERROR_GAS_LIMIT_TOO_HIGH} (daGas=${gasLimits.daGas}, max=${this.#effectiveMaxDAGas})`],
120
+ };
110
121
  }
111
122
 
112
123
  return { result: 'valid' };
@@ -157,19 +168,20 @@ export class GasTxValidator implements TxValidator<Tx> {
157
168
  if (gasLimitValidation.result === 'invalid') {
158
169
  return Promise.resolve(gasLimitValidation);
159
170
  }
160
- if (this.#shouldSkip(tx)) {
161
- return Promise.resolve({ result: 'skipped', reason: [TX_ERROR_INSUFFICIENT_FEE_PER_GAS] });
171
+ const skipReason = this.#getSkipReason(tx);
172
+ if (skipReason) {
173
+ return Promise.resolve({ result: 'skipped', reason: [skipReason] });
162
174
  }
163
175
  return await this.validateTxFee(tx);
164
176
  }
165
177
 
166
178
  /**
167
- * Check whether the tx's max fees are valid for the current block, and skip if not.
179
+ * Check whether the tx's max fees are valid for the current block, and return a skip reason if not.
168
180
  * We skip instead of invalidating since the tx may become eligible later.
169
181
  * Note that circuits check max fees even if fee payer is unset, so we
170
182
  * keep this validation even if the tx does not pay fees.
171
183
  */
172
- #shouldSkip(tx: Tx): boolean {
184
+ #getSkipReason(tx: Tx): string | undefined {
173
185
  const gasSettings = tx.data.constants.txContext.gasSettings;
174
186
 
175
187
  // Skip the tx if its max fees are not enough for the current block's gas fees.
@@ -182,8 +194,9 @@ export class GasTxValidator implements TxValidator<Tx> {
182
194
  txMaxFeesPerGas: maxFeesPerGas.toInspect(),
183
195
  currentGasFees: this.#gasFees.toInspect(),
184
196
  });
197
+ return `${TX_ERROR_INSUFFICIENT_FEE_PER_GAS} (maxFee=da:${maxFeesPerGas.feePerDaGas},l2:${maxFeesPerGas.feePerL2Gas} required=da:${this.#gasFees.feePerDaGas},l2:${this.#gasFees.feePerL2Gas})`;
185
198
  }
186
- return notEnoughMaxFees;
199
+ return undefined;
187
200
  }
188
201
 
189
202
  /**
@@ -212,7 +225,10 @@ export class GasTxValidator implements TxValidator<Tx> {
212
225
  balance,
213
226
  feeLimit,
214
227
  });
215
- return { result: 'invalid', reason: [TX_ERROR_INSUFFICIENT_FEE_PAYER_BALANCE] };
228
+ return {
229
+ result: 'invalid',
230
+ reason: [`${TX_ERROR_INSUFFICIENT_FEE_PAYER_BALANCE} (required=${feeLimit}, available=${balance})`],
231
+ };
216
232
  }
217
233
  return { result: 'valid' };
218
234
  }
@@ -28,8 +28,6 @@ export class AztecDatastore implements Datastore {
28
28
  #memoryDatastore: Map<string, MemoryItem>;
29
29
  #dbDatastore: AztecAsyncMap<string, Uint8Array>;
30
30
 
31
- #batchOps: BatchOp[] = [];
32
-
33
31
  private maxMemoryItems: number;
34
32
 
35
33
  constructor(db: AztecAsyncKVStore, { maxMemoryItems } = { maxMemoryItems: 50 }) {
@@ -92,23 +90,17 @@ export class AztecDatastore implements Datastore {
92
90
  }
93
91
 
94
92
  batch(): Batch {
93
+ const ops: BatchOp[] = [];
95
94
  return {
96
95
  put: (key, value) => {
97
- this.#batchOps.push({
98
- type: 'put',
99
- key,
100
- value,
101
- });
96
+ ops.push({ type: 'put', key, value });
102
97
  },
103
98
  delete: key => {
104
- this.#batchOps.push({
105
- type: 'del',
106
- key,
107
- });
99
+ ops.push({ type: 'del', key });
108
100
  },
109
101
  commit: async () => {
110
102
  await this.#db.transactionAsync(async () => {
111
- for (const op of this.#batchOps) {
103
+ for (const op of ops) {
112
104
  if (op.type === 'put' && op.value) {
113
105
  await this.put(op.key, op.value);
114
106
  } else if (op.type === 'del') {
@@ -116,7 +108,7 @@ export class AztecDatastore implements Datastore {
116
108
  }
117
109
  }
118
110
  });
119
- this.#batchOps = []; // Clear operations after commit
111
+ ops.length = 0;
120
112
  },
121
113
  };
122
114
  }
@@ -1,6 +1,6 @@
1
1
  import type { EthAddress } from '@aztec/foundation/eth-address';
2
2
  import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
3
- import type { Gossipable, PeerErrorSeverity, TopicType } from '@aztec/stdlib/p2p';
3
+ import type { CheckpointProposalCore, Gossipable, PeerErrorSeverity, TopicType } from '@aztec/stdlib/p2p';
4
4
  import { Tx, TxHash } from '@aztec/stdlib/tx';
5
5
 
6
6
  import type { PeerId } from '@libp2p/interface';
@@ -86,7 +86,12 @@ export class DummyP2PService implements P2PService {
86
86
  /**
87
87
  * Register a callback into the validator client for when a checkpoint proposal is received
88
88
  */
89
- public registerCheckpointReceivedCallback(_callback: P2PCheckpointReceivedCallback) {}
89
+ public registerValidatorCheckpointReceivedCallback(_callback: P2PCheckpointReceivedCallback) {}
90
+ public registerAllNodesCheckpointReceivedCallback(_callback: P2PCheckpointReceivedCallback) {}
91
+
92
+ public notifyOwnCheckpointProposal(_checkpoint: CheckpointProposalCore): Promise<void> {
93
+ return Promise.resolve();
94
+ }
90
95
 
91
96
  /**
92
97
  * Register a callback for when a duplicate proposal is detected
@@ -282,6 +287,7 @@ export class DummyPeerManager implements PeerManagerInterface {
282
287
 
283
288
  export class DummyReqResp implements ReqRespInterface {
284
289
  updateConfig(_config: Partial<P2PReqRespConfig>): void {}
290
+ setShouldRejectPeer(): void {}
285
291
  start(
286
292
  _subProtocolHandlers: ReqRespSubProtocolHandlers,
287
293
  _subProtocolValidators: ReqRespSubProtocolValidators,
@@ -1,5 +1,5 @@
1
1
  import { TopicType, createTopicString } from '@aztec/stdlib/p2p';
2
- import { calculateMaxBlocksPerSlot } from '@aztec/stdlib/timetable';
2
+ import { createCheckpointTimingModel } from '@aztec/stdlib/timetable';
3
3
 
4
4
  import { createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
5
5
 
@@ -9,12 +9,18 @@ import { createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
9
9
  export type TopicScoringNetworkParams = {
10
10
  /** L2 slot duration in milliseconds */
11
11
  slotDurationMs: number;
12
+ /** L1 slot duration in seconds */
13
+ ethereumSlotDuration: number;
12
14
  /** Gossipsub heartbeat interval in milliseconds */
13
15
  heartbeatIntervalMs: number;
14
16
  /** Target committee size (number of validators expected to attest per slot) */
15
17
  targetCommitteeSize: number;
16
18
  /** Duration per block in milliseconds when building multiple blocks per slot. If undefined, single block mode. */
17
19
  blockDurationMs?: number;
20
+ /** Time budget in seconds reserved for L1 publishing. Defaults to ethereumSlotDuration. */
21
+ l1PublishingTime?: number;
22
+ /** One-way proposal/attestation propagation budget in seconds. */
23
+ p2pPropagationTime?: number;
18
24
  /** Expected number of block proposals per slot for scoring override. 0 disables scoring, undefined falls back to blocksPerSlot - 1. */
19
25
  expectedBlockProposalsPerSlot?: number;
20
26
  };
@@ -25,10 +31,32 @@ export type TopicScoringNetworkParams = {
25
31
  *
26
32
  * @param slotDurationMs - L2 slot duration in milliseconds
27
33
  * @param blockDurationMs - Duration per block in milliseconds (undefined = single block mode)
34
+ * @param opts - Shared checkpoint timing inputs used by the sequencer and validators
28
35
  * @returns Number of blocks per slot
29
36
  */
30
- export function calculateBlocksPerSlot(slotDurationMs: number, blockDurationMs: number | undefined): number {
31
- return calculateMaxBlocksPerSlot(slotDurationMs / 1000, blockDurationMs ? blockDurationMs / 1000 : undefined);
37
+ export function calculateBlocksPerSlot(
38
+ slotDurationMs: number,
39
+ blockDurationMs: number | undefined,
40
+ opts?: {
41
+ ethereumSlotDuration: number;
42
+ l1PublishingTime?: number;
43
+ p2pPropagationTime?: number;
44
+ },
45
+ ): number {
46
+ if (!opts) {
47
+ return createCheckpointTimingModel({
48
+ aztecSlotDuration: slotDurationMs / 1000,
49
+ blockDuration: blockDurationMs ? blockDurationMs / 1000 : undefined,
50
+ }).calculateMaxBlocksPerSlot();
51
+ }
52
+
53
+ return createCheckpointTimingModel({
54
+ aztecSlotDuration: slotDurationMs / 1000,
55
+ ethereumSlotDuration: opts.ethereumSlotDuration,
56
+ blockDuration: blockDurationMs ? blockDurationMs / 1000 : undefined,
57
+ l1PublishingTime: opts.l1PublishingTime ?? opts.ethereumSlotDuration,
58
+ p2pPropagationTime: opts.p2pPropagationTime,
59
+ }).calculateMaxBlocksPerSlot();
32
60
  }
33
61
 
34
62
  /**
@@ -279,7 +307,11 @@ export class TopicScoreParamsFactory {
279
307
  const { slotDurationMs, heartbeatIntervalMs, blockDurationMs } = params;
280
308
 
281
309
  // Compute values that are the same for all topics
282
- this.blocksPerSlot = calculateBlocksPerSlot(slotDurationMs, blockDurationMs);
310
+ this.blocksPerSlot = calculateBlocksPerSlot(slotDurationMs, blockDurationMs, {
311
+ ethereumSlotDuration: params.ethereumSlotDuration,
312
+ l1PublishingTime: params.l1PublishingTime,
313
+ p2pPropagationTime: params.p2pPropagationTime,
314
+ });
283
315
  this.heartbeatsPerSlot = slotDurationMs / heartbeatIntervalMs;
284
316
  this.invalidDecay = computeDecay(heartbeatIntervalMs, slotDurationMs, INVALID_DECAY_WINDOW_SLOTS);
285
317