@aztec/p2p 0.0.1-commit.b655e406 → 0.0.1-commit.fce3e4f
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 +4 -22
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +10 -5
- package/dest/config.d.ts +60 -54
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -1
- 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 +28 -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 +33 -32
- package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +13 -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 +40 -17
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +11 -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 +30 -8
- package/dest/mem_pools/attestation_pool/mocks.d.ts +224 -3
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +1 -1
- 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 +1 -36
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/index.d.ts +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
- 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 +11 -11
- 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 +21 -9
- 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 +1 -1
- package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +1 -1
- 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 +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 +1 -1
- 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 +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +1 -1
- 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 +1 -1
- package/dest/services/encoding.d.ts.map +1 -1
- 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 +11 -67
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +224 -54
- 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 +2 -0
- 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 +1 -1
- package/dest/services/reqresp/interface.d.ts.map +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 +1 -1
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block.d.ts +1 -1
- 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 +3 -5
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +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 +2 -2
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- 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/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 +1 -7
- 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 +1 -3
- 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 +1 -2
- 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 +1 -1
- 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/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 +11 -8
- package/dest/testbench/parse_log_file.d.ts +1 -1
- package/dest/testbench/testbench.d.ts +1 -1
- 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 +12 -6
- package/src/config.ts +18 -1
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +29 -5
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +45 -32
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +65 -23
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +50 -16
- package/src/mem_pools/attestation_pool/mocks.ts +1 -1
- package/src/mem_pools/instrumentation.ts +13 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +13 -15
- 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 +25 -9
- package/src/msg_validators/tx_validator/phases_validator.ts +3 -1
- package/src/services/libp2p/instrumentation.ts +10 -1
- package/src/services/libp2p/libp2p_service.ts +248 -59
- package/src/services/peer-manager/metrics.ts +10 -0
- package/src/services/peer-manager/peer_manager.ts +2 -0
- package/src/services/peer-manager/peer_scoring.ts +46 -3
- package/src/services/tx_collection/slow_tx_collection.ts +3 -2
- package/src/testbench/p2p_client_testbench_worker.ts +8 -5
- package/src/util.ts +12 -2
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
+
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
3
|
import { randomInt } from '@aztec/foundation/crypto';
|
|
3
4
|
import { Fr } from '@aztec/foundation/fields';
|
|
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';
|
|
@@ -116,7 +121,6 @@ type ReceivedMessageValidationResult<T> =
|
|
|
116
121
|
* Lib P2P implementation of the P2PService interface.
|
|
117
122
|
*/
|
|
118
123
|
export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
|
|
119
|
-
private jobQueue: SerialQueue = new SerialQueue();
|
|
120
124
|
private discoveryRunningPromise?: RunningPromise;
|
|
121
125
|
private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
|
|
122
126
|
|
|
@@ -140,6 +144,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
140
144
|
|
|
141
145
|
private instrumentation: P2PInstrumentation;
|
|
142
146
|
|
|
147
|
+
protected logger: Logger;
|
|
148
|
+
|
|
143
149
|
constructor(
|
|
144
150
|
private clientType: T,
|
|
145
151
|
private config: P2PConfig,
|
|
@@ -153,10 +159,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
153
159
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
154
160
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
155
161
|
telemetry: TelemetryClient,
|
|
156
|
-
|
|
162
|
+
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
157
163
|
) {
|
|
158
164
|
super(telemetry, 'LibP2PService');
|
|
159
165
|
|
|
166
|
+
// Create child logger with fisherman prefix if in fisherman mode
|
|
167
|
+
this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
|
|
168
|
+
|
|
160
169
|
this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
|
|
161
170
|
|
|
162
171
|
this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
@@ -174,15 +183,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
174
183
|
this.protocolVersion,
|
|
175
184
|
);
|
|
176
185
|
|
|
177
|
-
|
|
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);
|
|
178
190
|
this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
|
|
179
191
|
|
|
180
192
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
181
193
|
|
|
182
194
|
this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
|
|
183
195
|
this.logger.debug(
|
|
184
|
-
`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber
|
|
185
|
-
{ 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() },
|
|
186
198
|
);
|
|
187
199
|
return undefined;
|
|
188
200
|
};
|
|
@@ -395,7 +407,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
395
407
|
logger: createLibp2pComponentLogger(logger.module),
|
|
396
408
|
});
|
|
397
409
|
|
|
398
|
-
const peerScoring = new PeerScoring(config);
|
|
410
|
+
const peerScoring = new PeerScoring(config, telemetry);
|
|
399
411
|
const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
|
|
400
412
|
|
|
401
413
|
const peerManager = new PeerManager(
|
|
@@ -450,9 +462,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
450
462
|
}
|
|
451
463
|
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
452
464
|
|
|
453
|
-
// Start job queue, peer discovery service and libp2p node
|
|
454
|
-
this.jobQueue.start();
|
|
455
|
-
|
|
456
465
|
await this.peerManager.initializePeers();
|
|
457
466
|
if (!this.config.p2pDiscoveryDisabled) {
|
|
458
467
|
await this.peerDiscoveryService.start();
|
|
@@ -490,9 +499,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
490
499
|
// add GossipSub listener
|
|
491
500
|
this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
492
501
|
|
|
493
|
-
// Start running promise for peer discovery
|
|
502
|
+
// Start running promise for peer discovery and metrics collection
|
|
494
503
|
this.discoveryRunningPromise = new RunningPromise(
|
|
495
|
-
() =>
|
|
504
|
+
async () => {
|
|
505
|
+
await this.peerManager.heartbeat();
|
|
506
|
+
},
|
|
496
507
|
this.logger,
|
|
497
508
|
this.config.peerCheckIntervalMS,
|
|
498
509
|
);
|
|
@@ -501,9 +512,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
501
512
|
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
502
513
|
const reqrespSubProtocolValidators = {
|
|
503
514
|
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
504
|
-
// TODO(#11336): A request validator for blocks
|
|
505
515
|
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
506
516
|
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
517
|
+
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
|
|
507
518
|
};
|
|
508
519
|
await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
|
|
509
520
|
this.logger.info(`Started P2P service`, {
|
|
@@ -525,9 +536,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
525
536
|
// Stop peer manager
|
|
526
537
|
this.logger.debug('Stopping peer manager...');
|
|
527
538
|
await this.peerManager.stop();
|
|
528
|
-
|
|
529
|
-
this.logger.debug('Stopping job queue...');
|
|
530
|
-
await this.jobQueue.end();
|
|
531
539
|
this.logger.debug('Stopping running promise...');
|
|
532
540
|
await this.discoveryRunningPromise?.stop();
|
|
533
541
|
this.logger.debug('Stopping peer discovery service...');
|
|
@@ -615,7 +623,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
615
623
|
if (!this.node.services.pubsub) {
|
|
616
624
|
throw new Error('Pubsub service not available.');
|
|
617
625
|
}
|
|
618
|
-
const p2pMessage = P2PMessage.fromGossipable(message);
|
|
626
|
+
const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
|
|
619
627
|
const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
|
|
620
628
|
return result.recipients.length;
|
|
621
629
|
}
|
|
@@ -660,13 +668,39 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
660
668
|
return { result: true, topicType };
|
|
661
669
|
}
|
|
662
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
|
+
|
|
663
692
|
/**
|
|
664
693
|
* Handles a new gossip message that was received by the client.
|
|
665
694
|
* @param topic - The message's topic.
|
|
666
695
|
* @param data - The message data
|
|
667
696
|
*/
|
|
668
697
|
protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
|
|
669
|
-
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
|
+
}
|
|
670
704
|
|
|
671
705
|
const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
|
|
672
706
|
|
|
@@ -675,15 +709,25 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
675
709
|
}
|
|
676
710
|
|
|
677
711
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
712
|
+
topicType = TopicType.tx;
|
|
678
713
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
679
714
|
}
|
|
680
|
-
if (msg.topic === this.topicStrings[TopicType.block_attestation]
|
|
681
|
-
|
|
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
|
+
}
|
|
682
720
|
}
|
|
683
721
|
if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
722
|
+
topicType = TopicType.block_proposal;
|
|
684
723
|
await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
|
|
685
724
|
}
|
|
686
725
|
|
|
726
|
+
if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
|
|
727
|
+
const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
|
|
728
|
+
this.instrumentation.recordMessageLatency(topicType, latency);
|
|
729
|
+
}
|
|
730
|
+
|
|
687
731
|
return;
|
|
688
732
|
}
|
|
689
733
|
|
|
@@ -698,6 +742,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
698
742
|
try {
|
|
699
743
|
resultAndObj = await validationFunc();
|
|
700
744
|
} catch (err) {
|
|
745
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
|
|
701
746
|
this.logger.error(`Error deserializing and validating gossipsub message`, err, {
|
|
702
747
|
msgId,
|
|
703
748
|
source: source.toString(),
|
|
@@ -751,6 +796,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
751
796
|
return;
|
|
752
797
|
}
|
|
753
798
|
|
|
799
|
+
this.instrumentation.incrementTxReceived(1);
|
|
754
800
|
await this.mempools.txPool.addTxs([tx]);
|
|
755
801
|
}
|
|
756
802
|
|
|
@@ -763,12 +809,22 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
763
809
|
private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
764
810
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
|
|
765
811
|
const attestation = BlockAttestation.fromBuffer(payloadData);
|
|
812
|
+
const pool = this.mempools.attestationPool!;
|
|
766
813
|
const isValid = await this.validateAttestation(source, attestation);
|
|
767
|
-
const exists = isValid && (await
|
|
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
|
+
}
|
|
768
823
|
|
|
769
824
|
this.logger.trace(`Validate propagated block attestation`, {
|
|
770
825
|
isValid,
|
|
771
826
|
exists,
|
|
827
|
+
canAdd,
|
|
772
828
|
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
773
829
|
[Attributes.P2P_ID]: source.toString(),
|
|
774
830
|
});
|
|
@@ -777,6 +833,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
777
833
|
return { result: TopicValidatorResult.Reject };
|
|
778
834
|
} else if (exists) {
|
|
779
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 };
|
|
780
843
|
} else {
|
|
781
844
|
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
782
845
|
}
|
|
@@ -794,10 +857,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
794
857
|
}
|
|
795
858
|
|
|
796
859
|
this.logger.debug(
|
|
797
|
-
`Received attestation for slot ${attestation.slotNumber
|
|
860
|
+
`Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
|
|
798
861
|
{
|
|
799
|
-
p2pMessageIdentifier: await attestation.
|
|
800
|
-
slot: attestation.slotNumber
|
|
862
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
863
|
+
slot: attestation.slotNumber,
|
|
801
864
|
archive: attestation.archive.toString(),
|
|
802
865
|
source: source.toString(),
|
|
803
866
|
},
|
|
@@ -810,14 +873,17 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
810
873
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
|
|
811
874
|
const block = BlockProposal.fromBuffer(payloadData);
|
|
812
875
|
const isValid = await this.validateBlockProposal(source, block);
|
|
876
|
+
const pool = this.mempools.attestationPool;
|
|
813
877
|
|
|
814
878
|
// Note that we dont have an attestation pool if we're a prover node, but we still
|
|
815
879
|
// subscribe to block proposal topics in order to prevent their txs from being cleared.
|
|
816
|
-
const exists = isValid && (await
|
|
880
|
+
const exists = isValid && (await pool?.hasBlockProposal(block));
|
|
881
|
+
const canAdd = isValid && (await pool?.canAddProposal(block));
|
|
817
882
|
|
|
818
883
|
this.logger.trace(`Validate propagated block proposal`, {
|
|
819
884
|
isValid,
|
|
820
885
|
exists,
|
|
886
|
+
canAdd,
|
|
821
887
|
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
822
888
|
[Attributes.P2P_ID]: source.toString(),
|
|
823
889
|
});
|
|
@@ -826,6 +892,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
826
892
|
return { result: TopicValidatorResult.Reject };
|
|
827
893
|
} else if (exists) {
|
|
828
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 };
|
|
829
903
|
} else {
|
|
830
904
|
return { result: TopicValidatorResult.Accept, obj: block };
|
|
831
905
|
}
|
|
@@ -847,16 +921,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
847
921
|
|
|
848
922
|
// REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
|
|
849
923
|
@trackSpan('Libp2pService.processValidBlockProposal', async block => ({
|
|
850
|
-
[Attributes.SLOT_NUMBER]: block.slotNumber
|
|
924
|
+
[Attributes.SLOT_NUMBER]: block.slotNumber,
|
|
851
925
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
852
|
-
[Attributes.P2P_ID]: await block.
|
|
926
|
+
[Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
853
927
|
}))
|
|
854
928
|
private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
|
|
855
|
-
const slot = block.slotNumber
|
|
856
|
-
const previousSlot = slot -
|
|
929
|
+
const slot = block.slotNumber;
|
|
930
|
+
const previousSlot = SlotNumber(slot - 1);
|
|
857
931
|
this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
858
|
-
p2pMessageIdentifier: await block.
|
|
859
|
-
slot: block.slotNumber
|
|
932
|
+
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
|
|
933
|
+
slot: block.slotNumber,
|
|
860
934
|
archive: block.archive.toString(),
|
|
861
935
|
source: sender.toString(),
|
|
862
936
|
});
|
|
@@ -865,18 +939,31 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
865
939
|
this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
|
|
866
940
|
}
|
|
867
941
|
|
|
868
|
-
//
|
|
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
|
+
}
|
|
869
957
|
await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
|
|
870
|
-
await this.mempools.attestationPool?.addBlockProposal(block);
|
|
871
958
|
const attestations = await this.blockReceivedCallback(block, sender);
|
|
872
959
|
|
|
873
960
|
// TODO: fix up this pattern - the abstraction is not nice
|
|
874
|
-
// 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
|
|
875
962
|
if (attestations?.length) {
|
|
876
963
|
for (const attestation of attestations) {
|
|
877
|
-
this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber
|
|
878
|
-
p2pMessageIdentifier: await attestation.
|
|
879
|
-
slot: attestation.slotNumber
|
|
964
|
+
this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
|
|
965
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
966
|
+
slot: attestation.slotNumber,
|
|
880
967
|
archive: attestation.archive.toString(),
|
|
881
968
|
});
|
|
882
969
|
await this.broadcastAttestation(attestation);
|
|
@@ -889,9 +976,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
889
976
|
* @param attestation - The attestation to broadcast.
|
|
890
977
|
*/
|
|
891
978
|
@trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
|
|
892
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber
|
|
979
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
893
980
|
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
894
|
-
[Attributes.P2P_ID]: await attestation.
|
|
981
|
+
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
895
982
|
}))
|
|
896
983
|
private async broadcastAttestation(attestation: BlockAttestation) {
|
|
897
984
|
await this.propagate(attestation);
|
|
@@ -902,19 +989,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
902
989
|
* @param message - The message to propagate.
|
|
903
990
|
*/
|
|
904
991
|
public async propagate<T extends Gossipable>(message: T) {
|
|
905
|
-
const p2pMessageIdentifier = await message.
|
|
992
|
+
const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
|
|
906
993
|
this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
|
|
907
|
-
void this.
|
|
908
|
-
.
|
|
909
|
-
|
|
910
|
-
})
|
|
911
|
-
.catch(error => {
|
|
912
|
-
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
913
|
-
});
|
|
994
|
+
void this.sendToPeers(message).catch(error => {
|
|
995
|
+
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
996
|
+
});
|
|
914
997
|
}
|
|
915
998
|
|
|
916
999
|
/**
|
|
917
|
-
* Validate the requested block transactions.
|
|
1000
|
+
* Validate the requested block transactions. Allow partial returns.
|
|
918
1001
|
* @param request - The block transactions request.
|
|
919
1002
|
* @param response - The block transactions response.
|
|
920
1003
|
* @param peerId - The ID of the peer that made the request.
|
|
@@ -924,14 +1007,71 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
924
1007
|
[Attributes.BLOCK_HASH]: request.blockHash.toString(),
|
|
925
1008
|
}))
|
|
926
1009
|
private async validateRequestedBlockTxs(
|
|
927
|
-
|
|
1010
|
+
request: BlockTxsRequest,
|
|
928
1011
|
response: BlockTxsResponse,
|
|
929
1012
|
peerId: PeerId,
|
|
930
1013
|
): Promise<boolean> {
|
|
931
1014
|
const requestedTxValidator = this.createRequestedTxValidator();
|
|
932
1015
|
|
|
933
1016
|
try {
|
|
934
|
-
|
|
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
|
+
|
|
935
1075
|
await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
|
|
936
1076
|
return true;
|
|
937
1077
|
} catch (e: any) {
|
|
@@ -955,7 +1095,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
955
1095
|
* ReqRespSubProtocol.TX subprotocol validation.
|
|
956
1096
|
*
|
|
957
1097
|
* @param requestedTxHash - The collection of the txs that was requested.
|
|
958
|
-
* @param responseTx - The
|
|
1098
|
+
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
959
1099
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
960
1100
|
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
961
1101
|
*/
|
|
@@ -982,6 +1122,53 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
982
1122
|
}
|
|
983
1123
|
}
|
|
984
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(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
|
+
|
|
985
1172
|
private createRequestedTxValidator(): TxValidator {
|
|
986
1173
|
return new AggregateTxValidator(
|
|
987
1174
|
new DataTxValidator(),
|
|
@@ -996,19 +1183,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
996
1183
|
}
|
|
997
1184
|
|
|
998
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
|
+
|
|
999
1188
|
if (!(await tx.validateTxHash())) {
|
|
1000
|
-
|
|
1189
|
+
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1001
1190
|
throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
|
|
1002
1191
|
}
|
|
1003
1192
|
|
|
1004
1193
|
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1005
|
-
|
|
1194
|
+
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1006
1195
|
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1007
1196
|
}
|
|
1008
1197
|
|
|
1009
1198
|
const { result } = await txValidator.validateTx(tx);
|
|
1010
1199
|
if (result === 'invalid') {
|
|
1011
|
-
|
|
1200
|
+
penalize(PeerErrorSeverity.LowToleranceError);
|
|
1012
1201
|
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1013
1202
|
}
|
|
1014
1203
|
}
|
|
@@ -1183,9 +1372,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1183
1372
|
* @returns True if the attestation is valid, false otherwise.
|
|
1184
1373
|
*/
|
|
1185
1374
|
@trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
|
|
1186
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber
|
|
1375
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
1187
1376
|
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
1188
|
-
[Attributes.P2P_ID]: await attestation.
|
|
1377
|
+
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1189
1378
|
}))
|
|
1190
1379
|
public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
|
|
1191
1380
|
const severity = await this.attestationValidator.validate(attestation);
|
|
@@ -1228,7 +1417,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1228
1417
|
private async sendToPeers<T extends Gossipable>(message: T) {
|
|
1229
1418
|
const parent = message.constructor as typeof Gossipable;
|
|
1230
1419
|
|
|
1231
|
-
const identifier = await message.
|
|
1420
|
+
const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
|
|
1232
1421
|
this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
|
|
1233
1422
|
|
|
1234
1423
|
const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
|
|
@@ -15,6 +15,7 @@ export class PeerManagerMetrics {
|
|
|
15
15
|
private sentGoodbyes: UpDownCounter;
|
|
16
16
|
private receivedGoodbyes: UpDownCounter;
|
|
17
17
|
private peerCount: Gauge;
|
|
18
|
+
private lowScoreDisconnects: UpDownCounter;
|
|
18
19
|
|
|
19
20
|
public readonly tracer: Tracer;
|
|
20
21
|
|
|
@@ -40,6 +41,11 @@ export class PeerManagerMetrics {
|
|
|
40
41
|
unit: 'peers',
|
|
41
42
|
valueType: ValueType.INT,
|
|
42
43
|
});
|
|
44
|
+
this.lowScoreDisconnects = meter.createUpDownCounter(Metrics.PEER_MANAGER_LOW_SCORE_DISCONNECTS, {
|
|
45
|
+
description: 'Number of peers disconnected due to low score',
|
|
46
|
+
unit: 'peers',
|
|
47
|
+
valueType: ValueType.INT,
|
|
48
|
+
});
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
public recordGoodbyeSent(reason: GoodByeReason) {
|
|
@@ -53,4 +59,8 @@ export class PeerManagerMetrics {
|
|
|
53
59
|
public recordPeerCount(count: number) {
|
|
54
60
|
this.peerCount.record(count);
|
|
55
61
|
}
|
|
62
|
+
|
|
63
|
+
public recordLowScoreDisconnect(scoreState: 'Banned' | 'Disconnect') {
|
|
64
|
+
this.lowScoreDisconnects.add(1, { [Attributes.P2P_PEER_SCORE_STATE]: scoreState });
|
|
65
|
+
}
|
|
56
66
|
}
|
|
@@ -577,9 +577,11 @@ export class PeerManager implements PeerManagerInterface {
|
|
|
577
577
|
const score = this.peerScoring.getScoreState(peer.remotePeer.toString());
|
|
578
578
|
switch (score) {
|
|
579
579
|
case PeerScoreState.Banned:
|
|
580
|
+
this.metrics.recordLowScoreDisconnect('Banned');
|
|
580
581
|
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.BANNED);
|
|
581
582
|
break;
|
|
582
583
|
case PeerScoreState.Disconnect:
|
|
584
|
+
this.metrics.recordLowScoreDisconnect('Disconnect');
|
|
583
585
|
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.LOW_SCORE);
|
|
584
586
|
break;
|
|
585
587
|
case PeerScoreState.Healthy:
|