@aztec/p2p 3.0.0-nightly.20251015 → 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.
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +2 -2
- package/dest/client/factory.d.ts +1 -0
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +4 -2
- package/dest/client/interface.d.ts +1 -1
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +2 -1
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +5 -0
- package/dest/enr/generate-enr.d.ts +1 -1
- package/dest/enr/generate-enr.d.ts.map +1 -1
- package/dest/enr/generate-enr.js +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +2 -2
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +18 -2
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +21 -7
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +5 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +1 -0
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +25 -2
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +16 -3
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +8 -2
- package/dest/msg_validators/tx_validator/index.d.ts +1 -0
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +1 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +0 -3
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +1 -19
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +12 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.js +32 -0
- package/dest/services/discv5/discV5_service.d.ts +2 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +2 -2
- package/dest/services/dummy_service.d.ts +1 -1
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.d.ts +11 -1
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +63 -21
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +9 -3
- package/dest/services/service.d.ts +1 -1
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +34 -4
- package/dest/test-helpers/make-enrs.js +1 -1
- package/dest/test-helpers/mock-tx-helpers.d.ts +12 -0
- package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -0
- package/dest/test-helpers/mock-tx-helpers.js +19 -0
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +1 -1
- package/dest/versioning.d.ts +1 -1
- package/dest/versioning.d.ts.map +1 -1
- package/package.json +17 -17
- package/src/bootstrap/bootstrap.ts +2 -2
- package/src/client/factory.ts +9 -2
- package/src/client/interface.ts +1 -1
- package/src/client/p2p_client.ts +5 -1
- package/src/enr/generate-enr.ts +1 -1
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +2 -2
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +22 -2
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +24 -7
- package/src/mem_pools/attestation_pool/mocks.ts +6 -3
- package/src/msg_validators/attestation_validator/attestation_validator.ts +35 -2
- package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +18 -3
- package/src/msg_validators/tx_validator/factory.ts +8 -2
- package/src/msg_validators/tx_validator/index.ts +1 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +0 -34
- package/src/msg_validators/tx_validator/timestamp_validator.ts +46 -0
- package/src/services/discv5/discV5_service.ts +2 -2
- package/src/services/dummy_service.ts +1 -1
- package/src/services/libp2p/libp2p_service.ts +85 -25
- package/src/services/peer-manager/peer_manager.ts +10 -3
- package/src/services/service.ts +1 -1
- package/src/services/tx_collection/tx_collection_sink.ts +34 -3
- package/src/test-helpers/make-enrs.ts +1 -1
- package/src/test-helpers/mock-tx-helpers.ts +24 -0
- package/src/test-helpers/reqresp-nodes.ts +1 -1
- 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.
|
|
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.
|
|
71
|
-
"@aztec/epoch-cache": "3.0.0-nightly.
|
|
72
|
-
"@aztec/ethereum": "3.0.0-nightly.
|
|
73
|
-
"@aztec/foundation": "3.0.0-nightly.
|
|
74
|
-
"@aztec/kv-store": "3.0.0-nightly.
|
|
75
|
-
"@aztec/noir-contracts.js": "3.0.0-nightly.
|
|
76
|
-
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.
|
|
77
|
-
"@aztec/protocol-contracts": "3.0.0-nightly.
|
|
78
|
-
"@aztec/simulator": "3.0.0-nightly.
|
|
79
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
80
|
-
"@aztec/telemetry-client": "3.0.0-nightly.
|
|
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.
|
|
108
|
-
"@aztec/world-state": "3.0.0-nightly.
|
|
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.
|
|
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';
|
package/src/client/factory.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
85
|
+
? ((deps.attestationPool ?? new KvAttestationPool(attestationStore, telemetry)) as T extends P2PClientType.Full
|
|
79
86
|
? AttestationPool
|
|
80
87
|
: undefined)
|
|
81
88
|
: undefined,
|
package/src/client/interface.ts
CHANGED
|
@@ -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';
|
package/src/client/p2p_client.ts
CHANGED
|
@@ -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 {
|
package/src/enr/generate-enr.ts
CHANGED
|
@@ -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()
|
|
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()
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
72
|
+
proposalAttestationMap.set(sender.toString(), attestation);
|
|
63
73
|
|
|
64
|
-
this.log.verbose(`Added attestation for slot ${slotNumber.toBigInt()} from ${
|
|
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
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
39
|
-
const
|
|
38
|
+
const attestationHash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockAttestation);
|
|
39
|
+
const attestationSignature = signer.sign(attestationHash);
|
|
40
40
|
|
|
41
|
-
|
|
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 } =
|
|
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[]) => {
|
|
@@ -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';
|