@aztec/p2p 3.0.0-devnet.2 → 3.0.0-devnet.2-patch.1
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 +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/client/factory.d.ts +1 -1
- package/dest/client/index.d.ts +1 -1
- package/dest/client/interface.d.ts +4 -2
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +8 -26
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +31 -24
- package/dest/config.d.ts +60 -54
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +12 -2
- package/dest/enr/generate-enr.d.ts +1 -1
- package/dest/enr/index.d.ts +1 -1
- package/dest/errors/attestation-pool.error.d.ts +7 -0
- package/dest/errors/attestation-pool.error.d.ts.map +1 -0
- package/dest/errors/attestation-pool.error.js +12 -0
- package/dest/errors/reqresp.error.d.ts +1 -1
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/index.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +43 -6
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +67 -34
- package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +15 -6
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +57 -18
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +13 -6
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +51 -7
- package/dest/mem_pools/attestation_pool/mocks.d.ts +226 -5
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +2 -2
- package/dest/mem_pools/index.d.ts +1 -1
- package/dest/mem_pools/instrumentation.d.ts +3 -1
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +11 -2
- package/dest/mem_pools/interface.d.ts +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +5 -38
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +9 -3
- package/dest/mem_pools/tx_pool/index.d.ts +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +5 -3
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +7 -0
- package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +10 -3
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +5 -4
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +12 -12
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +20 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +67 -0
- package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
- package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/index.js +1 -0
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +1 -1
- 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 +22 -10
- package/dest/msg_validators/block_proposal_validator/index.d.ts +1 -1
- package/dest/msg_validators/index.d.ts +1 -1
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +1 -1
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +1 -1
- package/dest/msg_validators/tx_validator/archive_cache.d.ts +2 -2
- package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.d.ts +4 -3
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.d.ts +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +3 -1
- package/dest/msg_validators/tx_validator/test_utils.d.ts +2 -2
- package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +3 -2
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.d.ts +1 -1
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/dummy_service.d.ts +1 -1
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/encoding.d.ts +25 -4
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +74 -6
- package/dest/services/gossipsub/scoring.d.ts +1 -1
- package/dest/services/index.d.ts +1 -1
- package/dest/services/libp2p/instrumentation.d.ts +3 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +9 -2
- package/dest/services/libp2p/libp2p_service.d.ts +25 -74
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +308 -84
- package/dest/services/peer-manager/interface.d.ts +1 -1
- package/dest/services/peer-manager/metrics.d.ts +3 -1
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +11 -0
- package/dest/services/peer-manager/peer_manager.d.ts +1 -32
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +4 -2
- package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +40 -2
- package/dest/services/reqresp/config.d.ts +1 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +1 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +1 -4
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/index.d.ts +1 -1
- package/dest/services/reqresp/interface.d.ts +2 -2
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +1 -1
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/auth.d.ts +2 -2
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/auth.js +2 -2
- package/dest/services/reqresp/protocols/block.d.ts +1 -1
- package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block.js +3 -2
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +4 -6
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +1 -1
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +1 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts +1 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.d.ts +1 -1
- package/dest/services/reqresp/protocols/ping.d.ts +1 -1
- package/dest/services/reqresp/protocols/status.d.ts +6 -5
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.js +4 -3
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +2 -2
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts +1 -41
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +2 -2
- package/dest/services/reqresp/status.d.ts +2 -2
- package/dest/services/reqresp/status.d.ts.map +1 -1
- package/dest/services/service.d.ts +1 -1
- package/dest/services/tx_collection/config.d.ts +1 -1
- package/dest/services/tx_collection/fast_tx_collection.d.ts +4 -9
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/index.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -7
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +2 -1
- package/dest/services/tx_collection/tx_collection.d.ts +11 -11
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.d.ts +3 -3
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.d.ts +1 -1
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_provider.d.ts +5 -4
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider_instrumentation.d.ts +1 -1
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
- package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
- package/dest/test-helpers/get-ports.d.ts +1 -1
- package/dest/test-helpers/get-ports.d.ts.map +1 -1
- package/dest/test-helpers/index.d.ts +1 -1
- package/dest/test-helpers/make-enrs.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts +2 -2
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.d.ts +4 -4
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-tx-helpers.d.ts +2 -2
- package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -1
- package/dest/test-helpers/mock-tx-helpers.js +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +14 -8
- package/dest/testbench/parse_log_file.d.ts +1 -1
- package/dest/testbench/testbench.d.ts +1 -1
- package/dest/testbench/testbench.js +2 -2
- package/dest/testbench/worker_client_manager.d.ts +1 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/types/index.d.ts +1 -1
- package/dest/util.d.ts +2 -1
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +11 -2
- package/dest/versioning.d.ts +1 -1
- package/package.json +19 -18
- package/src/client/interface.ts +4 -1
- package/src/client/p2p_client.ts +51 -43
- package/src/config.ts +19 -2
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +46 -5
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +84 -34
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +87 -24
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +77 -15
- package/src/mem_pools/attestation_pool/mocks.ts +3 -3
- package/src/mem_pools/instrumentation.ts +13 -0
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +15 -9
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +13 -6
- package/src/mem_pools/tx_pool/tx_pool.ts +10 -2
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +5 -4
- package/src/msg_validators/attestation_validator/attestation_validator.ts +14 -16
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +91 -0
- package/src/msg_validators/attestation_validator/index.ts +1 -0
- package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +26 -10
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/block_header_validator.ts +1 -1
- package/src/msg_validators/tx_validator/factory.ts +3 -2
- package/src/msg_validators/tx_validator/metadata_validator.ts +1 -1
- package/src/msg_validators/tx_validator/phases_validator.ts +3 -1
- package/src/msg_validators/tx_validator/test_utils.ts +1 -1
- package/src/msg_validators/tx_validator/timestamp_validator.ts +2 -1
- package/src/services/encoding.ts +81 -6
- package/src/services/libp2p/instrumentation.ts +10 -1
- package/src/services/libp2p/libp2p_service.ts +334 -91
- package/src/services/peer-manager/metrics.ts +10 -0
- package/src/services/peer-manager/peer_manager.ts +4 -2
- package/src/services/peer-manager/peer_scoring.ts +46 -3
- package/src/services/reqresp/interface.ts +1 -1
- package/src/services/reqresp/protocols/auth.ts +2 -2
- package/src/services/reqresp/protocols/block.ts +3 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +1 -1
- package/src/services/reqresp/protocols/status.ts +9 -8
- package/src/services/reqresp/reqresp.ts +2 -2
- package/src/services/tx_collection/fast_tx_collection.ts +3 -2
- package/src/services/tx_collection/slow_tx_collection.ts +7 -6
- package/src/services/tx_collection/tx_collection.ts +10 -9
- package/src/services/tx_provider.ts +4 -3
- package/src/test-helpers/mock-tx-helpers.ts +1 -1
- package/src/testbench/p2p_client_testbench_worker.ts +11 -5
- package/src/testbench/testbench.ts +2 -2
- package/src/util.ts +12 -2
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
5
|
import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
5
|
-
import { SerialQueue } from '@aztec/foundation/queue';
|
|
6
6
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
7
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, 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';
|
|
@@ -50,8 +50,13 @@ import { ENR } from '@nethermindeth/enr';
|
|
|
50
50
|
import { createLibp2p } from 'libp2p';
|
|
51
51
|
|
|
52
52
|
import type { P2PConfig } from '../../config.js';
|
|
53
|
+
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
53
54
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
54
|
-
import {
|
|
55
|
+
import {
|
|
56
|
+
AttestationValidator,
|
|
57
|
+
BlockProposalValidator,
|
|
58
|
+
FishermanAttestationValidator,
|
|
59
|
+
} from '../../msg_validators/index.js';
|
|
55
60
|
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
56
61
|
import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
|
|
57
62
|
import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
|
|
@@ -107,11 +112,15 @@ interface ValidationResult {
|
|
|
107
112
|
|
|
108
113
|
type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
|
|
109
114
|
|
|
115
|
+
// REFACTOR: Unify with the type above
|
|
116
|
+
type ReceivedMessageValidationResult<T> =
|
|
117
|
+
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject> }
|
|
118
|
+
| { obj?: undefined; result: TopicValidatorResult.Reject };
|
|
119
|
+
|
|
110
120
|
/**
|
|
111
121
|
* Lib P2P implementation of the P2PService interface.
|
|
112
122
|
*/
|
|
113
123
|
export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
|
|
114
|
-
private jobQueue: SerialQueue = new SerialQueue();
|
|
115
124
|
private discoveryRunningPromise?: RunningPromise;
|
|
116
125
|
private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
|
|
117
126
|
|
|
@@ -122,7 +131,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
122
131
|
private protocolVersion = '';
|
|
123
132
|
private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
|
|
124
133
|
|
|
125
|
-
private feesCache: { blockNumber:
|
|
134
|
+
private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
|
|
126
135
|
|
|
127
136
|
/**
|
|
128
137
|
* Callback for when a block is received from a peer.
|
|
@@ -135,6 +144,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
135
144
|
|
|
136
145
|
private instrumentation: P2PInstrumentation;
|
|
137
146
|
|
|
147
|
+
protected logger: Logger;
|
|
148
|
+
|
|
138
149
|
constructor(
|
|
139
150
|
private clientType: T,
|
|
140
151
|
private config: P2PConfig,
|
|
@@ -148,10 +159,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
148
159
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
149
160
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
150
161
|
telemetry: TelemetryClient,
|
|
151
|
-
|
|
162
|
+
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
152
163
|
) {
|
|
153
164
|
super(telemetry, 'LibP2PService');
|
|
154
165
|
|
|
166
|
+
// Create child logger with fisherman prefix if in fisherman mode
|
|
167
|
+
this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
|
|
168
|
+
|
|
155
169
|
this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
|
|
156
170
|
|
|
157
171
|
this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
@@ -169,15 +183,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
169
183
|
this.protocolVersion,
|
|
170
184
|
);
|
|
171
185
|
|
|
172
|
-
|
|
186
|
+
// Use FishermanAttestationValidator in fisherman mode to validate attestation payloads against proposals
|
|
187
|
+
this.attestationValidator = config.fishermanMode
|
|
188
|
+
? new FishermanAttestationValidator(epochCache, mempools.attestationPool!, telemetry)
|
|
189
|
+
: new AttestationValidator(epochCache);
|
|
173
190
|
this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
|
|
174
191
|
|
|
175
192
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
176
193
|
|
|
177
194
|
this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
|
|
178
195
|
this.logger.debug(
|
|
179
|
-
`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber
|
|
180
|
-
{ p2pMessageIdentifier: await block.
|
|
196
|
+
`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber} from peer.`,
|
|
197
|
+
{ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
|
|
181
198
|
);
|
|
182
199
|
return undefined;
|
|
183
200
|
};
|
|
@@ -390,7 +407,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
390
407
|
logger: createLibp2pComponentLogger(logger.module),
|
|
391
408
|
});
|
|
392
409
|
|
|
393
|
-
const peerScoring = new PeerScoring(config);
|
|
410
|
+
const peerScoring = new PeerScoring(config, telemetry);
|
|
394
411
|
const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
|
|
395
412
|
|
|
396
413
|
const peerManager = new PeerManager(
|
|
@@ -445,9 +462,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
445
462
|
}
|
|
446
463
|
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
447
464
|
|
|
448
|
-
// Start job queue, peer discovery service and libp2p node
|
|
449
|
-
this.jobQueue.start();
|
|
450
|
-
|
|
451
465
|
await this.peerManager.initializePeers();
|
|
452
466
|
if (!this.config.p2pDiscoveryDisabled) {
|
|
453
467
|
await this.peerDiscoveryService.start();
|
|
@@ -473,7 +487,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
473
487
|
};
|
|
474
488
|
|
|
475
489
|
// Only handle block transactions request if attestation pool is available to the client
|
|
476
|
-
if (this.mempools.attestationPool) {
|
|
490
|
+
if (this.mempools.attestationPool && !this.config.disableTransactions) {
|
|
477
491
|
const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.mempools.txPool);
|
|
478
492
|
requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
|
|
479
493
|
}
|
|
@@ -485,9 +499,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
485
499
|
// add GossipSub listener
|
|
486
500
|
this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
487
501
|
|
|
488
|
-
// Start running promise for peer discovery
|
|
502
|
+
// Start running promise for peer discovery and metrics collection
|
|
489
503
|
this.discoveryRunningPromise = new RunningPromise(
|
|
490
|
-
() =>
|
|
504
|
+
async () => {
|
|
505
|
+
await this.peerManager.heartbeat();
|
|
506
|
+
},
|
|
491
507
|
this.logger,
|
|
492
508
|
this.config.peerCheckIntervalMS,
|
|
493
509
|
);
|
|
@@ -496,9 +512,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
496
512
|
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
497
513
|
const reqrespSubProtocolValidators = {
|
|
498
514
|
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
499
|
-
// TODO(#11336): A request validator for blocks
|
|
500
515
|
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
501
516
|
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
517
|
+
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
|
|
502
518
|
};
|
|
503
519
|
await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
|
|
504
520
|
this.logger.info(`Started P2P service`, {
|
|
@@ -520,9 +536,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
520
536
|
// Stop peer manager
|
|
521
537
|
this.logger.debug('Stopping peer manager...');
|
|
522
538
|
await this.peerManager.stop();
|
|
523
|
-
|
|
524
|
-
this.logger.debug('Stopping job queue...');
|
|
525
|
-
await this.jobQueue.end();
|
|
526
539
|
this.logger.debug('Stopping running promise...');
|
|
527
540
|
await this.discoveryRunningPromise?.stop();
|
|
528
541
|
this.logger.debug('Stopping peer discovery service...');
|
|
@@ -610,11 +623,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
610
623
|
if (!this.node.services.pubsub) {
|
|
611
624
|
throw new Error('Pubsub service not available.');
|
|
612
625
|
}
|
|
613
|
-
const p2pMessage = P2PMessage.fromGossipable(message);
|
|
626
|
+
const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
|
|
614
627
|
const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
|
|
615
628
|
return result.recipients.length;
|
|
616
629
|
}
|
|
617
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Checks if this message has already been seen, based on its msgId computed from hashing the message data.
|
|
633
|
+
* Note that we do not rely on the seenCache from gossipsub since we want to keep a longer history of seen
|
|
634
|
+
* messages to avoid tx echoes across the network.
|
|
635
|
+
*/
|
|
618
636
|
protected preValidateReceivedMessage(
|
|
619
637
|
msg: Message,
|
|
620
638
|
msgId: string,
|
|
@@ -650,13 +668,39 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
650
668
|
return { result: true, topicType };
|
|
651
669
|
}
|
|
652
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Safely deserializes a P2PMessage from raw message data.
|
|
673
|
+
* @param msgId - The message ID.
|
|
674
|
+
* @param source - The peer ID of the message source.
|
|
675
|
+
* @param data - The raw message data.
|
|
676
|
+
* @returns The deserialized P2PMessage or undefined if deserialization fails.
|
|
677
|
+
*/
|
|
678
|
+
private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
|
|
679
|
+
try {
|
|
680
|
+
return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
|
|
681
|
+
} catch (err) {
|
|
682
|
+
this.logger.error(`Error deserializing P2PMessage`, err, {
|
|
683
|
+
msgId,
|
|
684
|
+
source: source.toString(),
|
|
685
|
+
});
|
|
686
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
|
|
687
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
|
|
688
|
+
return undefined;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
653
692
|
/**
|
|
654
693
|
* Handles a new gossip message that was received by the client.
|
|
655
694
|
* @param topic - The message's topic.
|
|
656
695
|
* @param data - The message data
|
|
657
696
|
*/
|
|
658
697
|
protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
|
|
659
|
-
const
|
|
698
|
+
const msgReceivedTime = Date.now();
|
|
699
|
+
let topicType: TopicType | undefined;
|
|
700
|
+
const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
|
|
701
|
+
if (!p2pMessage) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
660
704
|
|
|
661
705
|
const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
|
|
662
706
|
|
|
@@ -665,55 +709,81 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
665
709
|
}
|
|
666
710
|
|
|
667
711
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
712
|
+
topicType = TopicType.tx;
|
|
668
713
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
669
714
|
}
|
|
670
|
-
if (msg.topic === this.topicStrings[TopicType.block_attestation]
|
|
671
|
-
|
|
715
|
+
if (msg.topic === this.topicStrings[TopicType.block_attestation]) {
|
|
716
|
+
topicType = TopicType.block_attestation;
|
|
717
|
+
if (this.clientType === P2PClientType.Full) {
|
|
718
|
+
await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
719
|
+
}
|
|
672
720
|
}
|
|
673
721
|
if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
722
|
+
topicType = TopicType.block_proposal;
|
|
674
723
|
await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
|
|
675
724
|
}
|
|
676
725
|
|
|
726
|
+
if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
|
|
727
|
+
const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
|
|
728
|
+
this.instrumentation.recordMessageLatency(topicType, latency);
|
|
729
|
+
}
|
|
730
|
+
|
|
677
731
|
return;
|
|
678
732
|
}
|
|
679
733
|
|
|
680
734
|
protected async validateReceivedMessage<T>(
|
|
681
|
-
validationFunc: () => Promise<
|
|
735
|
+
validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
|
|
682
736
|
msgId: string,
|
|
683
737
|
source: PeerId,
|
|
684
738
|
topicType: TopicType,
|
|
685
|
-
): Promise<
|
|
686
|
-
let resultAndObj:
|
|
739
|
+
): Promise<ReceivedMessageValidationResult<T>> {
|
|
740
|
+
let resultAndObj: ReceivedMessageValidationResult<T> = { result: TopicValidatorResult.Reject };
|
|
687
741
|
const timer = new Timer();
|
|
688
742
|
try {
|
|
689
743
|
resultAndObj = await validationFunc();
|
|
690
744
|
} catch (err) {
|
|
691
|
-
this.
|
|
745
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
|
|
746
|
+
this.logger.error(`Error deserializing and validating gossipsub message`, err, {
|
|
747
|
+
msgId,
|
|
748
|
+
source: source.toString(),
|
|
749
|
+
topicType,
|
|
750
|
+
});
|
|
692
751
|
}
|
|
693
752
|
|
|
694
|
-
if (resultAndObj.result) {
|
|
753
|
+
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
695
754
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
696
755
|
}
|
|
697
756
|
|
|
698
|
-
this.node.services.pubsub.reportMessageValidationResult(
|
|
699
|
-
msgId,
|
|
700
|
-
source.toString(),
|
|
701
|
-
resultAndObj.result && resultAndObj.obj ? TopicValidatorResult.Accept : TopicValidatorResult.Reject,
|
|
702
|
-
);
|
|
757
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
703
758
|
return resultAndObj;
|
|
704
759
|
}
|
|
705
760
|
|
|
706
761
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
707
|
-
const validationFunc = async () => {
|
|
762
|
+
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
708
763
|
const tx = Tx.fromBuffer(payloadData);
|
|
709
|
-
const
|
|
710
|
-
|
|
764
|
+
const isValid = await this.validatePropagatedTx(tx, source);
|
|
765
|
+
const exists = isValid && (await this.mempools.txPool.hasTx(tx.getTxHash()));
|
|
766
|
+
|
|
767
|
+
this.logger.trace(`Validate propagated tx`, {
|
|
768
|
+
isValid,
|
|
769
|
+
exists,
|
|
770
|
+
[Attributes.P2P_ID]: source.toString(),
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
if (!isValid) {
|
|
774
|
+
return { result: TopicValidatorResult.Reject };
|
|
775
|
+
} else if (exists) {
|
|
776
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
777
|
+
} else {
|
|
778
|
+
return { result: TopicValidatorResult.Accept, obj: tx };
|
|
779
|
+
}
|
|
711
780
|
};
|
|
712
781
|
|
|
713
782
|
const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source, TopicType.tx);
|
|
714
|
-
if (
|
|
783
|
+
if (result !== TopicValidatorResult.Accept || !tx) {
|
|
715
784
|
return;
|
|
716
785
|
}
|
|
786
|
+
|
|
717
787
|
const txHash = tx.getTxHash();
|
|
718
788
|
const txHashString = txHash.toString();
|
|
719
789
|
this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
|
|
@@ -722,10 +792,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
722
792
|
});
|
|
723
793
|
|
|
724
794
|
if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
|
|
725
|
-
this.logger.
|
|
795
|
+
this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
|
|
726
796
|
return;
|
|
727
797
|
}
|
|
728
798
|
|
|
799
|
+
this.instrumentation.incrementTxReceived(1);
|
|
729
800
|
await this.mempools.txPool.addTxs([tx]);
|
|
730
801
|
}
|
|
731
802
|
|
|
@@ -736,14 +807,42 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
736
807
|
* @param attestation - The attestation to process.
|
|
737
808
|
*/
|
|
738
809
|
private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
739
|
-
const validationFunc = async () => {
|
|
810
|
+
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
|
|
740
811
|
const attestation = BlockAttestation.fromBuffer(payloadData);
|
|
741
|
-
const
|
|
742
|
-
this.
|
|
812
|
+
const pool = this.mempools.attestationPool!;
|
|
813
|
+
const isValid = await this.validateAttestation(source, attestation);
|
|
814
|
+
const exists = isValid && (await pool.hasAttestation(attestation));
|
|
815
|
+
|
|
816
|
+
let canAdd = true;
|
|
817
|
+
if (isValid && !exists) {
|
|
818
|
+
const slot = attestation.payload.header.slotNumber;
|
|
819
|
+
const { committee } = await this.epochCache.getCommittee(slot);
|
|
820
|
+
const committeeSize = committee?.length ?? 0;
|
|
821
|
+
canAdd = await pool.canAddAttestation(attestation, committeeSize);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
this.logger.trace(`Validate propagated block attestation`, {
|
|
825
|
+
isValid,
|
|
826
|
+
exists,
|
|
827
|
+
canAdd,
|
|
743
828
|
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
744
829
|
[Attributes.P2P_ID]: source.toString(),
|
|
745
830
|
});
|
|
746
|
-
|
|
831
|
+
|
|
832
|
+
if (!isValid) {
|
|
833
|
+
return { result: TopicValidatorResult.Reject };
|
|
834
|
+
} else if (exists) {
|
|
835
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
836
|
+
} else if (!canAdd) {
|
|
837
|
+
this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
|
|
838
|
+
slot: attestation.payload.header.slotNumber.toString(),
|
|
839
|
+
archive: attestation.archive.toString(),
|
|
840
|
+
source: source.toString(),
|
|
841
|
+
});
|
|
842
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
843
|
+
} else {
|
|
844
|
+
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
845
|
+
}
|
|
747
846
|
};
|
|
748
847
|
|
|
749
848
|
const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
|
|
@@ -752,30 +851,58 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
752
851
|
source,
|
|
753
852
|
TopicType.block_attestation,
|
|
754
853
|
);
|
|
755
|
-
|
|
854
|
+
|
|
855
|
+
if (result !== TopicValidatorResult.Accept || !attestation) {
|
|
756
856
|
return;
|
|
757
857
|
}
|
|
858
|
+
|
|
758
859
|
this.logger.debug(
|
|
759
|
-
`Received attestation for slot ${attestation.slotNumber
|
|
860
|
+
`Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
|
|
760
861
|
{
|
|
761
|
-
p2pMessageIdentifier: await attestation.
|
|
762
|
-
slot: attestation.slotNumber
|
|
862
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
863
|
+
slot: attestation.slotNumber,
|
|
763
864
|
archive: attestation.archive.toString(),
|
|
764
865
|
source: source.toString(),
|
|
765
866
|
},
|
|
766
867
|
);
|
|
868
|
+
|
|
767
869
|
await this.mempools.attestationPool!.addAttestations([attestation]);
|
|
768
870
|
}
|
|
769
871
|
|
|
770
872
|
private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
771
|
-
const validationFunc = async () => {
|
|
873
|
+
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
|
|
772
874
|
const block = BlockProposal.fromBuffer(payloadData);
|
|
773
|
-
const
|
|
774
|
-
this.
|
|
875
|
+
const isValid = await this.validateBlockProposal(source, block);
|
|
876
|
+
const pool = this.mempools.attestationPool;
|
|
877
|
+
|
|
878
|
+
// Note that we dont have an attestation pool if we're a prover node, but we still
|
|
879
|
+
// subscribe to block proposal topics in order to prevent their txs from being cleared.
|
|
880
|
+
const exists = isValid && (await pool?.hasBlockProposal(block));
|
|
881
|
+
const canAdd = isValid && (await pool?.canAddProposal(block));
|
|
882
|
+
|
|
883
|
+
this.logger.trace(`Validate propagated block proposal`, {
|
|
884
|
+
isValid,
|
|
885
|
+
exists,
|
|
886
|
+
canAdd,
|
|
775
887
|
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
776
888
|
[Attributes.P2P_ID]: source.toString(),
|
|
777
889
|
});
|
|
778
|
-
|
|
890
|
+
|
|
891
|
+
if (!isValid) {
|
|
892
|
+
return { result: TopicValidatorResult.Reject };
|
|
893
|
+
} else if (exists) {
|
|
894
|
+
return { result: TopicValidatorResult.Ignore, obj: block };
|
|
895
|
+
} else if (!canAdd) {
|
|
896
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
|
|
897
|
+
this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
|
|
898
|
+
slot: block.slotNumber.toString(),
|
|
899
|
+
archive: block.archive.toString(),
|
|
900
|
+
source: source.toString(),
|
|
901
|
+
});
|
|
902
|
+
return { result: TopicValidatorResult.Reject };
|
|
903
|
+
} else {
|
|
904
|
+
return { result: TopicValidatorResult.Accept, obj: block };
|
|
905
|
+
}
|
|
779
906
|
};
|
|
780
907
|
|
|
781
908
|
const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
|
|
@@ -784,6 +911,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
784
911
|
source,
|
|
785
912
|
TopicType.block_proposal,
|
|
786
913
|
);
|
|
914
|
+
|
|
787
915
|
if (!result || !block) {
|
|
788
916
|
return;
|
|
789
917
|
}
|
|
@@ -793,16 +921,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
793
921
|
|
|
794
922
|
// REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
|
|
795
923
|
@trackSpan('Libp2pService.processValidBlockProposal', async block => ({
|
|
796
|
-
[Attributes.SLOT_NUMBER]: block.slotNumber
|
|
924
|
+
[Attributes.SLOT_NUMBER]: block.slotNumber,
|
|
797
925
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
798
|
-
[Attributes.P2P_ID]: await block.
|
|
926
|
+
[Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
799
927
|
}))
|
|
800
928
|
private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
|
|
801
|
-
const slot = block.slotNumber
|
|
802
|
-
const previousSlot = slot -
|
|
929
|
+
const slot = block.slotNumber;
|
|
930
|
+
const previousSlot = SlotNumber(slot - 1);
|
|
803
931
|
this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
804
|
-
p2pMessageIdentifier: await block.
|
|
805
|
-
slot: block.slotNumber
|
|
932
|
+
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
|
|
933
|
+
slot: block.slotNumber,
|
|
806
934
|
archive: block.archive.toString(),
|
|
807
935
|
source: sender.toString(),
|
|
808
936
|
});
|
|
@@ -811,18 +939,31 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
811
939
|
this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
|
|
812
940
|
}
|
|
813
941
|
|
|
814
|
-
//
|
|
942
|
+
// Attempt to add proposal, then mark the txs in this proposal as non-evictable
|
|
943
|
+
try {
|
|
944
|
+
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
945
|
+
} catch (err: unknown) {
|
|
946
|
+
// Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
|
|
947
|
+
if (err instanceof ProposalSlotCapExceededError) {
|
|
948
|
+
this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
|
|
949
|
+
slot: String(slot),
|
|
950
|
+
archive: block.archive.toString(),
|
|
951
|
+
error: (err as Error).message,
|
|
952
|
+
});
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
throw err;
|
|
956
|
+
}
|
|
815
957
|
await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
|
|
816
|
-
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
817
958
|
const attestations = await this.blockReceivedCallback(block, sender);
|
|
818
959
|
|
|
819
960
|
// TODO: fix up this pattern - the abstraction is not nice
|
|
820
|
-
// The attestation can be undefined if no handler is registered / the validator deems the block invalid
|
|
961
|
+
// The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
|
|
821
962
|
if (attestations?.length) {
|
|
822
963
|
for (const attestation of attestations) {
|
|
823
|
-
this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber
|
|
824
|
-
p2pMessageIdentifier: await attestation.
|
|
825
|
-
slot: attestation.slotNumber
|
|
964
|
+
this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
|
|
965
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
966
|
+
slot: attestation.slotNumber,
|
|
826
967
|
archive: attestation.archive.toString(),
|
|
827
968
|
});
|
|
828
969
|
await this.broadcastAttestation(attestation);
|
|
@@ -835,9 +976,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
835
976
|
* @param attestation - The attestation to broadcast.
|
|
836
977
|
*/
|
|
837
978
|
@trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
|
|
838
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber
|
|
979
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
839
980
|
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
840
|
-
[Attributes.P2P_ID]: await attestation.
|
|
981
|
+
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
841
982
|
}))
|
|
842
983
|
private async broadcastAttestation(attestation: BlockAttestation) {
|
|
843
984
|
await this.propagate(attestation);
|
|
@@ -848,19 +989,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
848
989
|
* @param message - The message to propagate.
|
|
849
990
|
*/
|
|
850
991
|
public async propagate<T extends Gossipable>(message: T) {
|
|
851
|
-
const p2pMessageIdentifier = await message.
|
|
992
|
+
const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
|
|
852
993
|
this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
|
|
853
|
-
void this.
|
|
854
|
-
.
|
|
855
|
-
|
|
856
|
-
})
|
|
857
|
-
.catch(error => {
|
|
858
|
-
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
859
|
-
});
|
|
994
|
+
void this.sendToPeers(message).catch(error => {
|
|
995
|
+
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
996
|
+
});
|
|
860
997
|
}
|
|
861
998
|
|
|
862
999
|
/**
|
|
863
|
-
* Validate the requested block transactions.
|
|
1000
|
+
* Validate the requested block transactions. Allow partial returns.
|
|
864
1001
|
* @param request - The block transactions request.
|
|
865
1002
|
* @param response - The block transactions response.
|
|
866
1003
|
* @param peerId - The ID of the peer that made the request.
|
|
@@ -870,14 +1007,71 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
870
1007
|
[Attributes.BLOCK_HASH]: request.blockHash.toString(),
|
|
871
1008
|
}))
|
|
872
1009
|
private async validateRequestedBlockTxs(
|
|
873
|
-
|
|
1010
|
+
request: BlockTxsRequest,
|
|
874
1011
|
response: BlockTxsResponse,
|
|
875
1012
|
peerId: PeerId,
|
|
876
1013
|
): Promise<boolean> {
|
|
877
1014
|
const requestedTxValidator = this.createRequestedTxValidator();
|
|
878
1015
|
|
|
879
1016
|
try {
|
|
880
|
-
|
|
1017
|
+
if (!response.blockHash.equals(request.blockHash)) {
|
|
1018
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1019
|
+
throw new ValidationError(
|
|
1020
|
+
`Received block txs for unexpected block: expected ${request.blockHash.toString()}, got ${response.blockHash.toString()}`,
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
if (response.txIndices.getLength() !== request.txIndices.getLength()) {
|
|
1025
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1026
|
+
throw new ValidationError(
|
|
1027
|
+
`Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Check no duplicates and not exceeding returnable count
|
|
1032
|
+
const requestedIndices = new Set(request.txIndices.getTrueIndices());
|
|
1033
|
+
const availableIndices = new Set(response.txIndices.getTrueIndices());
|
|
1034
|
+
const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
|
|
1035
|
+
|
|
1036
|
+
const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
|
|
1037
|
+
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1038
|
+
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1039
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1040
|
+
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1041
|
+
}
|
|
1042
|
+
if (response.txs.length > maxReturnable) {
|
|
1043
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1044
|
+
throw new ValidationError(
|
|
1045
|
+
`Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// Given proposal (should have locally), ensure returned txs are valid subset and match request indices
|
|
1050
|
+
const proposal = await this.mempools.attestationPool?.getBlockProposal(request.blockHash.toString());
|
|
1051
|
+
if (proposal) {
|
|
1052
|
+
// Build intersected indices
|
|
1053
|
+
const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
|
|
1054
|
+
|
|
1055
|
+
// Enforce subset membership and preserve increasing order by index.
|
|
1056
|
+
const hashToIndexInProposal = new Map<string, number>(
|
|
1057
|
+
proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
|
|
1058
|
+
);
|
|
1059
|
+
const allowedIndexSet = new Set(intersectIdx);
|
|
1060
|
+
const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
|
|
1061
|
+
const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
|
|
1062
|
+
const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
|
|
1063
|
+
if (!allAllowed || !strictlyIncreasing) {
|
|
1064
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1065
|
+
throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
// No local proposal, cannot check the membership/order of the returned txs
|
|
1069
|
+
this.logger.warn(
|
|
1070
|
+
`Block proposal not found for block hash ${request.blockHash.toString()}; cannot validate membership/order of returned txs`,
|
|
1071
|
+
);
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
881
1075
|
await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
|
|
882
1076
|
return true;
|
|
883
1077
|
} catch (e: any) {
|
|
@@ -901,7 +1095,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
901
1095
|
* ReqRespSubProtocol.TX subprotocol validation.
|
|
902
1096
|
*
|
|
903
1097
|
* @param requestedTxHash - The collection of the txs that was requested.
|
|
904
|
-
* @param responseTx - The
|
|
1098
|
+
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
905
1099
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
906
1100
|
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
907
1101
|
*/
|
|
@@ -928,6 +1122,53 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
928
1122
|
}
|
|
929
1123
|
}
|
|
930
1124
|
|
|
1125
|
+
/**
|
|
1126
|
+
* Validates a BLOCK response.
|
|
1127
|
+
*
|
|
1128
|
+
* If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
|
|
1129
|
+
* Penalizes on block number mismatch or hash mismatch.
|
|
1130
|
+
*
|
|
1131
|
+
* @param requestedBlockNumber - The requested block number.
|
|
1132
|
+
* @param responseBlock - The block returned by the peer.
|
|
1133
|
+
* @param peerId - The peer that returned the block.
|
|
1134
|
+
* @returns True if the response is valid, false otherwise.
|
|
1135
|
+
*/
|
|
1136
|
+
@trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
|
|
1137
|
+
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
|
|
1138
|
+
}))
|
|
1139
|
+
private async validateRequestedBlock(
|
|
1140
|
+
requestedBlockNumber: Fr,
|
|
1141
|
+
responseBlock: L2Block,
|
|
1142
|
+
peerId: PeerId,
|
|
1143
|
+
): Promise<boolean> {
|
|
1144
|
+
try {
|
|
1145
|
+
const reqNum = Number(requestedBlockNumber.toString());
|
|
1146
|
+
if (responseBlock.number !== reqNum) {
|
|
1147
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
const local = await this.archiver.getBlock(BlockNumber(reqNum));
|
|
1152
|
+
if (!local) {
|
|
1153
|
+
// We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
|
|
1154
|
+
// TODO: Consider extending this validator to accept an expected hash or
|
|
1155
|
+
// performing quorum-based checks when using P2P syncing prior to L1 sync.
|
|
1156
|
+
this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
|
|
1160
|
+
if (!localHash.equals(respHash)) {
|
|
1161
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return true;
|
|
1166
|
+
} catch (e) {
|
|
1167
|
+
this.logger.warn(`Error validating requested block`, e);
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
931
1172
|
private createRequestedTxValidator(): TxValidator {
|
|
932
1173
|
return new AggregateTxValidator(
|
|
933
1174
|
new DataTxValidator(),
|
|
@@ -942,19 +1183,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
942
1183
|
}
|
|
943
1184
|
|
|
944
1185
|
private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
|
|
1186
|
+
const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
|
|
1187
|
+
|
|
945
1188
|
if (!(await tx.validateTxHash())) {
|
|
946
|
-
|
|
1189
|
+
penalize(PeerErrorSeverity.MidToleranceError);
|
|
947
1190
|
throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
|
|
948
1191
|
}
|
|
949
1192
|
|
|
950
1193
|
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
951
|
-
|
|
1194
|
+
penalize(PeerErrorSeverity.MidToleranceError);
|
|
952
1195
|
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
953
1196
|
}
|
|
954
1197
|
|
|
955
1198
|
const { result } = await txValidator.validateTx(tx);
|
|
956
1199
|
if (result === 'invalid') {
|
|
957
|
-
|
|
1200
|
+
penalize(PeerErrorSeverity.LowToleranceError);
|
|
958
1201
|
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
959
1202
|
}
|
|
960
1203
|
}
|
|
@@ -980,7 +1223,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
980
1223
|
|
|
981
1224
|
// Double spend validator has a special case handler
|
|
982
1225
|
if (name === 'doubleSpendValidator') {
|
|
983
|
-
const txBlockNumber = currentBlockNumber + 1; // tx is expected to be in the next block
|
|
1226
|
+
const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
|
|
984
1227
|
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
985
1228
|
}
|
|
986
1229
|
|
|
@@ -990,7 +1233,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
990
1233
|
return true;
|
|
991
1234
|
}
|
|
992
1235
|
|
|
993
|
-
private async getGasFees(blockNumber:
|
|
1236
|
+
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
994
1237
|
if (blockNumber === this.feesCache?.blockNumber) {
|
|
995
1238
|
return this.feesCache.gasFees;
|
|
996
1239
|
}
|
|
@@ -1031,13 +1274,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1031
1274
|
* @returns The message validators.
|
|
1032
1275
|
*/
|
|
1033
1276
|
private async createMessageValidators(
|
|
1034
|
-
currentBlockNumber:
|
|
1277
|
+
currentBlockNumber: BlockNumber,
|
|
1035
1278
|
nextSlotTimestamp: UInt64,
|
|
1036
1279
|
): Promise<Record<string, MessageValidator>[]> {
|
|
1037
1280
|
const gasFees = await this.getGasFees(currentBlockNumber);
|
|
1038
1281
|
const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
1039
1282
|
|
|
1040
|
-
const blockNumberInWhichTheTxIsConsideredToBeIncluded = currentBlockNumber + 1;
|
|
1283
|
+
const blockNumberInWhichTheTxIsConsideredToBeIncluded = BlockNumber(currentBlockNumber + 1);
|
|
1041
1284
|
|
|
1042
1285
|
return createTxMessageValidators(
|
|
1043
1286
|
nextSlotTimestamp,
|
|
@@ -1099,7 +1342,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1099
1342
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
1100
1343
|
* @returns Severity
|
|
1101
1344
|
*/
|
|
1102
|
-
private async handleDoubleSpendFailure(tx: Tx, blockNumber:
|
|
1345
|
+
private async handleDoubleSpendFailure(tx: Tx, blockNumber: BlockNumber): Promise<PeerErrorSeverity> {
|
|
1103
1346
|
if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
|
|
1104
1347
|
return PeerErrorSeverity.HighToleranceError;
|
|
1105
1348
|
}
|
|
@@ -1107,7 +1350,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1107
1350
|
const snapshotValidator = new DoubleSpendTxValidator({
|
|
1108
1351
|
nullifiersExist: async (nullifiers: Buffer[]) => {
|
|
1109
1352
|
const merkleTree = this.worldStateSynchronizer.getSnapshot(
|
|
1110
|
-
blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
|
|
1353
|
+
BlockNumber(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow),
|
|
1111
1354
|
);
|
|
1112
1355
|
const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
1113
1356
|
return indices.map(index => index !== undefined);
|
|
@@ -1129,9 +1372,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1129
1372
|
* @returns True if the attestation is valid, false otherwise.
|
|
1130
1373
|
*/
|
|
1131
1374
|
@trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
|
|
1132
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber
|
|
1375
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
1133
1376
|
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
1134
|
-
[Attributes.P2P_ID]: await attestation.
|
|
1377
|
+
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1135
1378
|
}))
|
|
1136
1379
|
public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
|
|
1137
1380
|
const severity = await this.attestationValidator.validate(attestation);
|
|
@@ -1174,7 +1417,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1174
1417
|
private async sendToPeers<T extends Gossipable>(message: T) {
|
|
1175
1418
|
const parent = message.constructor as typeof Gossipable;
|
|
1176
1419
|
|
|
1177
|
-
const identifier = await message.
|
|
1420
|
+
const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
|
|
1178
1421
|
this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
|
|
1179
1422
|
|
|
1180
1423
|
const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
|