@aztec/p2p 0.0.1-commit.3469e52 → 0.0.1-commit.59e663cd

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 (70) hide show
  1. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +3 -3
  2. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  3. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +3 -2
  4. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +1 -1
  5. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +3 -2
  6. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +1 -1
  7. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +3 -3
  8. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  9. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +8 -1
  10. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +3 -3
  11. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -1
  12. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +2 -0
  13. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +3 -3
  14. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  15. package/dest/msg_validators/attestation_validator/attestation_validator.js +40 -9
  16. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
  17. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  18. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +11 -4
  19. package/dest/msg_validators/clock_tolerance.d.ts +21 -0
  20. package/dest/msg_validators/clock_tolerance.d.ts.map +1 -0
  21. package/dest/msg_validators/clock_tolerance.js +37 -0
  22. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -3
  23. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  24. package/dest/msg_validators/proposal_validator/proposal_validator.js +46 -15
  25. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +3 -3
  26. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +1 -1
  27. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +66 -11
  28. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +1 -1
  29. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  30. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  31. package/dest/services/libp2p/libp2p_service.d.ts +5 -5
  32. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  33. package/dest/services/libp2p/libp2p_service.js +24 -24
  34. package/dest/services/reqresp/interface.d.ts +3 -3
  35. package/dest/services/reqresp/interface.d.ts.map +1 -1
  36. package/dest/services/reqresp/interface.js +2 -2
  37. package/dest/services/tx_collection/slow_tx_collection.d.ts +3 -3
  38. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  39. package/dest/services/tx_collection/tx_collection.d.ts +5 -5
  40. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  41. package/dest/services/tx_provider.d.ts +3 -3
  42. package/dest/services/tx_provider.d.ts.map +1 -1
  43. package/dest/test-helpers/index.d.ts +2 -1
  44. package/dest/test-helpers/index.d.ts.map +1 -1
  45. package/dest/test-helpers/index.js +1 -0
  46. package/dest/test-helpers/test_tx_provider.d.ts +40 -0
  47. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -0
  48. package/dest/test-helpers/test_tx_provider.js +41 -0
  49. package/dest/testbench/p2p_client_testbench_worker.js +2 -1
  50. package/package.json +14 -14
  51. package/src/client/p2p_client.ts +5 -5
  52. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +2 -2
  53. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +2 -1
  54. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +2 -1
  55. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +10 -7
  56. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +4 -2
  57. package/src/msg_validators/attestation_validator/attestation_validator.ts +25 -13
  58. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +6 -6
  59. package/src/msg_validators/clock_tolerance.ts +51 -0
  60. package/src/msg_validators/proposal_validator/proposal_validator.ts +21 -14
  61. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +53 -14
  62. package/src/msg_validators/tx_validator/block_header_validator.ts +3 -1
  63. package/src/services/libp2p/libp2p_service.ts +39 -29
  64. package/src/services/reqresp/interface.ts +2 -2
  65. package/src/services/tx_collection/slow_tx_collection.ts +2 -2
  66. package/src/services/tx_collection/tx_collection.ts +4 -4
  67. package/src/services/tx_provider.ts +2 -2
  68. package/src/test-helpers/index.ts +1 -0
  69. package/src/test-helpers/test_tx_provider.ts +64 -0
  70. package/src/testbench/p2p_client_testbench_worker.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
- import { type CheckpointAttestation, PeerErrorSeverity } from '@aztec/stdlib/p2p';
2
+ import { type CheckpointAttestation, PeerErrorSeverity, type ValidationResult } from '@aztec/stdlib/p2p';
3
3
  import { Attributes, Metrics, type TelemetryClient } from '@aztec/telemetry-client';
4
4
 
5
5
  import type { AttestationPool } from '../../mem_pools/attestation_pool/attestation_pool.js';
@@ -28,10 +28,10 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
28
28
  this.invalidAttestationCounter = meter.createUpDownCounter(Metrics.VALIDATOR_INVALID_ATTESTATION_RECEIVED_COUNT);
29
29
  }
30
30
 
31
- override async validate(message: CheckpointAttestation): Promise<PeerErrorSeverity | undefined> {
31
+ override async validate(message: CheckpointAttestation): Promise<ValidationResult> {
32
32
  // First run the standard validation
33
33
  const baseValidationResult = await super.validate(message);
34
- if (baseValidationResult !== undefined) {
34
+ if (baseValidationResult.result !== 'accept') {
35
35
  // Track base validation failures (invalid signature, wrong committee, etc.)
36
36
  this.invalidAttestationCounter.add(1, {
37
37
  [Attributes.ERROR_TYPE]: 'base_validation_failed',
@@ -45,7 +45,7 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
45
45
  const proposer = message.getProposer();
46
46
 
47
47
  if (!attester || !proposer) {
48
- return undefined;
48
+ return { result: 'accept' };
49
49
  }
50
50
 
51
51
  const proposalId = message.archive.toString();
@@ -74,7 +74,7 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
74
74
  });
75
75
 
76
76
  // Return error to reject the message, but LibP2PService won't penalize in fisherman mode
77
- return PeerErrorSeverity.LowToleranceError;
77
+ return { result: 'reject', severity: PeerErrorSeverity.LowToleranceError };
78
78
  }
79
79
  } else {
80
80
  // We might receive attestations before proposals in some cases
@@ -83,6 +83,6 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
83
83
  );
84
84
  }
85
85
 
86
- return undefined;
86
+ return { result: 'accept' };
87
87
  }
88
88
  }
@@ -0,0 +1,51 @@
1
+ import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
3
+
4
+ /**
5
+ * Maximum clock disparity tolerance for P2P message validation (in milliseconds).
6
+ * Messages for the previous slot are accepted if we're within this many milliseconds
7
+ * of the current slot start. This prevents penalizing peers for messages that
8
+ * were valid when sent but arrived slightly late due to network latency.
9
+ *
10
+ * This follows Ethereum's MAXIMUM_GOSSIP_CLOCK_DISPARITY approach.
11
+ */
12
+ export const MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS = 500;
13
+
14
+ /**
15
+ * Checks if a message for the previous slot should be accepted due to clock tolerance.
16
+ *
17
+ * @param messageSlot - The slot number from the received message
18
+ * @param currentSlot - The current slot number
19
+ * @param epochCache - EpochCache to get timing information
20
+ * @returns true if the message is for the previous slot AND we're within the clock tolerance window
21
+ */
22
+ export function isWithinClockTolerance(
23
+ messageSlot: SlotNumber,
24
+ currentSlot: SlotNumber,
25
+ epochCache: EpochCacheInterface,
26
+ ): boolean {
27
+ // Guard against slot 0 edge case (genesis)
28
+ if (currentSlot === SlotNumber.ZERO) {
29
+ return false;
30
+ }
31
+
32
+ // Only apply tolerance to messages for the previous slot
33
+ const previousSlot = SlotNumber(currentSlot - 1);
34
+ if (messageSlot !== previousSlot) {
35
+ return false;
36
+ }
37
+
38
+ // Check how far we are into the current slot (in milliseconds)
39
+ const { ts: slotStartTs, nowMs, slot } = epochCache.getEpochAndSlotNow();
40
+
41
+ // Sanity check: ensure the epoch cache's current slot matches the expected current slot
42
+ if (slot !== currentSlot) {
43
+ return false;
44
+ }
45
+
46
+ // ts is in seconds, convert to ms; nowMs is already in milliseconds
47
+ const slotStartMs = slotStartTs * 1000n;
48
+ const elapsedMs = Number(nowMs - slotStartMs);
49
+
50
+ return elapsedMs < MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
51
+ }
@@ -1,7 +1,9 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { NoCommitteeError } from '@aztec/ethereum/contracts';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
- import { BlockProposal, CheckpointProposal, PeerErrorSeverity } from '@aztec/stdlib/p2p';
4
+ import { BlockProposal, CheckpointProposal, PeerErrorSeverity, type ValidationResult } from '@aztec/stdlib/p2p';
5
+
6
+ import { isWithinClockTolerance } from '../clock_tolerance.js';
5
7
 
6
8
  export abstract class ProposalValidator<TProposal extends BlockProposal | CheckpointProposal> {
7
9
  protected epochCache: EpochCacheInterface;
@@ -14,30 +16,35 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
14
16
  this.logger = createLogger(loggerName);
15
17
  }
16
18
 
17
- public async validate(proposal: TProposal): Promise<PeerErrorSeverity | undefined> {
19
+ public async validate(proposal: TProposal): Promise<ValidationResult> {
18
20
  try {
19
21
  // Slot check
20
22
  const { currentSlot, nextSlot } = this.epochCache.getCurrentAndNextSlot();
21
23
  const slotNumber = proposal.slotNumber;
22
24
  if (slotNumber !== currentSlot && slotNumber !== nextSlot) {
23
- this.logger.debug(`Penalizing peer for invalid slot number ${slotNumber}`, { currentSlot, nextSlot });
24
- return PeerErrorSeverity.HighToleranceError;
25
+ // Check if message is for previous slot and within clock tolerance
26
+ if (!isWithinClockTolerance(slotNumber, currentSlot, this.epochCache)) {
27
+ this.logger.warn(`Penalizing peer for invalid slot number ${slotNumber}`, { currentSlot, nextSlot });
28
+ return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
29
+ }
30
+ this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
31
+ return { result: 'ignore' };
25
32
  }
26
33
 
27
34
  // Signature validity
28
35
  const proposer = proposal.getSender();
29
36
  if (!proposer) {
30
- this.logger.debug(`Penalizing peer for proposal with invalid signature`);
31
- return PeerErrorSeverity.MidToleranceError;
37
+ this.logger.warn(`Penalizing peer for proposal with invalid signature`);
38
+ return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
32
39
  }
33
40
 
34
41
  // Transactions permitted check
35
42
  const embeddedTxCount = proposal.txs?.length ?? 0;
36
43
  if (!this.txsPermitted && (proposal.txHashes.length > 0 || embeddedTxCount > 0)) {
37
- this.logger.debug(
44
+ this.logger.warn(
38
45
  `Penalizing peer for proposal with ${proposal.txHashes.length} transaction(s) when transactions are not permitted`,
39
46
  );
40
- return PeerErrorSeverity.MidToleranceError;
47
+ return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
41
48
  }
42
49
 
43
50
  // Embedded txs must be listed in txHashes
@@ -52,17 +59,17 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
52
59
  txHashesLength: proposal.txHashes.length,
53
60
  missingTxHashes,
54
61
  });
55
- return PeerErrorSeverity.MidToleranceError;
62
+ return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
56
63
  }
57
64
 
58
65
  // Proposer check
59
66
  const expectedProposer = await this.epochCache.getProposerAttesterAddressInSlot(slotNumber);
60
67
  if (expectedProposer !== undefined && !proposer.equals(expectedProposer)) {
61
- this.logger.debug(`Penalizing peer for invalid proposer for current slot ${slotNumber}`, {
68
+ this.logger.warn(`Penalizing peer for invalid proposer for current slot ${slotNumber}`, {
62
69
  expectedProposer,
63
70
  proposer: proposer.toString(),
64
71
  });
65
- return PeerErrorSeverity.MidToleranceError;
72
+ return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
66
73
  }
67
74
 
68
75
  // Validate tx hashes for all txs embedded in the proposal
@@ -71,13 +78,13 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
71
78
  proposer,
72
79
  slotNumber,
73
80
  });
74
- return PeerErrorSeverity.LowToleranceError;
81
+ return { result: 'reject', severity: PeerErrorSeverity.LowToleranceError };
75
82
  }
76
83
 
77
- return undefined;
84
+ return { result: 'accept' };
78
85
  } catch (e) {
79
86
  if (e instanceof NoCommitteeError) {
80
- return PeerErrorSeverity.LowToleranceError;
87
+ return { result: 'reject', severity: PeerErrorSeverity.LowToleranceError };
81
88
  }
82
89
  throw e;
83
90
  }
@@ -1,7 +1,12 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
3
3
  import type { EthAddress } from '@aztec/foundation/eth-address';
4
- import { type BlockProposal, type CheckpointProposal, PeerErrorSeverity } from '@aztec/stdlib/p2p';
4
+ import {
5
+ type BlockProposal,
6
+ type CheckpointProposal,
7
+ PeerErrorSeverity,
8
+ type ValidationResult,
9
+ } from '@aztec/stdlib/p2p';
5
10
  import type { TxHash } from '@aztec/stdlib/tx';
6
11
 
7
12
  import type { MockProxy } from 'jest-mock-extended';
@@ -10,7 +15,7 @@ export interface ProposalValidatorTestParams<TProposal extends BlockProposal | C
10
15
  validatorFactory: (
11
16
  epochCache: EpochCacheInterface,
12
17
  opts: { txsPermitted: boolean },
13
- ) => { validate: (proposal: TProposal) => Promise<PeerErrorSeverity | undefined> };
18
+ ) => { validate: (proposal: TProposal) => Promise<ValidationResult> };
14
19
  makeProposal: (options?: any) => Promise<TProposal>;
15
20
  makeHeader: (epochNumber: number | bigint, slotNumber: number | bigint, blockNumber: number | bigint) => any;
16
21
  getSigner: () => Secp256k1Signer;
@@ -29,11 +34,12 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
29
34
 
30
35
  describe('shared proposal validation logic', () => {
31
36
  let epochCache: MockProxy<EpochCacheInterface>;
32
- let validator: { validate: (proposal: TProposal) => Promise<PeerErrorSeverity | undefined> };
37
+ let validator: { validate: (proposal: TProposal) => Promise<ValidationResult> };
38
+ const previousSlot = getSlot(99);
33
39
  const currentSlot = getSlot(100);
34
40
  const nextSlot = getSlot(101);
35
41
 
36
- function mockGetProposer(currentProposer: EthAddress, nextProposer: EthAddress) {
42
+ function mockGetProposer(currentProposer: EthAddress, nextProposer: EthAddress, previousProposer?: EthAddress) {
37
43
  epochCache.getProposerAttesterAddressInSlot.mockImplementation(slot => {
38
44
  if (slot === currentSlot) {
39
45
  return Promise.resolve(currentProposer);
@@ -41,6 +47,9 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
41
47
  if (slot === nextSlot) {
42
48
  return Promise.resolve(nextProposer);
43
49
  }
50
+ if (slot === previousSlot && previousProposer) {
51
+ return Promise.resolve(previousProposer);
52
+ }
44
53
  throw new Error('Unexpected argument');
45
54
  });
46
55
  }
@@ -54,18 +63,48 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
54
63
  });
55
64
  });
56
65
 
57
- it('returns high tolerance error if slot number is not current or next slot', async () => {
66
+ it('returns high tolerance error if slot number is not current or next slot (outside clock tolerance)', async () => {
58
67
  const header = makeHeader(1, 99, 99);
59
68
  const mockProposal = await makeProposal({ blockHeader: header, lastBlockHeader: header });
60
69
 
70
+ // Mock getEpochAndSlotNow to return time OUTSIDE clock tolerance (1000ms elapsed)
71
+ epochCache.getEpochAndSlotNow.mockReturnValue({
72
+ epoch: 1 as any,
73
+ slot: currentSlot,
74
+ ts: 1000n, // slot started at 1000 seconds
75
+ nowMs: 1001000n, // 1000ms elapsed, outside 500ms tolerance
76
+ });
77
+
61
78
  epochCache.getProposerAttesterAddressInSlot.mockResolvedValue(getAddress());
62
79
  const result = await validator.validate(mockProposal);
63
- expect(result).toBe(PeerErrorSeverity.HighToleranceError);
80
+ expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.HighToleranceError });
64
81
 
65
82
  // Should not try to resolve proposers if base validation fails
66
83
  expect(epochCache.getProposerAttesterAddressInSlot).not.toHaveBeenCalled();
67
84
  });
68
85
 
86
+ it('returns ignore if previous slot proposal is within clock tolerance', async () => {
87
+ const previousProposer = getSigner();
88
+ const header = makeHeader(1, 99, 99);
89
+ const mockProposal = await makeProposal({
90
+ blockHeader: header,
91
+ lastBlockHeader: header,
92
+ signer: previousProposer,
93
+ });
94
+
95
+ // Mock getEpochAndSlotNow to return time WITHIN clock tolerance (100ms elapsed)
96
+ epochCache.getEpochAndSlotNow.mockReturnValue({
97
+ epoch: 1 as any,
98
+ slot: currentSlot,
99
+ ts: 1000n, // slot started at 1000 seconds
100
+ nowMs: 1000100n, // 100ms elapsed, within 500ms tolerance
101
+ });
102
+
103
+ mockGetProposer(getAddress(), getAddress(), getAddress(previousProposer));
104
+ const result = await validator.validate(mockProposal);
105
+ expect(result).toEqual({ result: 'ignore' });
106
+ });
107
+
69
108
  it('returns mid tolerance error if proposer is not current proposer for current slot', async () => {
70
109
  const currentProposer = getSigner();
71
110
  const nextProposer = getSigner();
@@ -79,7 +118,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
79
118
 
80
119
  mockGetProposer(getAddress(currentProposer), getAddress(nextProposer));
81
120
  const result = await validator.validate(mockProposal);
82
- expect(result).toBe(PeerErrorSeverity.MidToleranceError);
121
+ expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
83
122
  });
84
123
 
85
124
  it('returns mid tolerance error if proposer is not next proposer for next slot', async () => {
@@ -95,7 +134,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
95
134
 
96
135
  mockGetProposer(getAddress(currentProposer), getAddress(nextProposer));
97
136
  const result = await validator.validate(mockProposal);
98
- expect(result).toBe(PeerErrorSeverity.MidToleranceError);
137
+ expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
99
138
  });
100
139
 
101
140
  it('returns mid tolerance error if proposer is current proposer but proposal is for next slot', async () => {
@@ -110,7 +149,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
110
149
 
111
150
  mockGetProposer(getAddress(currentProposer), getAddress(nextProposer));
112
151
  const result = await validator.validate(mockProposal);
113
- expect(result).toBe(PeerErrorSeverity.MidToleranceError);
152
+ expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
114
153
  });
115
154
 
116
155
  it('returns undefined if proposal is valid for current slot and proposer', async () => {
@@ -125,7 +164,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
125
164
 
126
165
  mockGetProposer(getAddress(currentProposer), getAddress(nextProposer));
127
166
  const result = await validator.validate(mockProposal);
128
- expect(result).toBeUndefined();
167
+ expect(result).toEqual({ result: 'accept' });
129
168
  });
130
169
 
131
170
  it('returns undefined if proposal is valid for next slot and proposer', async () => {
@@ -136,7 +175,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
136
175
 
137
176
  mockGetProposer(getAddress(currentProposer), getAddress(nextProposer));
138
177
  const result = await validator.validate(mockProposal);
139
- expect(result).toBeUndefined();
178
+ expect(result).toEqual({ result: 'accept' });
140
179
  });
141
180
 
142
181
  describe('transaction permission validation', () => {
@@ -153,7 +192,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
153
192
 
154
193
  mockGetProposer(getAddress(currentProposer), getAddress());
155
194
  const result = await validatorWithTxsDisabled.validate(mockProposal);
156
- expect(result).toBe(PeerErrorSeverity.MidToleranceError);
195
+ expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
157
196
  });
158
197
 
159
198
  it('returns undefined if txs not permitted but proposal has no txHashes', async () => {
@@ -169,7 +208,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
169
208
 
170
209
  mockGetProposer(getAddress(currentProposer), getAddress());
171
210
  const result = await validatorWithTxsDisabled.validate(mockProposal);
172
- expect(result).toBeUndefined();
211
+ expect(result).toEqual({ result: 'accept' });
173
212
  });
174
213
 
175
214
  it('returns undefined if txs permitted and proposal contains txHashes', async () => {
@@ -184,7 +223,7 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
184
223
 
185
224
  mockGetProposer(getAddress(currentProposer), getAddress());
186
225
  const result = await validator.validate(mockProposal);
187
- expect(result).toBeUndefined();
226
+ expect(result).toEqual({ result: 'accept' });
188
227
  });
189
228
  });
190
229
  });
@@ -15,7 +15,9 @@ export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
15
15
  }
16
16
 
17
17
  async validateTx(tx: T): Promise<TxValidationResult> {
18
- const [index] = await this.#archiveSource.getArchiveIndices([await tx.data.constants.anchorBlockHeader.hash()]);
18
+ const [index] = await this.#archiveSource.getArchiveIndices([
19
+ (await tx.data.constants.anchorBlockHeader.hash()).toField(),
20
+ ]);
19
21
  if (index === undefined) {
20
22
  this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for referencing an unknown block header`);
21
23
  return { result: 'invalid', reason: [TX_ERROR_BLOCK_HEADER] };
@@ -8,7 +8,7 @@ import { Timer } from '@aztec/foundation/timer';
8
8
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
9
9
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
10
  import { protocolContractsHash } from '@aztec/protocol-contracts';
11
- import type { EthAddress, L2BlockNew, L2BlockSource } from '@aztec/stdlib/block';
11
+ import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
12
12
  import type { ContractDataSource } from '@aztec/stdlib/contract';
13
13
  import { GasFees } from '@aztec/stdlib/gas';
14
14
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
@@ -20,6 +20,7 @@ import {
20
20
  type Gossipable,
21
21
  P2PClientType,
22
22
  P2PMessage,
23
+ type ValidationResult as P2PValidationResult,
23
24
  PeerErrorSeverity,
24
25
  TopicType,
25
26
  createTopicString,
@@ -924,7 +925,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
924
925
  const validationFunc: () => Promise<ReceivedMessageValidationResult<CheckpointAttestation>> = async () => {
925
926
  const attestation = CheckpointAttestation.fromBuffer(payloadData);
926
927
  const pool = this.mempools.attestationPool;
927
- const isValid = await this.validateCheckpointAttestation(source, attestation);
928
+ const validationResult = await this.validateCheckpointAttestation(source, attestation);
929
+ const isValid = validationResult.result === 'accept';
928
930
  const exists = isValid && (await pool.hasCheckpointAttestation(attestation));
929
931
 
930
932
  let canAdd = true;
@@ -943,9 +945,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
943
945
  [Attributes.P2P_ID]: source.toString(),
944
946
  });
945
947
 
946
- if (!isValid) {
948
+ if (validationResult.result === 'reject') {
947
949
  return { result: TopicValidatorResult.Reject };
948
- } else if (exists) {
950
+ } else if (validationResult.result === 'ignore' || exists) {
949
951
  return { result: TopicValidatorResult.Ignore, obj: attestation };
950
952
  } else if (!canAdd) {
951
953
  this.logger.warn(`Dropping checkpoint attestation due to per-(slot, proposalId) attestation cap`, {
@@ -986,7 +988,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
986
988
  private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
987
989
  const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
988
990
  const block = BlockProposal.fromBuffer(payloadData);
989
- const isValid = await this.validateBlockProposal(source, block);
991
+ const validationResult = await this.validateBlockProposal(source, block);
992
+ const isValid = validationResult.result === 'accept';
990
993
  const pool = this.mempools.attestationPool;
991
994
 
992
995
  const exists = isValid && (await pool.hasBlockProposal(block));
@@ -1000,9 +1003,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1000
1003
  [Attributes.P2P_ID]: source.toString(),
1001
1004
  });
1002
1005
 
1003
- if (!isValid) {
1006
+ if (validationResult.result === 'reject') {
1004
1007
  return { result: TopicValidatorResult.Reject };
1005
- } else if (exists) {
1008
+ } else if (validationResult.result === 'ignore' || exists) {
1006
1009
  return { result: TopicValidatorResult.Ignore, obj: block };
1007
1010
  } else if (!canAdd) {
1008
1011
  this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
@@ -1082,7 +1085,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1082
1085
  // TODO(palla/mbps): This pattern is repeated across multiple message handlers, consider abstracting it.
1083
1086
  const validationFunc: () => Promise<ReceivedMessageValidationResult<CheckpointProposal>> = async () => {
1084
1087
  const checkpoint = CheckpointProposal.fromBuffer(payloadData);
1085
- const isValid = await this.validateCheckpointProposal(source, checkpoint);
1088
+ const validationResult = await this.validateCheckpointProposal(source, checkpoint);
1089
+ const isValid = validationResult.result === 'accept';
1086
1090
  const pool = this.mempools.attestationPool;
1087
1091
 
1088
1092
  const exists = isValid && (await pool.hasCheckpointProposal(checkpoint));
@@ -1096,9 +1100,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1096
1100
  [Attributes.P2P_ID]: source.toString(),
1097
1101
  });
1098
1102
 
1099
- if (!isValid) {
1103
+ if (validationResult.result === 'reject') {
1100
1104
  return { result: TopicValidatorResult.Reject };
1101
- } else if (exists) {
1105
+ } else if (validationResult.result === 'ignore' || exists) {
1102
1106
  return { result: TopicValidatorResult.Ignore, obj: checkpoint };
1103
1107
  } else if (!canAdd) {
1104
1108
  this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
@@ -1337,7 +1341,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1337
1341
  }))
1338
1342
  private async validateRequestedBlock(
1339
1343
  requestedBlockNumber: Fr,
1340
- responseBlock: L2BlockNew,
1344
+ responseBlock: L2Block,
1341
1345
  peerId: PeerId,
1342
1346
  ): Promise<boolean> {
1343
1347
  try {
@@ -1576,15 +1580,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1576
1580
  [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
1577
1581
  [Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
1578
1582
  }))
1579
- public async validateCheckpointAttestation(peerId: PeerId, attestation: CheckpointAttestation): Promise<boolean> {
1580
- const severity = await this.checkpointAttestationValidator.validate(attestation);
1581
- if (severity) {
1583
+ public async validateCheckpointAttestation(
1584
+ peerId: PeerId,
1585
+ attestation: CheckpointAttestation,
1586
+ ): Promise<P2PValidationResult> {
1587
+ const result = await this.checkpointAttestationValidator.validate(attestation);
1588
+
1589
+ if (result.result === 'reject') {
1582
1590
  this.logger.debug(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
1583
- this.peerManager.penalizePeer(peerId, severity);
1584
- return false;
1591
+ this.peerManager.penalizePeer(peerId, result.severity);
1585
1592
  }
1586
1593
 
1587
- return true;
1594
+ return result;
1588
1595
  }
1589
1596
 
1590
1597
  /**
@@ -1596,15 +1603,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1596
1603
  @trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
1597
1604
  [Attributes.SLOT_NUMBER]: block.slotNumber.toString(),
1598
1605
  }))
1599
- public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
1600
- const severity = await this.blockProposalValidator.validate(block);
1601
- if (severity) {
1606
+ public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<P2PValidationResult> {
1607
+ const result = await this.blockProposalValidator.validate(block);
1608
+
1609
+ if (result.result === 'reject') {
1602
1610
  this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
1603
- this.peerManager.penalizePeer(peerId, severity);
1604
- return false;
1611
+ this.peerManager.penalizePeer(peerId, result.severity);
1605
1612
  }
1606
1613
 
1607
- return true;
1614
+ return result;
1608
1615
  }
1609
1616
 
1610
1617
  /**
@@ -1616,15 +1623,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1616
1623
  @trackSpan('Libp2pService.validateCheckpointProposal', (_peerId, checkpoint) => ({
1617
1624
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1618
1625
  }))
1619
- public async validateCheckpointProposal(peerId: PeerId, checkpoint: CheckpointProposal): Promise<boolean> {
1620
- const severity = await this.checkpointProposalValidator.validate(checkpoint);
1621
- if (severity) {
1626
+ public async validateCheckpointProposal(
1627
+ peerId: PeerId,
1628
+ checkpoint: CheckpointProposal,
1629
+ ): Promise<P2PValidationResult> {
1630
+ const result = await this.checkpointProposalValidator.validate(checkpoint);
1631
+
1632
+ if (result.result === 'reject') {
1622
1633
  this.logger.debug(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
1623
- this.peerManager.penalizePeer(peerId, severity);
1624
- return false;
1634
+ this.peerManager.penalizePeer(peerId, result.severity);
1625
1635
  }
1626
1636
 
1627
- return true;
1637
+ return result;
1628
1638
  }
1629
1639
 
1630
1640
  public getPeerScore(peerId: PeerId): number {
@@ -1,5 +1,5 @@
1
1
  import { Fr } from '@aztec/foundation/curves/bn254';
2
- import { L2BlockNew } from '@aztec/stdlib/block';
2
+ import { L2Block } from '@aztec/stdlib/block';
3
3
  import { TxArray, TxHashArray } from '@aztec/stdlib/tx';
4
4
 
5
5
  import type { PeerId } from '@libp2p/interface';
@@ -198,7 +198,7 @@ export const subProtocolMap = {
198
198
  },
199
199
  [ReqRespSubProtocol.BLOCK]: {
200
200
  request: Fr, // block number
201
- response: L2BlockNew,
201
+ response: L2Block,
202
202
  },
203
203
  [ReqRespSubProtocol.AUTH]: {
204
204
  request: AuthRequest,
@@ -4,7 +4,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { boundInclusive } from '@aztec/foundation/number';
5
5
  import { RunningPromise } from '@aztec/foundation/promise';
6
6
  import { DateProvider } from '@aztec/foundation/timer';
7
- import type { L2BlockNew } from '@aztec/stdlib/block';
7
+ import type { L2Block } from '@aztec/stdlib/block';
8
8
  import { type L1RollupConstants, getEpochAtSlot, getTimestampRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
9
9
  import { type Tx, TxHash } from '@aztec/stdlib/tx';
10
10
 
@@ -76,7 +76,7 @@ export class SlowTxCollection {
76
76
  }
77
77
 
78
78
  /** Starts collecting the given tx hashes for the given L2Block in the slow loop */
79
- public startCollecting(block: L2BlockNew, txHashes: TxHash[]) {
79
+ public startCollecting(block: L2Block, txHashes: TxHash[]) {
80
80
  const slot = block.header.getSlot();
81
81
  const deadline = this.getDeadlineForSlot(slot);
82
82
  if (+deadline < this.dateProvider.now()) {
@@ -3,7 +3,7 @@ import { compactArray } from '@aztec/foundation/collection';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { type PromiseWithResolvers, RunningPromise } from '@aztec/foundation/promise';
5
5
  import { DateProvider } from '@aztec/foundation/timer';
6
- import type { L2BlockInfo, L2BlockNew } from '@aztec/stdlib/block';
6
+ import type { L2Block, L2BlockInfo } from '@aztec/stdlib/block';
7
7
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
8
8
  import type { BlockProposal } from '@aztec/stdlib/p2p';
9
9
  import { Tx, TxHash } from '@aztec/stdlib/tx';
@@ -25,7 +25,7 @@ export type CollectionMethod = 'fast-req-resp' | 'fast-node-rpc' | 'slow-req-res
25
25
  export type MissingTxInfo = { blockNumber: BlockNumber; deadline: Date; readyForReqResp: boolean };
26
26
 
27
27
  export type FastCollectionRequestInput =
28
- | { type: 'block'; block: L2BlockNew }
28
+ | { type: 'block'; block: L2Block }
29
29
  | { type: 'proposal'; blockProposal: BlockProposal; blockNumber: BlockNumber };
30
30
 
31
31
  export type FastCollectionRequest = FastCollectionRequestInput & {
@@ -146,7 +146,7 @@ export class TxCollection {
146
146
  }
147
147
 
148
148
  /** Starts collecting the given tx hashes for the given L2Block in the slow loop */
149
- public startCollecting(block: L2BlockNew, txHashes: TxHash[]) {
149
+ public startCollecting(block: L2Block, txHashes: TxHash[]) {
150
150
  return this.slowCollection.startCollecting(block, txHashes);
151
151
  }
152
152
 
@@ -162,7 +162,7 @@ export class TxCollection {
162
162
 
163
163
  /** Collects the set of txs for the given mined block as fast as possible */
164
164
  public collectFastForBlock(
165
- block: L2BlockNew,
165
+ block: L2Block,
166
166
  txHashes: TxHash[] | string[],
167
167
  opts: { deadline: Date; pinnedPeer?: PeerId },
168
168
  ) {
@@ -2,7 +2,7 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { compactArray } from '@aztec/foundation/collection';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { elapsed } from '@aztec/foundation/timer';
5
- import type { L2BlockInfo, L2BlockNew } from '@aztec/stdlib/block';
5
+ import type { L2Block, L2BlockInfo } from '@aztec/stdlib/block';
6
6
  import type { ITxProvider } from '@aztec/stdlib/interfaces/server';
7
7
  import type { BlockProposal } from '@aztec/stdlib/p2p';
8
8
  import { Tx, TxHash } from '@aztec/stdlib/tx';
@@ -68,7 +68,7 @@ export class TxProvider implements ITxProvider {
68
68
  }
69
69
 
70
70
  /** Gathers txs from the tx pool, remote rpc nodes, and reqresp. */
71
- public getTxsForBlock(block: L2BlockNew, opts: { deadline: Date }): Promise<{ txs: Tx[]; missingTxs: TxHash[] }> {
71
+ public getTxsForBlock(block: L2Block, opts: { deadline: Date }): Promise<{ txs: Tx[]; missingTxs: TxHash[] }> {
72
72
  return this.getOrderedTxsFromAllSources(
73
73
  { type: 'block', block },
74
74
  block.toBlockInfo(),
@@ -4,3 +4,4 @@ export * from './make-enrs.js';
4
4
  export * from './make-test-p2p-clients.js';
5
5
  export * from './reqresp-nodes.js';
6
6
  export * from './mock-pubsub.js';
7
+ export * from './test_tx_provider.js';