@aztec/p2p 3.0.0-nightly.20251016 → 3.0.0-nightly.20251022

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 (85) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/bootstrap/bootstrap.js +2 -2
  3. package/dest/client/factory.d.ts +1 -0
  4. package/dest/client/factory.d.ts.map +1 -1
  5. package/dest/client/factory.js +4 -2
  6. package/dest/client/interface.d.ts +1 -1
  7. package/dest/client/interface.d.ts.map +1 -1
  8. package/dest/client/p2p_client.d.ts +2 -1
  9. package/dest/client/p2p_client.d.ts.map +1 -1
  10. package/dest/client/p2p_client.js +5 -0
  11. package/dest/enr/generate-enr.d.ts +1 -1
  12. package/dest/enr/generate-enr.d.ts.map +1 -1
  13. package/dest/enr/generate-enr.js +1 -1
  14. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +2 -2
  15. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  16. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +18 -2
  17. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  18. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +21 -7
  19. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  20. package/dest/mem_pools/attestation_pool/mocks.js +5 -3
  21. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +1 -0
  22. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  23. package/dest/msg_validators/attestation_validator/attestation_validator.js +25 -2
  24. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  25. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +16 -3
  26. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  27. package/dest/msg_validators/tx_validator/factory.js +8 -2
  28. package/dest/msg_validators/tx_validator/index.d.ts +1 -0
  29. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  30. package/dest/msg_validators/tx_validator/index.js +1 -0
  31. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +0 -3
  32. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  33. package/dest/msg_validators/tx_validator/metadata_validator.js +1 -19
  34. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +12 -0
  35. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -0
  36. package/dest/msg_validators/tx_validator/timestamp_validator.js +32 -0
  37. package/dest/services/discv5/discV5_service.d.ts +2 -2
  38. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  39. package/dest/services/discv5/discV5_service.js +2 -2
  40. package/dest/services/dummy_service.d.ts +1 -1
  41. package/dest/services/dummy_service.d.ts.map +1 -1
  42. package/dest/services/libp2p/libp2p_service.d.ts +11 -1
  43. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  44. package/dest/services/libp2p/libp2p_service.js +63 -21
  45. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  46. package/dest/services/peer-manager/peer_manager.js +9 -3
  47. package/dest/services/service.d.ts +1 -1
  48. package/dest/services/service.d.ts.map +1 -1
  49. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  50. package/dest/services/tx_collection/tx_collection_sink.js +34 -4
  51. package/dest/test-helpers/make-enrs.js +1 -1
  52. package/dest/test-helpers/mock-tx-helpers.d.ts +12 -0
  53. package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -0
  54. package/dest/test-helpers/mock-tx-helpers.js +19 -0
  55. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  56. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  57. package/dest/test-helpers/reqresp-nodes.js +1 -1
  58. package/dest/versioning.d.ts +1 -1
  59. package/dest/versioning.d.ts.map +1 -1
  60. package/package.json +17 -17
  61. package/src/bootstrap/bootstrap.ts +2 -2
  62. package/src/client/factory.ts +9 -2
  63. package/src/client/interface.ts +1 -1
  64. package/src/client/p2p_client.ts +5 -1
  65. package/src/enr/generate-enr.ts +1 -1
  66. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +2 -2
  67. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +22 -2
  68. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +24 -7
  69. package/src/mem_pools/attestation_pool/mocks.ts +6 -3
  70. package/src/msg_validators/attestation_validator/attestation_validator.ts +35 -2
  71. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +18 -3
  72. package/src/msg_validators/tx_validator/factory.ts +8 -2
  73. package/src/msg_validators/tx_validator/index.ts +1 -0
  74. package/src/msg_validators/tx_validator/metadata_validator.ts +0 -34
  75. package/src/msg_validators/tx_validator/timestamp_validator.ts +46 -0
  76. package/src/services/discv5/discV5_service.ts +2 -2
  77. package/src/services/dummy_service.ts +1 -1
  78. package/src/services/libp2p/libp2p_service.ts +85 -25
  79. package/src/services/peer-manager/peer_manager.ts +10 -3
  80. package/src/services/service.ts +1 -1
  81. package/src/services/tx_collection/tx_collection_sink.ts +34 -3
  82. package/src/test-helpers/make-enrs.ts +1 -1
  83. package/src/test-helpers/mock-tx-helpers.ts +24 -0
  84. package/src/test-helpers/reqresp-nodes.ts +1 -1
  85. package/src/versioning.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/p2p",
3
- "version": "3.0.0-nightly.20251016",
3
+ "version": "3.0.0-nightly.20251022",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -67,19 +67,17 @@
67
67
  ]
68
68
  },
69
69
  "dependencies": {
70
- "@aztec/constants": "3.0.0-nightly.20251016",
71
- "@aztec/epoch-cache": "3.0.0-nightly.20251016",
72
- "@aztec/ethereum": "3.0.0-nightly.20251016",
73
- "@aztec/foundation": "3.0.0-nightly.20251016",
74
- "@aztec/kv-store": "3.0.0-nightly.20251016",
75
- "@aztec/noir-contracts.js": "3.0.0-nightly.20251016",
76
- "@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251016",
77
- "@aztec/protocol-contracts": "3.0.0-nightly.20251016",
78
- "@aztec/simulator": "3.0.0-nightly.20251016",
79
- "@aztec/stdlib": "3.0.0-nightly.20251016",
80
- "@aztec/telemetry-client": "3.0.0-nightly.20251016",
81
- "@chainsafe/discv5": "9.0.0",
82
- "@chainsafe/enr": "3.0.0",
70
+ "@aztec/constants": "3.0.0-nightly.20251022",
71
+ "@aztec/epoch-cache": "3.0.0-nightly.20251022",
72
+ "@aztec/ethereum": "3.0.0-nightly.20251022",
73
+ "@aztec/foundation": "3.0.0-nightly.20251022",
74
+ "@aztec/kv-store": "3.0.0-nightly.20251022",
75
+ "@aztec/noir-contracts.js": "3.0.0-nightly.20251022",
76
+ "@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251022",
77
+ "@aztec/protocol-contracts": "3.0.0-nightly.20251022",
78
+ "@aztec/simulator": "3.0.0-nightly.20251022",
79
+ "@aztec/stdlib": "3.0.0-nightly.20251022",
80
+ "@aztec/telemetry-client": "3.0.0-nightly.20251022",
83
81
  "@chainsafe/libp2p-gossipsub": "13.0.0",
84
82
  "@chainsafe/libp2p-noise": "^15.0.0",
85
83
  "@chainsafe/libp2p-yamux": "^6.0.2",
@@ -94,6 +92,8 @@
94
92
  "@libp2p/prometheus-metrics": "^4.2.4",
95
93
  "@libp2p/tcp": "9.0.24",
96
94
  "@multiformats/multiaddr": "12.1.14",
95
+ "@nethermindeth/discv5": "9.0.0-backport-306-v4",
96
+ "@nethermindeth/enr": "3.0.0-backport-306-v4",
97
97
  "interface-datastore": "^8.2.11",
98
98
  "interface-store": "^5.1.8",
99
99
  "libp2p": "1.5.0",
@@ -104,8 +104,8 @@
104
104
  "xxhash-wasm": "^1.1.0"
105
105
  },
106
106
  "devDependencies": {
107
- "@aztec/archiver": "3.0.0-nightly.20251016",
108
- "@aztec/world-state": "3.0.0-nightly.20251016",
107
+ "@aztec/archiver": "3.0.0-nightly.20251022",
108
+ "@aztec/world-state": "3.0.0-nightly.20251022",
109
109
  "@jest/globals": "^30.0.0",
110
110
  "@types/jest": "^30.0.0",
111
111
  "@types/node": "^22.15.17",
@@ -117,7 +117,7 @@
117
117
  "ts-node": "^10.9.1",
118
118
  "typescript": "^5.3.3",
119
119
  "uint8arrays": "^5.0.3",
120
- "viem": "2.23.7"
120
+ "viem": "npm:@spalladino/viem@2.38.2-eip7594.0"
121
121
  },
122
122
  "files": [
123
123
  "dest",
@@ -3,10 +3,10 @@ import type { AztecAsyncKVStore } from '@aztec/kv-store';
3
3
  import type { P2PBootstrapApi } from '@aztec/stdlib/interfaces/server';
4
4
  import { OtelMetricsAdapter, type TelemetryClient } from '@aztec/telemetry-client';
5
5
 
6
- import { Discv5, type Discv5EventEmitter } from '@chainsafe/discv5';
7
- import { ENR, type SignableENR } from '@chainsafe/enr';
8
6
  import type { PeerId } from '@libp2p/interface';
9
7
  import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';
8
+ import { Discv5, type Discv5EventEmitter } from '@nethermindeth/discv5';
9
+ import { ENR, type SignableENR } from '@nethermindeth/enr';
10
10
 
11
11
  import type { BootnodeConfig } from '../config.js';
12
12
  import { createBootnodeENRandPeerId } from '../enr/generate-enr.js';
@@ -14,7 +14,7 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien
14
14
  import { P2PClient } from '../client/p2p_client.js';
15
15
  import type { P2PConfig } from '../config.js';
16
16
  import type { AttestationPool } from '../mem_pools/attestation_pool/attestation_pool.js';
17
- import { InMemoryAttestationPool } from '../mem_pools/attestation_pool/memory_attestation_pool.js';
17
+ import { KvAttestationPool } from '../mem_pools/attestation_pool/kv_attestation_pool.js';
18
18
  import type { MemPools } from '../mem_pools/interface.js';
19
19
  import { AztecKVTxPool, type TxPool } from '../mem_pools/tx_pool/index.js';
20
20
  import { DummyP2PService } from '../services/dummy_service.js';
@@ -35,6 +35,7 @@ export type P2PClientDeps<T extends P2PClientType> = {
35
35
  export const P2P_STORE_NAME = 'p2p';
36
36
  export const P2P_ARCHIVE_STORE_NAME = 'p2p-archive';
37
37
  export const P2P_PEER_STORE_NAME = 'p2p-peers';
38
+ export const P2P_ATTESTATION_STORE_NAME = 'p2p-attestation';
38
39
 
39
40
  export async function createP2PClient<T extends P2PClientType>(
40
41
  clientType: T,
@@ -64,6 +65,12 @@ export async function createP2PClient<T extends P2PClientType>(
64
65
  const store = deps.store ?? (await createStore(P2P_STORE_NAME, 2, config, createLogger('p2p:lmdb-v2')));
65
66
  const archive = await createStore(P2P_ARCHIVE_STORE_NAME, 1, config, createLogger('p2p-archive:lmdb-v2'));
66
67
  const peerStore = await createStore(P2P_PEER_STORE_NAME, 1, config, createLogger('p2p-peer:lmdb-v2'));
68
+ const attestationStore = await createStore(
69
+ P2P_ATTESTATION_STORE_NAME,
70
+ 1,
71
+ config,
72
+ createLogger('p2p-attestation:lmdb-v2'),
73
+ );
67
74
  const l1Constants = await archiver.getL1Constants();
68
75
 
69
76
  const mempools: MemPools<T> = {
@@ -75,7 +82,7 @@ export async function createP2PClient<T extends P2PClientType>(
75
82
  }),
76
83
  attestationPool:
77
84
  clientType === P2PClientType.Full
78
- ? ((deps.attestationPool ?? new InMemoryAttestationPool(telemetry)) as T extends P2PClientType.Full
85
+ ? ((deps.attestationPool ?? new KvAttestationPool(attestationStore, telemetry)) as T extends P2PClientType.Full
79
86
  ? AttestationPool
80
87
  : undefined)
81
88
  : undefined,
@@ -3,8 +3,8 @@ import type { P2PApiFull } from '@aztec/stdlib/interfaces/server';
3
3
  import type { BlockProposal, P2PClientType } from '@aztec/stdlib/p2p';
4
4
  import type { Tx, TxHash } from '@aztec/stdlib/tx';
5
5
 
6
- import type { ENR } from '@chainsafe/enr';
7
6
  import type { PeerId } from '@libp2p/interface';
7
+ import type { ENR } from '@nethermindeth/enr';
8
8
 
9
9
  import type { P2PConfig } from '../config.js';
10
10
  import type { AuthRequest, StatusMessage } from '../services/index.js';
@@ -26,8 +26,8 @@ import {
26
26
  trackSpan,
27
27
  } from '@aztec/telemetry-client';
28
28
 
29
- import type { ENR } from '@chainsafe/enr';
30
29
  import type { PeerId } from '@libp2p/interface';
30
+ import type { ENR } from '@nethermindeth/enr';
31
31
 
32
32
  import { type P2PConfig, getP2PDefaultConfig } from '../config.js';
33
33
  import type { AttestationPool } from '../mem_pools/attestation_pool/attestation_pool.js';
@@ -387,6 +387,10 @@ export class P2PClient<T extends P2PClientType = P2PClientType.Full>
387
387
  return this.attestationPool?.addAttestations(attestations) ?? Promise.resolve();
388
388
  }
389
389
 
390
+ public deleteAttestation(attestation: BlockAttestation): Promise<void> {
391
+ return this.attestationPool?.deleteAttestations([attestation]) ?? Promise.resolve();
392
+ }
393
+
390
394
  // REVIEW: https://github.com/AztecProtocol/aztec-packages/issues/7963
391
395
  // ^ This pattern is not my favorite (md)
392
396
  public registerBlockProposalHandler(handler: P2PBlockReceivedCallback): void {
@@ -2,9 +2,9 @@ import type { LogFn } from '@aztec/foundation/log';
2
2
  import { type ChainConfig, emptyChainConfig } from '@aztec/stdlib/config';
3
3
  import type { ComponentsVersions } from '@aztec/stdlib/versioning';
4
4
 
5
- import { ENR, SignableENR } from '@chainsafe/enr';
6
5
  import type { PeerId } from '@libp2p/interface';
7
6
  import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';
7
+ import { ENR, SignableENR } from '@nethermindeth/enr';
8
8
 
9
9
  import { AZTEC_ENR_CLIENT_VERSION_KEY, AZTEC_ENR_KEY } from '../types/index.js';
10
10
  import { convertToMultiaddr, createLibP2PPeerIdFromPrivateKey } from '../util.js';
@@ -117,7 +117,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
117
117
  const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(BigInt(slotNumber), archive.toString());
118
118
  expect(retreivedAttestations.length).toBe(1);
119
119
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer());
120
- expect(retreivedAttestations[0].getSender().toString()).toEqual(signer.address.toString());
120
+ expect(retreivedAttestations[0].getSender()?.toString()).toEqual(signer.address.toString());
121
121
 
122
122
  // Try adding them on another operation and check they are still not duplicated
123
123
  await ap.addAttestations([attestations[0]]);
@@ -291,7 +291,7 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
291
291
  expect(retrievedProposal).toBeDefined();
292
292
  // Should have the second proposal
293
293
  expect(retrievedProposal!.toBuffer()).toEqual(proposal2.toBuffer());
294
- expect(retrievedProposal!.getSender().toString()).toBe(signers[1].address.toString());
294
+ expect(retrievedProposal!.getSender()?.toString()).toBe(signers[1].address.toString());
295
295
  });
296
296
 
297
297
  it('should handle block proposals with different slots and same archive', async () => {
@@ -66,7 +66,19 @@ export class KvAttestationPool implements AttestationPool {
66
66
  for (const attestation of attestations) {
67
67
  const slotNumber = attestation.payload.header.slotNumber;
68
68
  const proposalId = attestation.archive;
69
- const address = attestation.getSender().toString();
69
+ const sender = attestation.getSender();
70
+
71
+ // Skip attestations with invalid signatures
72
+ if (!sender) {
73
+ this.log.warn(`Skipping attestation with invalid signature for slot ${slotNumber.toBigInt()}`, {
74
+ signature: attestation.signature.toString(),
75
+ slotNumber,
76
+ proposalId,
77
+ });
78
+ continue;
79
+ }
80
+
81
+ const address = sender.toString();
70
82
 
71
83
  await this.attestations.set(this.getAttestationKey(slotNumber, proposalId, address), attestation.toBuffer());
72
84
 
@@ -176,7 +188,15 @@ export class KvAttestationPool implements AttestationPool {
176
188
  for (const attestation of attestations) {
177
189
  const slotNumber = attestation.payload.header.slotNumber;
178
190
  const proposalId = attestation.archive;
179
- const address = attestation.getSender().toString();
191
+ const sender = attestation.getSender();
192
+
193
+ // Skip attestations with invalid signatures
194
+ if (!sender) {
195
+ this.log.warn(`Skipping deletion of attestation with invalid signature for slot ${slotNumber.toBigInt()}`);
196
+ continue;
197
+ }
198
+
199
+ const address = sender.toString();
180
200
  const key = this.getAttestationKey(slotNumber, proposalId, address);
181
201
 
182
202
  if (await this.attestations.hasAsync(key)) {
@@ -55,16 +55,26 @@ export class InMemoryAttestationPool implements AttestationPool {
55
55
  const slotNumber = attestation.payload.header.slotNumber;
56
56
 
57
57
  const proposalId = attestation.archive.toString();
58
- const address = attestation.getSender();
58
+ const sender = attestation.getSender();
59
+
60
+ // Skip attestations with invalid signatures
61
+ if (!sender) {
62
+ this.log.warn(`Skipping attestation with invalid signature for slot ${slotNumber.toBigInt()}`, {
63
+ signature: attestation.signature.toString(),
64
+ slotNumber,
65
+ proposalId,
66
+ });
67
+ continue;
68
+ }
59
69
 
60
70
  const slotAttestationMap = getSlotOrDefault(this.attestations, slotNumber.toBigInt());
61
71
  const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
62
- proposalAttestationMap.set(address.toString(), attestation);
72
+ proposalAttestationMap.set(sender.toString(), attestation);
63
73
 
64
- this.log.verbose(`Added attestation for slot ${slotNumber.toBigInt()} from ${address}`, {
74
+ this.log.verbose(`Added attestation for slot ${slotNumber.toBigInt()} from ${sender}`, {
65
75
  signature: attestation.signature.toString(),
66
76
  slotNumber,
67
- address,
77
+ address: sender,
68
78
  proposalId,
69
79
  });
70
80
  }
@@ -147,9 +157,16 @@ export class InMemoryAttestationPool implements AttestationPool {
147
157
  const proposalId = attestation.archive.toString();
148
158
  const proposalAttestationMap = getProposalOrDefault(slotAttestationMap, proposalId);
149
159
  if (proposalAttestationMap) {
150
- const address = attestation.getSender();
151
- proposalAttestationMap.delete(address.toString());
152
- this.log.debug(`Deleted attestation for slot ${slotNumber} from ${address}`);
160
+ const sender = attestation.getSender();
161
+
162
+ // Skip attestations with invalid signatures
163
+ if (!sender) {
164
+ this.log.warn(`Skipping deletion of attestation with invalid signature for slot ${slotNumber.toBigInt()}`);
165
+ continue;
166
+ }
167
+
168
+ proposalAttestationMap.delete(sender.toString());
169
+ this.log.debug(`Deleted attestation for slot ${slotNumber} from ${sender}`);
153
170
  }
154
171
  }
155
172
  }
@@ -35,8 +35,11 @@ export const mockAttestation = (
35
35
  const header = makeL2BlockHeader(1, 2, slot);
36
36
  const payload = new ConsensusPayload(header.toCheckpointHeader(), archive, header.state);
37
37
 
38
- const hash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
39
- const signature = signer.sign(hash);
38
+ const attestationHash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
39
+ const attestationSignature = signer.sign(attestationHash);
40
40
 
41
- return new BlockAttestation(header.globalVariables.blockNumber, payload, signature);
41
+ const proposalHash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockProposal);
42
+ const proposerSignature = signer.sign(proposalHash);
43
+
44
+ return new BlockAttestation(header.globalVariables.blockNumber, payload, attestationSignature, proposerSignature);
42
45
  };
@@ -1,31 +1,64 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { NoCommitteeError } from '@aztec/ethereum';
3
+ import { type Logger, createLogger } from '@aztec/foundation/log';
3
4
  import { type BlockAttestation, type P2PValidator, PeerErrorSeverity } from '@aztec/stdlib/p2p';
4
5
 
5
6
  export class AttestationValidator implements P2PValidator<BlockAttestation> {
6
7
  private epochCache: EpochCacheInterface;
8
+ private logger: Logger;
7
9
 
8
10
  constructor(epochCache: EpochCacheInterface) {
9
11
  this.epochCache = epochCache;
12
+ this.logger = createLogger('p2p:attestation-validator');
10
13
  }
11
14
 
12
15
  async validate(message: BlockAttestation): Promise<PeerErrorSeverity | undefined> {
16
+ const slotNumberBigInt = message.payload.header.slotNumber.toBigInt();
17
+
13
18
  try {
14
- const { currentSlot, nextSlot } = await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
19
+ const { currentProposer, nextProposer, currentSlot, nextSlot } =
20
+ await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
15
21
 
16
- const slotNumberBigInt = message.payload.header.slotNumber.toBigInt();
17
22
  if (slotNumberBigInt !== currentSlot && slotNumberBigInt !== nextSlot) {
23
+ this.logger.warn(
24
+ `Attestation slot ${slotNumberBigInt} is not current (${currentSlot}) or next (${nextSlot}) slot`,
25
+ );
18
26
  return PeerErrorSeverity.HighToleranceError;
19
27
  }
20
28
 
29
+ // Verify the signature is valid
21
30
  const attester = message.getSender();
31
+ if (attester === undefined) {
32
+ this.logger.warn(`Invalid signature in attestation for slot ${slotNumberBigInt}`);
33
+ return PeerErrorSeverity.LowToleranceError;
34
+ }
35
+
36
+ // Verify the attester is in the committee for this slot
22
37
  if (!(await this.epochCache.isInCommittee(slotNumberBigInt, attester))) {
38
+ this.logger.warn(`Attester ${attester.toString()} is not in committee for slot ${slotNumberBigInt}`);
39
+ return PeerErrorSeverity.HighToleranceError;
40
+ }
41
+
42
+ // Verify the proposer signature matches the expected proposer for this slot
43
+ const proposer = message.getProposer();
44
+ const expectedProposer = slotNumberBigInt === currentSlot ? currentProposer : nextProposer;
45
+ if (!expectedProposer) {
46
+ this.logger.warn(`No proposer defined for slot ${slotNumberBigInt}`);
23
47
  return PeerErrorSeverity.HighToleranceError;
24
48
  }
49
+ if (!proposer.equals(expectedProposer)) {
50
+ this.logger.warn(
51
+ `Proposer signature mismatch in attestation. ` +
52
+ `Expected ${expectedProposer?.toString() ?? 'none'} but got ${proposer.toString()} for slot ${slotNumberBigInt}`,
53
+ );
54
+ return PeerErrorSeverity.HighToleranceError;
55
+ }
56
+
25
57
  return undefined;
26
58
  } catch (e) {
27
59
  // People shouldn't be sending us attestations if the committee doesn't exist
28
60
  if (e instanceof NoCommitteeError) {
61
+ this.logger.warn(`No committee exists for attestation for slot ${slotNumberBigInt}`);
29
62
  return PeerErrorSeverity.LowToleranceError;
30
63
  }
31
64
  throw e;
@@ -14,6 +14,13 @@ export class BlockProposalValidator implements P2PValidator<BlockProposal> {
14
14
 
15
15
  async validate(block: BlockProposal): Promise<PeerErrorSeverity | undefined> {
16
16
  try {
17
+ // Check signature validity first - invalid signatures are a high-severity issue
18
+ const proposer = block.getSender();
19
+ if (!proposer) {
20
+ this.logger.debug(`Penalizing peer for block proposal with invalid signature`);
21
+ return PeerErrorSeverity.MidToleranceError;
22
+ }
23
+
17
24
  const { currentProposer, nextProposer, currentSlot, nextSlot } =
18
25
  await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
19
26
 
@@ -25,12 +32,11 @@ export class BlockProposalValidator implements P2PValidator<BlockProposal> {
25
32
  }
26
33
 
27
34
  // Check that the block proposal is from the current or next proposer
28
- const proposer = block.getSender();
29
35
  if (slotNumberBigInt === currentSlot && currentProposer !== undefined && !proposer.equals(currentProposer)) {
30
36
  this.logger.debug(`Penalizing peer for invalid proposer for current slot ${slotNumberBigInt}`, {
31
37
  currentProposer,
32
38
  nextProposer,
33
- proposer,
39
+ proposer: proposer.toString(),
34
40
  });
35
41
  return PeerErrorSeverity.MidToleranceError;
36
42
  }
@@ -39,11 +45,20 @@ export class BlockProposalValidator implements P2PValidator<BlockProposal> {
39
45
  this.logger.debug(`Penalizing peer for invalid proposer for next slot ${slotNumberBigInt}`, {
40
46
  currentProposer,
41
47
  nextProposer,
42
- proposer,
48
+ proposer: proposer.toString(),
43
49
  });
44
50
  return PeerErrorSeverity.MidToleranceError;
45
51
  }
46
52
 
53
+ // Validate tx hashes for all txs embedded in the proposal
54
+ if (!(await Promise.all(block.txs?.map(tx => tx.validateTxHash()) ?? [])).every(v => v)) {
55
+ this.logger.warn(`Penalizing peer for invalid tx hashes in block proposal`, {
56
+ proposer,
57
+ slotNumber: slotNumberBigInt,
58
+ });
59
+ return PeerErrorSeverity.LowToleranceError;
60
+ }
61
+
47
62
  return undefined;
48
63
  } catch (e) {
49
64
  // People shouldn't be sending us block proposals if the committee doesn't exist
@@ -20,6 +20,7 @@ import { DoubleSpendTxValidator } from './double_spend_validator.js';
20
20
  import { GasTxValidator } from './gas_validator.js';
21
21
  import { MetadataTxValidator } from './metadata_validator.js';
22
22
  import { PhasesTxValidator } from './phases_validator.js';
23
+ import { TimestampTxValidator } from './timestamp_validator.js';
23
24
  import { TxPermittedValidator } from './tx_permitted_validator.js';
24
25
  import { TxProofValidator } from './tx_proof_validator.js';
25
26
 
@@ -59,13 +60,18 @@ export function createTxMessageValidators(
59
60
  validator: new MetadataTxValidator({
60
61
  l1ChainId: new Fr(l1ChainId),
61
62
  rollupVersion: new Fr(rollupVersion),
62
- timestamp,
63
- blockNumber,
64
63
  protocolContractsHash,
65
64
  vkTreeRoot: getVKTreeRoot(),
66
65
  }),
67
66
  severity: PeerErrorSeverity.HighToleranceError,
68
67
  },
68
+ timestampValidator: {
69
+ validator: new TimestampTxValidator<Tx>({
70
+ timestamp,
71
+ blockNumber,
72
+ }),
73
+ severity: PeerErrorSeverity.MidToleranceError,
74
+ },
69
75
  doubleSpendValidator: {
70
76
  validator: new DoubleSpendTxValidator({
71
77
  nullifiersExist: async (nullifiers: Buffer[]) => {
@@ -10,3 +10,4 @@ export * from './test_utils.js';
10
10
  export * from './allowed_public_setup.js';
11
11
  export * from './archive_cache.js';
12
12
  export * from './tx_permitted_validator.js';
13
+ export * from './timestamp_validator.js';
@@ -6,12 +6,9 @@ import {
6
6
  TX_ERROR_INCORRECT_PROTOCOL_CONTRACTS_HASH,
7
7
  TX_ERROR_INCORRECT_ROLLUP_VERSION,
8
8
  TX_ERROR_INCORRECT_VK_TREE_ROOT,
9
- TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP,
10
9
  type TxValidationResult,
11
10
  type TxValidator,
12
- getTxHash,
13
11
  } from '@aztec/stdlib/tx';
14
- import type { UInt64 } from '@aztec/stdlib/types';
15
12
 
16
13
  export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
17
14
  #log = createLogger('p2p:tx_validator:tx_metadata');
@@ -20,11 +17,6 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
20
17
  private values: {
21
18
  l1ChainId: Fr;
22
19
  rollupVersion: Fr;
23
- // Timestamp at which we will validate that the tx is not expired. This is typically the timestamp of the block
24
- // being built.
25
- timestamp: UInt64;
26
- // Block number in which the tx is considered to be included.
27
- blockNumber: number;
28
20
  vkTreeRoot: Fr;
29
21
  protocolContractsHash: Fr;
30
22
  },
@@ -38,9 +30,6 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
38
30
  if (!this.#hasCorrectRollupVersion(tx)) {
39
31
  errors.push(TX_ERROR_INCORRECT_ROLLUP_VERSION);
40
32
  }
41
- if (!this.#isValidForTimestamp(tx)) {
42
- errors.push(TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP);
43
- }
44
33
  if (!this.#hasCorrectVkTreeRoot(tx)) {
45
34
  errors.push(TX_ERROR_INCORRECT_VK_TREE_ROOT);
46
35
  }
@@ -83,29 +72,6 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
83
72
  }
84
73
  }
85
74
 
86
- #isValidForTimestamp(tx: T): boolean {
87
- const includeByTimestamp = tx.data.includeByTimestamp;
88
- // If building block 1, we skip the expiration check. For details on why see the `validate_include_by_timestamp`
89
- // function in `noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validation_requests.nr`.
90
- const buildingBlock1 = this.values.blockNumber === 1;
91
-
92
- if (!buildingBlock1 && includeByTimestamp < this.values.timestamp) {
93
- if (tx.data.constants.anchorBlockHeader.globalVariables.blockNumber === 0) {
94
- this.#log.warn(
95
- `A tx built against a genesis block failed to be included in block 1 which is the only block in which txs built against a genesis block are allowed to be included.`,
96
- );
97
- }
98
- this.#log.verbose(
99
- `Rejecting tx ${getTxHash(tx)} for low expiration timestamp. Tx expiration timestamp: ${includeByTimestamp}, timestamp: ${
100
- this.values.timestamp
101
- }.`,
102
- );
103
- return false;
104
- } else {
105
- return true;
106
- }
107
- }
108
-
109
75
  #hasCorrectRollupVersion(tx: T): boolean {
110
76
  if (!tx.data.constants.txContext.version.equals(this.values.rollupVersion)) {
111
77
  this.#log.verbose(
@@ -0,0 +1,46 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import {
3
+ type AnyTx,
4
+ TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP,
5
+ type TxValidationResult,
6
+ type TxValidator,
7
+ getTxHash,
8
+ } from '@aztec/stdlib/tx';
9
+ import type { UInt64 } from '@aztec/stdlib/types';
10
+
11
+ export class TimestampTxValidator<T extends AnyTx> implements TxValidator<T> {
12
+ #log = createLogger('p2p:tx_validator:timestamp');
13
+
14
+ constructor(
15
+ private values: {
16
+ // Timestamp at which we will validate that the tx is not expired. This is typically the timestamp of the block
17
+ // being built.
18
+ timestamp: UInt64;
19
+ // Block number in which the tx is considered to be included.
20
+ blockNumber: number;
21
+ },
22
+ ) {}
23
+
24
+ validateTx(tx: T): Promise<TxValidationResult> {
25
+ const includeByTimestamp = tx.data.includeByTimestamp;
26
+ // If building block 1, we skip the expiration check. For details on why see the `validate_include_by_timestamp`
27
+ // function in `noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validation_requests.nr`.
28
+ const buildingBlock1 = this.values.blockNumber === 1;
29
+
30
+ if (!buildingBlock1 && includeByTimestamp < this.values.timestamp) {
31
+ if (tx.data.constants.anchorBlockHeader.globalVariables.blockNumber === 0) {
32
+ this.#log.warn(
33
+ `A tx built against a genesis block failed to be included in block 1 which is the only block in which txs built against a genesis block are allowed to be included.`,
34
+ );
35
+ }
36
+ this.#log.verbose(
37
+ `Rejecting tx ${getTxHash(tx)} for low expiration timestamp. Tx expiration timestamp: ${includeByTimestamp}, timestamp: ${
38
+ this.values.timestamp
39
+ }.`,
40
+ );
41
+ return Promise.resolve({ result: 'invalid', reason: [TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP] });
42
+ } else {
43
+ return Promise.resolve({ result: 'valid' });
44
+ }
45
+ }
46
+ }
@@ -3,10 +3,10 @@ import { sleep } from '@aztec/foundation/sleep';
3
3
  import { type ComponentsVersions, checkCompressedComponentVersion } from '@aztec/stdlib/versioning';
4
4
  import { OtelMetricsAdapter, type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
5
5
 
6
- import { Discv5, type Discv5EventEmitter, type IDiscv5CreateOptions } from '@chainsafe/discv5';
7
- import { ENR, SignableENR } from '@chainsafe/enr';
8
6
  import type { PeerId } from '@libp2p/interface';
9
7
  import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';
8
+ import { Discv5, type Discv5EventEmitter, type IDiscv5CreateOptions } from '@nethermindeth/discv5';
9
+ import { ENR, SignableENR } from '@nethermindeth/enr';
10
10
  import EventEmitter from 'events';
11
11
 
12
12
  import type { P2PConfig } from '../../config.js';
@@ -3,8 +3,8 @@ import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
3
3
  import type { Gossipable, PeerErrorSeverity } from '@aztec/stdlib/p2p';
4
4
  import { Tx, TxHash } from '@aztec/stdlib/tx';
5
5
 
6
- import type { ENR } from '@chainsafe/enr';
7
6
  import type { PeerId } from '@libp2p/interface';
7
+ import type { ENR } from '@nethermindeth/enr';
8
8
  import EventEmitter from 'events';
9
9
 
10
10
  import type { PeerManagerInterface } from './peer-manager/interface.js';