@aztec/p2p 5.0.0-private.20260319 → 5.0.0-rc.2
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/README.md +4 -1
- package/dest/bootstrap/bootstrap.d.ts +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +22 -6
- package/dest/client/factory.d.ts +4 -3
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +33 -24
- package/dest/client/interface.d.ts +43 -11
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +33 -19
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +100 -44
- package/dest/config.d.ts +137 -107
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +64 -23
- package/dest/errors/p2p-service.error.d.ts +9 -0
- package/dest/errors/p2p-service.error.d.ts.map +1 -0
- package/dest/errors/p2p-service.error.js +10 -0
- package/dest/errors/reqresp.error.d.ts +1 -20
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/errors/reqresp.error.js +0 -21
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +98 -56
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +265 -196
- 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 +175 -59
- package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +6 -4
- package/dest/mem_pools/index.d.ts +1 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.d.ts +1 -1
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +17 -1
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +1 -5
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +1 -7
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +22 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +13 -6
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +7 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +10 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +8 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +7 -4
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +8 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +153 -111
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +77 -14
- 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/aggregate_tx_validator.js +8 -15
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
- package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
- package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +36 -2
- package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +36 -17
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
- package/dest/msg_validators/tx_validator/index.d.ts +3 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +2 -0
- package/dest/msg_validators/tx_validator/phases_validator.js +3 -3
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/discv5/discV5_service.d.ts +13 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +119 -23
- package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
- package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
- package/dest/services/discv5/persisted_enr_store.js +145 -0
- package/dest/services/dummy_service.d.ts +15 -15
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +15 -16
- package/dest/services/encoding.d.ts +8 -8
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +11 -15
- package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +4 -14
- 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 +14 -0
- package/dest/services/libp2p/libp2p_service.d.ts +44 -39
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +383 -278
- package/dest/services/peer-manager/peer_manager.d.ts +20 -3
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +79 -23
- package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +102 -13
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +38 -44
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/interface.d.ts +16 -18
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +10 -20
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/metrics.js +0 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
- 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 +42 -24
- package/dest/services/reqresp/protocols/index.d.ts +1 -2
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +0 -1
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +1 -3
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
- package/dest/services/reqresp/reqresp.d.ts +7 -29
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +26 -207
- package/dest/services/service.d.ts +25 -12
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +2 -23
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +2 -55
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
- package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +39 -29
- package/dest/services/tx_collection/index.d.ts +2 -3
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +0 -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/instrumentation.js +0 -2
- package/dest/services/tx_collection/tx_collection.d.ts +35 -51
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +275 -119
- package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +2 -2
- package/dest/services/tx_collection/tx_source.d.ts +6 -5
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +13 -8
- package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_file_store/tx_file_store.js +4 -14
- package/dest/services/tx_provider.d.ts +3 -1
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +3 -0
- package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +5 -2
- package/dest/test-helpers/mock-pubsub.d.ts +23 -9
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +44 -44
- package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +16 -18
- package/dest/test-helpers/test_tx_provider.d.ts +3 -1
- package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
- package/dest/test-helpers/test_tx_provider.js +3 -0
- package/dest/test-helpers/testbench-utils.d.ts +14 -14
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +25 -13
- package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +102 -41
- package/dest/testbench/worker_client_manager.d.ts +10 -6
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +51 -9
- package/dest/util.d.ts +11 -6
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +35 -14
- package/dest/versioning.d.ts +3 -6
- package/dest/versioning.d.ts.map +1 -1
- package/dest/versioning.js +3 -24
- package/package.json +16 -16
- package/src/bootstrap/bootstrap.ts +17 -3
- package/src/client/factory.ts +52 -24
- package/src/client/interface.ts +38 -17
- package/src/client/p2p_client.ts +118 -76
- package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
- package/src/config.ts +130 -31
- package/src/errors/p2p-service.error.ts +11 -0
- package/src/errors/reqresp.error.ts +0 -25
- package/src/index.ts +0 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +319 -238
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +198 -62
- package/src/mem_pools/attestation_pool/mocks.ts +13 -8
- package/src/mem_pools/index.ts +0 -3
- package/src/mem_pools/instrumentation.ts +5 -1
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +1 -9
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/interfaces.ts +18 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +20 -8
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +12 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +8 -4
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +163 -117
- package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
- package/src/msg_validators/proposal_validator/README.md +1 -1
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
- package/src/msg_validators/proposal_validator/proposal_validator.ts +89 -11
- package/src/msg_validators/tx_validator/README.md +13 -3
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/data_validator.ts +44 -1
- package/src/msg_validators/tx_validator/factory.ts +44 -20
- package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
- package/src/msg_validators/tx_validator/index.ts +2 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +3 -3
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
- package/src/services/data_store.ts +5 -13
- package/src/services/discv5/discV5_service.ts +126 -14
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +20 -39
- package/src/services/encoding.ts +11 -21
- package/src/services/gossipsub/README.md +33 -11
- package/src/services/gossipsub/topic_score_params.ts +9 -18
- package/src/services/libp2p/instrumentation.ts +14 -0
- package/src/services/libp2p/libp2p_service.ts +436 -311
- package/src/services/peer-manager/peer_manager.ts +86 -22
- package/src/services/peer-manager/peer_scoring.ts +102 -8
- package/src/services/reqresp/README.md +0 -14
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +42 -52
- package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
- package/src/services/reqresp/config.ts +2 -2
- package/src/services/reqresp/interface.ts +21 -47
- package/src/services/reqresp/metrics.ts +0 -1
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
- package/src/services/reqresp/protocols/index.ts +0 -1
- package/src/services/reqresp/protocols/tx.ts +1 -3
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
- package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
- package/src/services/reqresp/reqresp.ts +29 -252
- package/src/services/service.ts +31 -28
- package/src/services/tx_collection/config.ts +3 -80
- package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
- package/src/services/tx_collection/file_store_tx_source.ts +43 -31
- package/src/services/tx_collection/index.ts +1 -6
- package/src/services/tx_collection/instrumentation.ts +1 -7
- package/src/services/tx_collection/tx_collection.ts +330 -173
- package/src/services/tx_collection/tx_collection_sink.ts +2 -2
- package/src/services/tx_collection/tx_source.ts +10 -8
- package/src/services/tx_file_store/tx_file_store.ts +5 -17
- package/src/services/tx_provider.ts +5 -0
- package/src/test-helpers/make-test-p2p-clients.ts +3 -0
- package/src/test-helpers/mock-pubsub.ts +46 -60
- package/src/test-helpers/reqresp-nodes.ts +12 -22
- package/src/test-helpers/test_tx_provider.ts +5 -0
- package/src/test-helpers/testbench-utils.ts +31 -26
- package/src/testbench/p2p_client_testbench_worker.ts +110 -55
- package/src/testbench/worker_client_manager.ts +59 -20
- package/src/util.ts +32 -17
- package/src/versioning.ts +3 -33
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -305
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
- package/dest/mem_pools/tx_pool/index.d.ts +0 -3
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.js +0 -2
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.js +0 -15
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
- package/dest/msg_validators/clock_tolerance.d.ts +0 -21
- package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
- package/dest/msg_validators/clock_tolerance.js +0 -38
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
- package/dest/services/reqresp/protocols/block.d.ts +0 -9
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.js +0 -32
- package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/fast_tx_collection.js +0 -311
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
- package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/slow_tx_collection.js +0 -211
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -344
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
- package/src/mem_pools/tx_pool/README.md +0 -270
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
- package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
- package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
- package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
- package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
- package/src/mem_pools/tx_pool/index.ts +0 -2
- package/src/mem_pools/tx_pool/priority.ts +0 -20
- package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
- package/src/msg_validators/clock_tolerance.ts +0 -52
- package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
- package/src/services/reqresp/protocols/block.ts +0 -37
- package/src/services/tx_collection/fast_tx_collection.ts +0 -361
- package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
- package/src/services/tx_collection/slow_tx_collection.ts +0 -266
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
2
|
import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { maxBy } from '@aztec/foundation/collection';
|
|
4
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
|
|
5
4
|
import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
6
5
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
7
6
|
import { Timer } from '@aztec/foundation/timer';
|
|
8
7
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
9
8
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
10
|
-
import type { EthAddress,
|
|
9
|
+
import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
|
|
10
|
+
import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/stdlib/config';
|
|
11
11
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
12
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
12
|
+
import { type BlockMinFeesProvider, GasFees, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
|
|
13
13
|
import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
14
14
|
import {
|
|
15
15
|
BlockProposal,
|
|
@@ -25,8 +25,9 @@ import {
|
|
|
25
25
|
getTopicsForConfig,
|
|
26
26
|
metricsTopicStrToLabels,
|
|
27
27
|
} from '@aztec/stdlib/p2p';
|
|
28
|
+
import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
|
|
28
29
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
29
|
-
import { Tx, type
|
|
30
|
+
import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
|
|
30
31
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
31
32
|
import { compressComponentVersions } from '@aztec/stdlib/versioning';
|
|
32
33
|
import {
|
|
@@ -51,13 +52,15 @@ import { yamux } from '@chainsafe/libp2p-yamux';
|
|
|
51
52
|
import { bootstrap } from '@libp2p/bootstrap';
|
|
52
53
|
import { identify } from '@libp2p/identify';
|
|
53
54
|
import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
54
|
-
import type { ConnectionManager } from '@libp2p/interface-internal';
|
|
55
|
+
import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
|
|
55
56
|
import { mplex } from '@libp2p/mplex';
|
|
56
57
|
import { tcp } from '@libp2p/tcp';
|
|
58
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
|
57
59
|
import { ENR } from '@nethermindeth/enr';
|
|
58
60
|
import { createLibp2p } from 'libp2p';
|
|
59
61
|
|
|
60
62
|
import type { P2PConfig } from '../../config.js';
|
|
63
|
+
import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
|
|
61
64
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
62
65
|
import {
|
|
63
66
|
BlockProposalValidator,
|
|
@@ -73,14 +76,14 @@ import {
|
|
|
73
76
|
createFirstStageTxValidationsForGossipedTransactions,
|
|
74
77
|
createSecondStageTxValidationsForGossipedTransactions,
|
|
75
78
|
createTxValidatorForBlockProposalReceivedTxs,
|
|
76
|
-
createTxValidatorForReqResponseReceivedTxs,
|
|
77
79
|
} from '../../msg_validators/tx_validator/factory.js';
|
|
80
|
+
import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
|
|
78
81
|
import { GossipSubEvent } from '../../types/index.js';
|
|
79
82
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
80
83
|
import { getVersions } from '../../versioning.js';
|
|
81
84
|
import { AztecDatastore } from '../data_store.js';
|
|
82
85
|
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
83
|
-
import { SnappyTransform,
|
|
86
|
+
import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
84
87
|
import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
85
88
|
import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
|
|
86
89
|
import type { PeerManagerInterface } from '../peer-manager/interface.js';
|
|
@@ -92,19 +95,15 @@ import {
|
|
|
92
95
|
AuthRequest,
|
|
93
96
|
BlockTxsRequest,
|
|
94
97
|
BlockTxsResponse,
|
|
95
|
-
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
96
98
|
type ReqRespInterface,
|
|
97
99
|
type ReqRespResponse,
|
|
98
100
|
ReqRespSubProtocol,
|
|
99
101
|
type ReqRespSubProtocolHandler,
|
|
100
102
|
type ReqRespSubProtocolHandlers,
|
|
101
|
-
type ReqRespSubProtocolValidators,
|
|
102
103
|
StatusMessage,
|
|
103
|
-
type SubProtocolMap,
|
|
104
104
|
ValidationError,
|
|
105
105
|
pingHandler,
|
|
106
106
|
reqGoodbyeHandler,
|
|
107
|
-
reqRespBlockHandler,
|
|
108
107
|
reqRespBlockTxsHandler,
|
|
109
108
|
reqRespStatusHandler,
|
|
110
109
|
reqRespTxHandler,
|
|
@@ -112,13 +111,33 @@ import {
|
|
|
112
111
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
113
112
|
import type {
|
|
114
113
|
P2PBlockReceivedCallback,
|
|
114
|
+
P2PCheckpointAttestationCallback,
|
|
115
115
|
P2PCheckpointReceivedCallback,
|
|
116
116
|
P2PDuplicateAttestationCallback,
|
|
117
|
+
P2POversizedProposalCallback,
|
|
117
118
|
P2PService,
|
|
118
119
|
PeerDiscoveryService,
|
|
119
120
|
} from '../service.js';
|
|
120
121
|
import { P2PInstrumentation } from './instrumentation.js';
|
|
121
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Builds the {@link ConsensusTimetable} shared by the gossip validators for proposal/attestation receive-window
|
|
125
|
+
* bounds. Derived purely from protocol slot-timing constants plus the block sub-slot duration and the consensus
|
|
126
|
+
* materialization grace, so every node agrees on these bounds without depending on proposer operational budgets.
|
|
127
|
+
*/
|
|
128
|
+
function buildConsensusTimetable(
|
|
129
|
+
config: P2PConfig,
|
|
130
|
+
l1Constants: ReturnType<EpochCacheInterface['getL1Constants']>,
|
|
131
|
+
): ConsensusTimetable {
|
|
132
|
+
const blockDuration = config.blockDurationMs / 1000;
|
|
133
|
+
return new ConsensusTimetable({
|
|
134
|
+
l1Constants,
|
|
135
|
+
blockDuration,
|
|
136
|
+
checkpointProposalSyncGrace:
|
|
137
|
+
config.checkpointProposalSyncGraceSeconds ?? getDefaultCheckpointProposalSyncGrace(blockDuration),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
122
141
|
interface ValidationResult {
|
|
123
142
|
name: string;
|
|
124
143
|
isValid: TxValidationResult;
|
|
@@ -130,7 +149,7 @@ type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: Vali
|
|
|
130
149
|
// REFACTOR: Unify with the type above
|
|
131
150
|
type ReceivedMessageValidationResult<T, M = undefined> =
|
|
132
151
|
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
|
|
133
|
-
| { obj?: T; result: TopicValidatorResult.Reject; metadata?: M };
|
|
152
|
+
| { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
|
|
134
153
|
|
|
135
154
|
/**
|
|
136
155
|
* Lib P2P implementation of the P2PService interface.
|
|
@@ -147,8 +166,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
147
166
|
private protocolVersion = '';
|
|
148
167
|
private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
|
|
149
168
|
|
|
150
|
-
private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
|
|
151
|
-
|
|
152
169
|
/** Callback invoked when a duplicate proposal is detected (triggers slashing). */
|
|
153
170
|
private duplicateProposalCallback?: (info: {
|
|
154
171
|
slot: SlotNumber;
|
|
@@ -156,9 +173,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
156
173
|
type: 'checkpoint' | 'block';
|
|
157
174
|
}) => void;
|
|
158
175
|
|
|
176
|
+
/** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
|
|
177
|
+
private oversizedProposalCallback?: P2POversizedProposalCallback;
|
|
178
|
+
|
|
159
179
|
/** Callback invoked when a duplicate attestation is detected (triggers slashing). */
|
|
160
180
|
private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
|
|
161
181
|
|
|
182
|
+
/** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
|
|
183
|
+
private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
|
|
184
|
+
|
|
162
185
|
/**
|
|
163
186
|
* Callback for when a block is received from a peer.
|
|
164
187
|
* @param block - The block received from the peer.
|
|
@@ -171,10 +194,19 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
171
194
|
* @param checkpoint - The checkpoint proposal received from the peer.
|
|
172
195
|
* @returns The attestations for the checkpoint, if any.
|
|
173
196
|
*/
|
|
174
|
-
private
|
|
197
|
+
private allNodesCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
|
|
198
|
+
/**
|
|
199
|
+
* Callback for when a checkpoint proposal is received - specifically for validators - from a peer.
|
|
200
|
+
* @param checkpoint - The checkpoint proposal received from the peer.
|
|
201
|
+
* @returns The attestations for the checkpoint, if any.
|
|
202
|
+
*/
|
|
203
|
+
private validatorCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
|
|
175
204
|
|
|
176
205
|
private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
|
|
177
206
|
|
|
207
|
+
private ipChangedHandler?: (ip: string) => void;
|
|
208
|
+
private discoveredP2pIp?: string;
|
|
209
|
+
|
|
178
210
|
private instrumentation: P2PInstrumentation;
|
|
179
211
|
|
|
180
212
|
private telemetry: TelemetryClient;
|
|
@@ -192,8 +224,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
192
224
|
private epochCache: EpochCacheInterface,
|
|
193
225
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
194
226
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
227
|
+
private blockMinFeesProvider: BlockMinFeesProvider,
|
|
195
228
|
telemetry: TelemetryClient,
|
|
196
229
|
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
230
|
+
private txValidationCache?: TxValidationCache,
|
|
197
231
|
) {
|
|
198
232
|
super(telemetry, 'LibP2PService');
|
|
199
233
|
this.telemetry = telemetry;
|
|
@@ -223,15 +257,39 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
223
257
|
this.protocolVersion,
|
|
224
258
|
);
|
|
225
259
|
|
|
260
|
+
// Build the consensus timetable once from protocol slot-timing constants and inject it into every
|
|
261
|
+
// validator so they share one set of receive-window bounds, independent of proposer operational budgets.
|
|
262
|
+
const consensusTimetable = buildConsensusTimetable(config, epochCache.getL1Constants());
|
|
226
263
|
const proposalValidatorOpts = {
|
|
227
264
|
txsPermitted: !config.disableTransactions,
|
|
228
265
|
maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
|
|
266
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
|
|
267
|
+
skipSlotValidation: config.skipProposalSlotValidation,
|
|
268
|
+
signatureContext: {
|
|
269
|
+
chainId: config.l1ChainId,
|
|
270
|
+
rollupAddress: config.rollupAddress,
|
|
271
|
+
},
|
|
272
|
+
clockDisparityMs: config.maxGossipClockDisparityMs,
|
|
273
|
+
};
|
|
274
|
+
this.blockProposalValidator = new BlockProposalValidator(epochCache, consensusTimetable, proposalValidatorOpts);
|
|
275
|
+
this.checkpointProposalValidator = new CheckpointProposalValidator(
|
|
276
|
+
epochCache,
|
|
277
|
+
consensusTimetable,
|
|
278
|
+
proposalValidatorOpts,
|
|
279
|
+
);
|
|
280
|
+
const attestationValidatorOpts = {
|
|
281
|
+
signatureContext: proposalValidatorOpts.signatureContext,
|
|
282
|
+
clockDisparityMs: config.maxGossipClockDisparityMs,
|
|
229
283
|
};
|
|
230
|
-
this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
|
|
231
|
-
this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
|
|
232
284
|
this.checkpointAttestationValidator = config.fishermanMode
|
|
233
|
-
? new FishermanAttestationValidator(
|
|
234
|
-
|
|
285
|
+
? new FishermanAttestationValidator(
|
|
286
|
+
epochCache,
|
|
287
|
+
consensusTimetable,
|
|
288
|
+
mempools.attestationPool,
|
|
289
|
+
telemetry,
|
|
290
|
+
attestationValidatorOpts,
|
|
291
|
+
)
|
|
292
|
+
: new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
|
|
235
293
|
|
|
236
294
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
237
295
|
|
|
@@ -243,18 +301,22 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
243
301
|
return true;
|
|
244
302
|
};
|
|
245
303
|
|
|
246
|
-
this.
|
|
247
|
-
|
|
304
|
+
this.allNodesCheckpointReceivedCallback = (
|
|
305
|
+
_checkpoint: CheckpointProposalCore,
|
|
306
|
+
): Promise<CheckpointAttestation[] | undefined> => {
|
|
307
|
+
throw new CheckpointProposalReceivedCallbackNotRegisteredError();
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
this.validatorCheckpointReceivedCallback = (
|
|
311
|
+
_checkpoint: CheckpointProposalCore,
|
|
248
312
|
): Promise<CheckpointAttestation[] | undefined> => {
|
|
249
|
-
this.logger.debug(
|
|
250
|
-
`Handler not yet registered: Checkpoint received callback not set. Received checkpoint for slot ${checkpoint.slotNumber} from peer.`,
|
|
251
|
-
);
|
|
252
313
|
return Promise.resolve(undefined);
|
|
253
314
|
};
|
|
254
315
|
}
|
|
255
316
|
|
|
256
|
-
public updateConfig(config: Partial<P2PReqRespConfig
|
|
317
|
+
public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
|
|
257
318
|
this.reqresp.updateConfig(config);
|
|
319
|
+
this.config = merge(this.config, config);
|
|
258
320
|
}
|
|
259
321
|
|
|
260
322
|
/**
|
|
@@ -273,9 +335,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
273
335
|
proofVerifier: ClientProtocolCircuitVerifier;
|
|
274
336
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
275
337
|
peerStore: AztecAsyncKVStore;
|
|
338
|
+
blockMinFeesProvider: BlockMinFeesProvider;
|
|
276
339
|
telemetry: TelemetryClient;
|
|
277
340
|
logger: Logger;
|
|
278
341
|
packageVersion: string;
|
|
342
|
+
txValidationCache?: TxValidationCache;
|
|
279
343
|
},
|
|
280
344
|
) {
|
|
281
345
|
const {
|
|
@@ -285,9 +349,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
285
349
|
mempools,
|
|
286
350
|
proofVerifier,
|
|
287
351
|
peerStore,
|
|
352
|
+
blockMinFeesProvider,
|
|
288
353
|
telemetry,
|
|
289
354
|
logger,
|
|
290
355
|
packageVersion,
|
|
356
|
+
txValidationCache,
|
|
291
357
|
} = deps;
|
|
292
358
|
const { p2pPort, maxPeerCount, listenAddress } = config;
|
|
293
359
|
const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
|
|
@@ -302,6 +368,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
302
368
|
packageVersion,
|
|
303
369
|
telemetry,
|
|
304
370
|
createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
|
|
371
|
+
peerStore,
|
|
305
372
|
);
|
|
306
373
|
|
|
307
374
|
// Seed libp2p's bootstrap discovery with private and trusted peers
|
|
@@ -316,34 +383,49 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
316
383
|
const protocolVersion = compressComponentVersions(versions);
|
|
317
384
|
|
|
318
385
|
const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
319
|
-
const directPeers = (
|
|
386
|
+
const directPeers = compactArray(
|
|
320
387
|
await Promise.all(
|
|
321
388
|
preferredPeersEnrs.map(async enr => {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
389
|
+
try {
|
|
390
|
+
const peerId = await enr.peerId();
|
|
391
|
+
const address = enr.getLocationMultiaddr('tcp');
|
|
392
|
+
if (address === undefined) {
|
|
393
|
+
throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
id: peerId,
|
|
397
|
+
addrs: [address],
|
|
398
|
+
};
|
|
399
|
+
} catch (err) {
|
|
400
|
+
// A malformed configured ENR shouldn't abort node setup — skip it and log.
|
|
401
|
+
logger.warn(`Skipping preferred peer with invalid ENR`, { err });
|
|
402
|
+
return undefined;
|
|
326
403
|
}
|
|
327
|
-
return {
|
|
328
|
-
id: peerId,
|
|
329
|
-
addrs: [address],
|
|
330
|
-
};
|
|
331
404
|
}),
|
|
332
|
-
)
|
|
333
|
-
)
|
|
405
|
+
),
|
|
406
|
+
);
|
|
334
407
|
|
|
335
408
|
const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
|
|
336
409
|
|
|
337
|
-
// Create dynamic topic score params based on network configuration
|
|
410
|
+
// Create dynamic topic score params based on network configuration. Scoring uses the network-wide
|
|
411
|
+
// max-blocks-per-checkpoint config value directly to size expected per-slot message rates; these are
|
|
412
|
+
// peer-rate thresholds, not consensus deadlines, so they need no proposer operational budgets.
|
|
338
413
|
const l1Constants = epochCache.getL1Constants();
|
|
339
414
|
const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
|
|
340
415
|
slotDurationMs: l1Constants.slotDuration * 1000,
|
|
341
416
|
heartbeatIntervalMs: config.gossipsubInterval,
|
|
342
417
|
targetCommitteeSize: l1Constants.targetCommitteeSize,
|
|
343
|
-
|
|
418
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
|
|
344
419
|
expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
|
|
345
420
|
});
|
|
346
421
|
|
|
422
|
+
// Restrict gossipsub to exactly the topics we subscribe to. Without this, an arbitrary-topic
|
|
423
|
+
// message is transformed, msg-id'd and inserted into the seenCache before the subscription check,
|
|
424
|
+
// so a crafted topic colliding on msg id can suppress a real message as a duplicate.
|
|
425
|
+
const allowedTopics = getTopicsForConfig(config.disableTransactions).map(topic =>
|
|
426
|
+
createTopicString(topic, protocolVersion),
|
|
427
|
+
);
|
|
428
|
+
|
|
347
429
|
const node = await createLibp2p({
|
|
348
430
|
start: false,
|
|
349
431
|
peerId,
|
|
@@ -389,7 +471,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
389
471
|
},
|
|
390
472
|
connectionGater: {
|
|
391
473
|
denyInboundConnection: (maConn: MultiaddrConnection) => {
|
|
392
|
-
const allowed = peerManager.
|
|
474
|
+
const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
|
|
393
475
|
if (allowed) {
|
|
394
476
|
return false;
|
|
395
477
|
}
|
|
@@ -400,7 +482,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
400
482
|
denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
|
|
401
483
|
//NOTE: it is not necessary to check address here because this was already done by
|
|
402
484
|
// denyInboundConnection
|
|
403
|
-
const allowed = peerManager.
|
|
485
|
+
const allowed = peerManager.isPeerAllowedToConnect(peerId);
|
|
404
486
|
if (allowed) {
|
|
405
487
|
return false;
|
|
406
488
|
}
|
|
@@ -428,9 +510,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
428
510
|
mcacheLength: config.gossipsubMcacheLength,
|
|
429
511
|
mcacheGossip: config.gossipsubMcacheGossip,
|
|
430
512
|
seenTTL: config.gossipsubSeenTTL,
|
|
513
|
+
allowedTopics,
|
|
514
|
+
// No fastMsgIdFn: the fast-path dedup cache keys on a non-cryptographic 64-bit hash of the
|
|
515
|
+
// raw data only (no topic), so a collision — accidental or engineered via a weak seed — drops
|
|
516
|
+
// a different message with no fallback to the full id. Dedup instead rests solely on the
|
|
517
|
+
// cryptographic, topic-framed msgIdFn below.
|
|
431
518
|
msgIdFn: getMsgIdFn,
|
|
432
519
|
msgIdToStrFn: msgIdToStrFn,
|
|
433
|
-
fastMsgIdFn: fastMsgIdFn,
|
|
434
520
|
dataTransform: new SnappyTransform(),
|
|
435
521
|
metricsRegister: otelMetricsAdapter,
|
|
436
522
|
metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
|
|
@@ -442,8 +528,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
442
528
|
topics: topicScoreParams,
|
|
443
529
|
}),
|
|
444
530
|
}) as (components: GossipSubComponents) => GossipSub,
|
|
445
|
-
components: (components: { connectionManager: ConnectionManager }) => ({
|
|
531
|
+
components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
|
|
446
532
|
connectionManager: components.connectionManager,
|
|
533
|
+
addressManager: components.addressManager,
|
|
447
534
|
}),
|
|
448
535
|
},
|
|
449
536
|
logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
|
|
@@ -465,6 +552,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
465
552
|
epochCache,
|
|
466
553
|
);
|
|
467
554
|
|
|
555
|
+
// Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
|
|
556
|
+
reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
|
|
557
|
+
|
|
468
558
|
// Configure application-specific scoring for gossipsub.
|
|
469
559
|
// The weight scales app score to align with gossipsub thresholds:
|
|
470
560
|
// - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
|
|
@@ -485,8 +575,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
485
575
|
epochCache,
|
|
486
576
|
proofVerifier,
|
|
487
577
|
worldStateSynchronizer,
|
|
578
|
+
blockMinFeesProvider,
|
|
488
579
|
telemetry,
|
|
489
580
|
logger,
|
|
581
|
+
txValidationCache,
|
|
490
582
|
);
|
|
491
583
|
}
|
|
492
584
|
|
|
@@ -500,24 +592,21 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
500
592
|
throw new Error('P2P service already started');
|
|
501
593
|
}
|
|
502
594
|
|
|
503
|
-
// Get listen & announce addresses for logging
|
|
504
595
|
const { p2pIp, p2pPort } = this.config;
|
|
505
|
-
if (!p2pIp) {
|
|
506
|
-
throw new Error('Announce address not provided.');
|
|
596
|
+
if (!p2pIp && !this.config.queryForIp) {
|
|
597
|
+
throw new Error('Announce address not provided and queryForIp is not enabled.');
|
|
507
598
|
}
|
|
508
|
-
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
599
|
+
const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
|
|
509
600
|
|
|
510
601
|
// Create request response protocol handlers
|
|
511
602
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
512
603
|
const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
|
|
513
|
-
const blockHandler = reqRespBlockHandler(this.archiver);
|
|
514
604
|
const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
|
|
515
605
|
|
|
516
606
|
const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
|
|
517
607
|
[ReqRespSubProtocol.PING]: pingHandler,
|
|
518
608
|
[ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
|
|
519
609
|
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
|
|
520
|
-
[ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
|
|
521
610
|
};
|
|
522
611
|
|
|
523
612
|
if (!this.config.disableTransactions) {
|
|
@@ -533,17 +622,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
533
622
|
requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
|
|
534
623
|
}
|
|
535
624
|
|
|
536
|
-
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
537
|
-
const reqrespSubProtocolValidators = {
|
|
538
|
-
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
539
|
-
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
540
|
-
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
541
|
-
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
|
|
542
|
-
};
|
|
543
|
-
|
|
544
625
|
await this.peerManager.initializePeers();
|
|
545
626
|
|
|
546
|
-
await this.reqresp.start(requestResponseHandlers
|
|
627
|
+
await this.reqresp.start(requestResponseHandlers);
|
|
547
628
|
|
|
548
629
|
await this.node.start();
|
|
549
630
|
|
|
@@ -559,6 +640,38 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
559
640
|
if (!this.config.p2pDiscoveryDisabled) {
|
|
560
641
|
await this.peerDiscoveryService.start();
|
|
561
642
|
}
|
|
643
|
+
|
|
644
|
+
// Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
|
|
645
|
+
if (this.config.queryForIp) {
|
|
646
|
+
this.discoveredP2pIp = this.config.p2pIp;
|
|
647
|
+
this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
|
|
648
|
+
this.ipChangedHandler = (ip: string) => {
|
|
649
|
+
const addressManager = this.node.services.components.addressManager;
|
|
650
|
+
const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
|
|
651
|
+
const previousIp = this.discoveredP2pIp;
|
|
652
|
+
|
|
653
|
+
if (previousIp) {
|
|
654
|
+
const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
|
|
655
|
+
addressManager.removeObservedAddr(oldAddr);
|
|
656
|
+
this.logger.info('Libp2p announce address updated due to IP change', {
|
|
657
|
+
previousIp,
|
|
658
|
+
newIp: ip,
|
|
659
|
+
newMultiaddr: newAddr.toString(),
|
|
660
|
+
});
|
|
661
|
+
} else {
|
|
662
|
+
this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
|
|
663
|
+
ip,
|
|
664
|
+
multiaddr: newAddr.toString(),
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
addressManager.addObservedAddr(newAddr);
|
|
669
|
+
addressManager.confirmObservedAddr(newAddr);
|
|
670
|
+
this.discoveredP2pIp = ip;
|
|
671
|
+
};
|
|
672
|
+
this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
|
|
673
|
+
}
|
|
674
|
+
|
|
562
675
|
this.discoveryRunningPromise = new RunningPromise(
|
|
563
676
|
async () => {
|
|
564
677
|
await this.peerManager.heartbeat();
|
|
@@ -584,6 +697,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
584
697
|
// Remove gossip sub listener
|
|
585
698
|
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
586
699
|
|
|
700
|
+
if (this.ipChangedHandler) {
|
|
701
|
+
this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
|
|
702
|
+
this.ipChangedHandler = undefined;
|
|
703
|
+
}
|
|
704
|
+
|
|
587
705
|
// Stop peer manager
|
|
588
706
|
this.logger.debug('Stopping peer manager...');
|
|
589
707
|
await this.peerManager.stop();
|
|
@@ -598,12 +716,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
598
716
|
this.logger.info('LibP2P service stopped');
|
|
599
717
|
}
|
|
600
718
|
|
|
601
|
-
addReqRespSubProtocol(
|
|
602
|
-
subProtocol
|
|
603
|
-
handler: ReqRespSubProtocolHandler,
|
|
604
|
-
validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
|
|
605
|
-
): Promise<void> {
|
|
606
|
-
return this.reqresp.addSubProtocol(subProtocol, handler, validator);
|
|
719
|
+
addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
|
|
720
|
+
return this.reqresp.addSubProtocol(subProtocol, handler);
|
|
607
721
|
}
|
|
608
722
|
|
|
609
723
|
public registerThisValidatorAddresses(address: EthAddress[]): void {
|
|
@@ -631,20 +745,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
631
745
|
setImmediate(() => void safeJob());
|
|
632
746
|
}
|
|
633
747
|
|
|
634
|
-
/**
|
|
635
|
-
* Send a batch of requests to peers, and return the responses
|
|
636
|
-
* @param protocol - The request response protocol to use
|
|
637
|
-
* @param requests - The requests to send to the peers
|
|
638
|
-
* @returns The responses to the requests
|
|
639
|
-
*/
|
|
640
|
-
sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
|
|
641
|
-
protocol: SubProtocol,
|
|
642
|
-
requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
|
|
643
|
-
pinnedPeerId: PeerId | undefined,
|
|
644
|
-
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
|
|
645
|
-
return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
748
|
public sendRequestToPeer(
|
|
649
749
|
peerId: PeerId,
|
|
650
750
|
subProtocol: ReqRespSubProtocol,
|
|
@@ -666,8 +766,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
666
766
|
this.blockReceivedCallback = callback;
|
|
667
767
|
}
|
|
668
768
|
|
|
669
|
-
public
|
|
670
|
-
this.
|
|
769
|
+
public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
|
|
770
|
+
this.validatorCheckpointReceivedCallback = callback;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
|
|
774
|
+
this.allNodesCheckpointReceivedCallback = callback;
|
|
671
775
|
}
|
|
672
776
|
|
|
673
777
|
/**
|
|
@@ -680,6 +784,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
680
784
|
this.duplicateProposalCallback = callback;
|
|
681
785
|
}
|
|
682
786
|
|
|
787
|
+
/**
|
|
788
|
+
* Registers a callback to be invoked when an oversized block proposal is stored as slashing evidence.
|
|
789
|
+
*/
|
|
790
|
+
public registerOversizedProposalCallback(callback: P2POversizedProposalCallback): void {
|
|
791
|
+
this.oversizedProposalCallback = callback;
|
|
792
|
+
}
|
|
793
|
+
|
|
683
794
|
/**
|
|
684
795
|
* Registers a callback to be invoked when a duplicate attestation is detected.
|
|
685
796
|
* A validator signing attestations for different proposals at the same slot.
|
|
@@ -689,6 +800,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
689
800
|
this.duplicateAttestationCallback = callback;
|
|
690
801
|
}
|
|
691
802
|
|
|
803
|
+
public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
|
|
804
|
+
this.checkpointAttestationCallback = callback;
|
|
805
|
+
}
|
|
806
|
+
|
|
692
807
|
/**
|
|
693
808
|
* Subscribes to a topic.
|
|
694
809
|
* @param topic - The topic to subscribe to.
|
|
@@ -817,6 +932,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
817
932
|
|
|
818
933
|
// Process the message, optionally within a linked span for trace propagation
|
|
819
934
|
const processMessage = async () => {
|
|
935
|
+
if (
|
|
936
|
+
this.config.skipIncomingProposals &&
|
|
937
|
+
(msg.topic === this.topicStrings[TopicType.block_proposal] ||
|
|
938
|
+
msg.topic === this.topicStrings[TopicType.checkpoint_proposal])
|
|
939
|
+
) {
|
|
940
|
+
this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, { topic: msg.topic });
|
|
941
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
820
944
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
821
945
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
822
946
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
@@ -882,30 +1006,67 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
882
1006
|
source: PeerId,
|
|
883
1007
|
topicType: TopicType,
|
|
884
1008
|
): Promise<ReceivedMessageValidationResult<T, M>> {
|
|
885
|
-
|
|
1009
|
+
// Default to reject result with a penalty if validation function throws an error
|
|
1010
|
+
let resultAndObj: ReceivedMessageValidationResult<T, M> = {
|
|
1011
|
+
result: TopicValidatorResult.Reject,
|
|
1012
|
+
severity: PeerErrorSeverity.MidToleranceError,
|
|
1013
|
+
};
|
|
886
1014
|
const timer = new Timer();
|
|
887
1015
|
try {
|
|
888
1016
|
resultAndObj = await validationFunc();
|
|
889
1017
|
} catch (err) {
|
|
890
|
-
this.
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1018
|
+
this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const validationTimeMs = timer.ms();
|
|
1022
|
+
const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
|
|
1023
|
+
if (validationTimeMs > mcacheWindowMs * 0.75) {
|
|
1024
|
+
this.instrumentation.incSlowValidation(topicType);
|
|
1025
|
+
this.logger.warn(
|
|
1026
|
+
`Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
|
|
1027
|
+
`Message forwarding may be skipped if validation exceeds the window.`,
|
|
1028
|
+
{ msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs },
|
|
1029
|
+
);
|
|
896
1030
|
}
|
|
897
1031
|
|
|
898
1032
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
1033
|
+
this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
|
|
899
1034
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
1035
|
+
} else if (resultAndObj.result === TopicValidatorResult.Reject) {
|
|
1036
|
+
this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
|
|
1037
|
+
msgId,
|
|
1038
|
+
source: source.toString(),
|
|
1039
|
+
topicType,
|
|
1040
|
+
severity: resultAndObj.severity,
|
|
1041
|
+
});
|
|
1042
|
+
this.peerManager.penalizePeer(source, resultAndObj.severity);
|
|
1043
|
+
} else {
|
|
1044
|
+
this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
|
|
900
1045
|
}
|
|
901
1046
|
|
|
902
1047
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
903
1048
|
return resultAndObj;
|
|
904
1049
|
}
|
|
905
1050
|
|
|
1051
|
+
private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
|
|
1052
|
+
try {
|
|
1053
|
+
return deserializeFunc();
|
|
1054
|
+
} catch (err) {
|
|
1055
|
+
this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
|
|
1056
|
+
err,
|
|
1057
|
+
msgId,
|
|
1058
|
+
source: source.toString(),
|
|
1059
|
+
});
|
|
1060
|
+
return undefined;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
906
1064
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
907
1065
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
908
|
-
const tx = Tx.fromBuffer(payloadData);
|
|
1066
|
+
const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
|
|
1067
|
+
if (!tx) {
|
|
1068
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
|
|
1069
|
+
}
|
|
909
1070
|
|
|
910
1071
|
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
911
1072
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
@@ -930,8 +1091,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
930
1091
|
severity,
|
|
931
1092
|
source: source.toString(),
|
|
932
1093
|
});
|
|
933
|
-
|
|
934
|
-
return { result: TopicValidatorResult.Reject };
|
|
1094
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
935
1095
|
}
|
|
936
1096
|
|
|
937
1097
|
// Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
|
|
@@ -953,8 +1113,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
953
1113
|
severity,
|
|
954
1114
|
source: source.toString(),
|
|
955
1115
|
});
|
|
956
|
-
|
|
957
|
-
return { result: TopicValidatorResult.Reject };
|
|
1116
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
958
1117
|
}
|
|
959
1118
|
|
|
960
1119
|
// Pool add: persist the tx
|
|
@@ -979,8 +1138,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
979
1138
|
source: source.toString(),
|
|
980
1139
|
txHash: txHash.toString(),
|
|
981
1140
|
});
|
|
982
|
-
|
|
983
|
-
return { result: TopicValidatorResult.Reject };
|
|
1141
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
984
1142
|
}
|
|
985
1143
|
};
|
|
986
1144
|
|
|
@@ -1010,7 +1168,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1010
1168
|
source: PeerId,
|
|
1011
1169
|
): Promise<void> {
|
|
1012
1170
|
const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
|
|
1013
|
-
() =>
|
|
1171
|
+
() => {
|
|
1172
|
+
const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
|
|
1173
|
+
if (!attestation) {
|
|
1174
|
+
return Promise.resolve({
|
|
1175
|
+
result: TopicValidatorResult.Reject,
|
|
1176
|
+
severity: PeerErrorSeverity.LowToleranceError,
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
return this.validateAndStoreCheckpointAttestation(source, attestation);
|
|
1180
|
+
},
|
|
1014
1181
|
msgId,
|
|
1015
1182
|
source,
|
|
1016
1183
|
TopicType.checkpoint_attestation,
|
|
@@ -1043,8 +1210,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1043
1210
|
|
|
1044
1211
|
if (validationResult.result === 'reject') {
|
|
1045
1212
|
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1046
|
-
|
|
1047
|
-
return { result: TopicValidatorResult.Reject };
|
|
1213
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1048
1214
|
}
|
|
1049
1215
|
|
|
1050
1216
|
if (validationResult.result === 'ignore') {
|
|
@@ -1070,16 +1236,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1070
1236
|
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1071
1237
|
}
|
|
1072
1238
|
|
|
1073
|
-
// Could not add (cap reached for signer),
|
|
1239
|
+
// Could not add (cap reached for signer), penalize and do not re-broadcast
|
|
1074
1240
|
if (!added) {
|
|
1075
|
-
this.logger.warn(`
|
|
1241
|
+
this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
|
|
1076
1242
|
slot: slot.toString(),
|
|
1077
1243
|
archive: attestation.archive.toString(),
|
|
1078
1244
|
source: peerId.toString(),
|
|
1079
1245
|
attester: attestation.getSender()?.toString(),
|
|
1080
1246
|
count,
|
|
1081
1247
|
});
|
|
1082
|
-
return { result: TopicValidatorResult.
|
|
1248
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
1083
1249
|
}
|
|
1084
1250
|
|
|
1085
1251
|
// Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
|
|
@@ -1098,6 +1264,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1098
1264
|
}
|
|
1099
1265
|
|
|
1100
1266
|
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1267
|
+
this.checkpointAttestationCallback?.(attestation);
|
|
1101
1268
|
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
1102
1269
|
}
|
|
1103
1270
|
|
|
@@ -1105,16 +1272,17 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1105
1272
|
const {
|
|
1106
1273
|
result,
|
|
1107
1274
|
obj: block,
|
|
1108
|
-
metadata: { isEquivocated } = {},
|
|
1109
|
-
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
|
|
1275
|
+
metadata: { isEquivocated, isOversized } = {},
|
|
1276
|
+
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
|
|
1110
1277
|
() => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
|
|
1111
1278
|
msgId,
|
|
1112
1279
|
source,
|
|
1113
1280
|
TopicType.block_proposal,
|
|
1114
1281
|
);
|
|
1115
1282
|
|
|
1116
|
-
// If not accepted or
|
|
1117
|
-
|
|
1283
|
+
// If not accepted, equivocated, or oversized, return. Oversized proposals are re-broadcast as
|
|
1284
|
+
// slashing evidence but never attested or processed.
|
|
1285
|
+
if (result !== TopicValidatorResult.Accept || !block || isEquivocated || isOversized) {
|
|
1118
1286
|
return;
|
|
1119
1287
|
}
|
|
1120
1288
|
|
|
@@ -1129,13 +1297,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1129
1297
|
protected async validateAndStoreBlockProposal(
|
|
1130
1298
|
peerId: PeerId,
|
|
1131
1299
|
block: BlockProposal,
|
|
1132
|
-
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
|
|
1300
|
+
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
|
|
1133
1301
|
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1134
1302
|
|
|
1135
1303
|
if (validationResult.result === 'reject') {
|
|
1136
1304
|
this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1137
|
-
|
|
1138
|
-
return { result: TopicValidatorResult.Reject };
|
|
1305
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1139
1306
|
}
|
|
1140
1307
|
|
|
1141
1308
|
if (validationResult.result === 'ignore') {
|
|
@@ -1145,6 +1312,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1145
1312
|
// Try to add the proposal: this handles existence check, cap check, and adding in one call
|
|
1146
1313
|
const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
|
|
1147
1314
|
const isEquivocated = count !== undefined && count > 1;
|
|
1315
|
+
// An oversized proposal (index at or beyond the consensus per-checkpoint limit) is structurally valid
|
|
1316
|
+
// proposer misbehavior: it is stored and re-broadcast as slashing evidence but never processed or
|
|
1317
|
+
// attested to. No-ops when maxBlocksPerCheckpoint is unset (local/test).
|
|
1318
|
+
const isOversized =
|
|
1319
|
+
this.config.maxBlocksPerCheckpoint !== undefined &&
|
|
1320
|
+
block.indexWithinCheckpoint >= this.config.maxBlocksPerCheckpoint;
|
|
1148
1321
|
|
|
1149
1322
|
// Duplicate proposal received, no need to re-broadcast
|
|
1150
1323
|
if (alreadyExists) {
|
|
@@ -1154,12 +1327,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1154
1327
|
proposer: block.getSender()?.toString(),
|
|
1155
1328
|
source: peerId.toString(),
|
|
1156
1329
|
});
|
|
1157
|
-
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
|
|
1330
|
+
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1158
1331
|
}
|
|
1159
1332
|
|
|
1160
1333
|
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
1161
1334
|
if (!added) {
|
|
1162
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
1163
1335
|
this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
|
|
1164
1336
|
...block.toBlockInfo(),
|
|
1165
1337
|
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
@@ -1167,7 +1339,27 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1167
1339
|
proposer: block.getSender()?.toString(),
|
|
1168
1340
|
source: peerId.toString(),
|
|
1169
1341
|
});
|
|
1170
|
-
return {
|
|
1342
|
+
return {
|
|
1343
|
+
result: TopicValidatorResult.Reject,
|
|
1344
|
+
metadata: { isEquivocated, isOversized },
|
|
1345
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// The proposal was stored: if oversized, invoke the oversized callback so the proposer can be
|
|
1350
|
+
// slashed. Fired alongside (not instead of) equivocation detection below.
|
|
1351
|
+
if (isOversized) {
|
|
1352
|
+
const proposer = block.getSender();
|
|
1353
|
+
if (proposer) {
|
|
1354
|
+
this.logger.warn(`Detected oversized block proposal at slot ${block.slotNumber}`, {
|
|
1355
|
+
...block.toBlockInfo(),
|
|
1356
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1357
|
+
maxBlocksPerCheckpoint: this.config.maxBlocksPerCheckpoint,
|
|
1358
|
+
source: peerId.toString(),
|
|
1359
|
+
proposer: proposer.toString(),
|
|
1360
|
+
});
|
|
1361
|
+
this.oversizedProposalCallback?.({ slot: block.slotNumber, proposer });
|
|
1362
|
+
}
|
|
1171
1363
|
}
|
|
1172
1364
|
|
|
1173
1365
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
@@ -1183,11 +1375,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1183
1375
|
if (proposer && count === 2) {
|
|
1184
1376
|
this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
|
|
1185
1377
|
}
|
|
1186
|
-
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
|
|
1378
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1187
1379
|
}
|
|
1188
1380
|
|
|
1189
1381
|
// Otherwise, we're good to go!
|
|
1190
|
-
return { result: TopicValidatorResult.Accept, obj: block };
|
|
1382
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
|
|
1191
1383
|
}
|
|
1192
1384
|
|
|
1193
1385
|
// REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
|
|
@@ -1213,6 +1405,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1213
1405
|
const isValid = await this.blockReceivedCallback(block, sender);
|
|
1214
1406
|
if (!isValid) {
|
|
1215
1407
|
this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
|
|
1408
|
+
// Release the protections this proposal created so its txs return to pending. Only entries still
|
|
1409
|
+
// keyed to this slot are cleared, so a tx referenced by a live proposal at another slot stays protected.
|
|
1410
|
+
await this.mempools.txPool.unprotectTxs(block.txHashes, slot);
|
|
1216
1411
|
}
|
|
1217
1412
|
}
|
|
1218
1413
|
|
|
@@ -1224,25 +1419,45 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1224
1419
|
const {
|
|
1225
1420
|
result,
|
|
1226
1421
|
obj: checkpoint,
|
|
1227
|
-
metadata: { isEquivocated, processBlock } = {},
|
|
1228
|
-
} = await this.validateReceivedMessage<
|
|
1422
|
+
metadata: { isEquivocated, processBlock, isOversized } = {},
|
|
1423
|
+
} = await this.validateReceivedMessage<
|
|
1424
|
+
CheckpointProposal,
|
|
1425
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1426
|
+
>(
|
|
1229
1427
|
() => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
|
|
1230
1428
|
msgId,
|
|
1231
1429
|
source,
|
|
1232
1430
|
TopicType.checkpoint_proposal,
|
|
1233
1431
|
);
|
|
1234
1432
|
|
|
1433
|
+
// An oversized checkpoint is re-broadcast as slashing evidence but never attested or processed.
|
|
1434
|
+
// Process checkpoint proposal if valid and neither equivocated nor oversized.
|
|
1435
|
+
const processCheckpointFn = () =>
|
|
1436
|
+
result === TopicValidatorResult.Accept && checkpoint && !isEquivocated && !isOversized
|
|
1437
|
+
? this.processValidCheckpointProposal(checkpoint.toCore(), source)
|
|
1438
|
+
: Promise.resolve();
|
|
1439
|
+
|
|
1235
1440
|
// If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
|
|
1236
1441
|
// TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1442
|
+
const processBlockFn = () =>
|
|
1443
|
+
processBlock && checkpoint && checkpoint.getBlockProposal()
|
|
1444
|
+
? this.processValidBlockProposal(checkpoint.getBlockProposal()!, source)
|
|
1445
|
+
: Promise.resolve();
|
|
1446
|
+
|
|
1447
|
+
// A node that skips checkpoint validation attests without re-executing the embedded last block, so run
|
|
1448
|
+
// the checkpoint callback first: this creates and broadcasts the attestation before the block is
|
|
1449
|
+
// processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
|
|
1450
|
+
// waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
|
|
1451
|
+
// window, after which peers reject it as stale.
|
|
1452
|
+
if (this.config.skipCheckpointProposalValidation) {
|
|
1453
|
+
await processCheckpointFn();
|
|
1454
|
+
await processBlockFn();
|
|
1242
1455
|
return;
|
|
1243
1456
|
}
|
|
1244
1457
|
|
|
1245
|
-
|
|
1458
|
+
// Process the block first, since it's required for the checkpoint proposal validation.
|
|
1459
|
+
await processBlockFn();
|
|
1460
|
+
await processCheckpointFn();
|
|
1246
1461
|
}
|
|
1247
1462
|
|
|
1248
1463
|
/**
|
|
@@ -1255,13 +1470,17 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1255
1470
|
protected async validateAndStoreCheckpointProposal(
|
|
1256
1471
|
peerId: PeerId,
|
|
1257
1472
|
checkpoint: CheckpointProposal,
|
|
1258
|
-
): Promise<
|
|
1473
|
+
): Promise<
|
|
1474
|
+
ReceivedMessageValidationResult<
|
|
1475
|
+
CheckpointProposal,
|
|
1476
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1477
|
+
>
|
|
1478
|
+
> {
|
|
1259
1479
|
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1260
1480
|
|
|
1261
1481
|
if (validationResult.result === 'reject') {
|
|
1262
1482
|
this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
|
|
1263
|
-
|
|
1264
|
-
return { result: TopicValidatorResult.Reject };
|
|
1483
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1265
1484
|
}
|
|
1266
1485
|
|
|
1267
1486
|
if (validationResult.result === 'ignore') {
|
|
@@ -1271,25 +1490,30 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1271
1490
|
// Extract and try to add the block proposal first if present
|
|
1272
1491
|
const blockProposal = checkpoint.getBlockProposal();
|
|
1273
1492
|
let processBlock = false;
|
|
1493
|
+
let isOversized = false;
|
|
1274
1494
|
if (blockProposal) {
|
|
1275
1495
|
this.logger.debug(`Validating block proposal from propagated checkpoint`, {
|
|
1276
1496
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1277
1497
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1278
1498
|
});
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
if (result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1499
|
+
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1500
|
+
const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
|
|
1501
|
+
isOversized = blockIsOversized ?? false;
|
|
1502
|
+
|
|
1503
|
+
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1285
1504
|
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1286
1505
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1287
1506
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1288
1507
|
isEquivocated,
|
|
1289
|
-
result,
|
|
1508
|
+
result: blockProposalResult.result,
|
|
1290
1509
|
});
|
|
1291
|
-
return {
|
|
1292
|
-
|
|
1510
|
+
return {
|
|
1511
|
+
result: TopicValidatorResult.Reject,
|
|
1512
|
+
severity:
|
|
1513
|
+
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1514
|
+
};
|
|
1515
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
|
|
1516
|
+
// An oversized terminal block is re-broadcast as slashing evidence but never processed.
|
|
1293
1517
|
processBlock = true;
|
|
1294
1518
|
}
|
|
1295
1519
|
}
|
|
@@ -1309,20 +1533,24 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1309
1533
|
return {
|
|
1310
1534
|
result: TopicValidatorResult.Ignore,
|
|
1311
1535
|
obj: checkpoint,
|
|
1312
|
-
metadata: { isEquivocated, processBlock },
|
|
1536
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1313
1537
|
};
|
|
1314
1538
|
}
|
|
1315
1539
|
|
|
1316
|
-
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
|
|
1540
|
+
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
|
|
1317
1541
|
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1318
1542
|
if (!added) {
|
|
1319
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
1320
1543
|
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
1321
1544
|
...checkpoint.toCheckpointInfo(),
|
|
1322
1545
|
count,
|
|
1323
1546
|
source: peerId.toString(),
|
|
1324
1547
|
});
|
|
1325
|
-
return {
|
|
1548
|
+
return {
|
|
1549
|
+
result: TopicValidatorResult.Reject,
|
|
1550
|
+
obj: checkpoint,
|
|
1551
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1552
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1553
|
+
};
|
|
1326
1554
|
}
|
|
1327
1555
|
|
|
1328
1556
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
@@ -1341,12 +1569,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1341
1569
|
return {
|
|
1342
1570
|
result: TopicValidatorResult.Accept,
|
|
1343
1571
|
obj: checkpoint,
|
|
1344
|
-
metadata: { isEquivocated, processBlock },
|
|
1572
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1345
1573
|
};
|
|
1346
1574
|
}
|
|
1347
1575
|
|
|
1348
1576
|
// Otherwise, we're good to go!
|
|
1349
|
-
return {
|
|
1577
|
+
return {
|
|
1578
|
+
result: TopicValidatorResult.Accept,
|
|
1579
|
+
obj: checkpoint,
|
|
1580
|
+
metadata: { processBlock, isEquivocated, isOversized },
|
|
1581
|
+
};
|
|
1350
1582
|
}
|
|
1351
1583
|
|
|
1352
1584
|
/**
|
|
@@ -1367,9 +1599,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1367
1599
|
source: sender.toString(),
|
|
1368
1600
|
});
|
|
1369
1601
|
|
|
1602
|
+
await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
|
|
1603
|
+
|
|
1370
1604
|
// Call the checkpoint received callback with the core version (without lastBlock)
|
|
1371
1605
|
// to validate and potentially generate attestations
|
|
1372
|
-
const attestations = await this.
|
|
1606
|
+
const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
|
|
1373
1607
|
if (attestations && attestations.length > 0) {
|
|
1374
1608
|
// If the callback returned attestations, add them to the pool and propagate them
|
|
1375
1609
|
await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
|
|
@@ -1392,82 +1626,87 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1392
1626
|
}
|
|
1393
1627
|
|
|
1394
1628
|
/**
|
|
1395
|
-
* Validate the requested block transactions
|
|
1629
|
+
* Validate the requested block transactions request-response consistency.
|
|
1630
|
+
* It does NOT validate the transactions themselves.
|
|
1396
1631
|
* @param request - The block transactions request.
|
|
1397
1632
|
* @param response - The block transactions response.
|
|
1398
1633
|
* @param peerId - The ID of the peer that made the request.
|
|
1399
|
-
* @returns True if the
|
|
1634
|
+
* @returns True if the request-response is consistent, false otherwise.
|
|
1400
1635
|
*/
|
|
1401
|
-
@trackSpan('Libp2pService.
|
|
1636
|
+
@trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
|
|
1402
1637
|
[Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
|
|
1403
1638
|
}))
|
|
1404
|
-
protected async
|
|
1639
|
+
protected async validateRequestedBlockTxsConsistency(
|
|
1405
1640
|
request: BlockTxsRequest,
|
|
1406
1641
|
response: BlockTxsResponse,
|
|
1407
1642
|
peerId: PeerId,
|
|
1408
1643
|
): Promise<boolean> {
|
|
1409
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1410
|
-
|
|
1411
1644
|
try {
|
|
1412
|
-
|
|
1645
|
+
// Check for duplicates txs (or hashes) in the response.
|
|
1646
|
+
const returnedHashes = response.txs.map(tx => tx.getTxHash().toString());
|
|
1647
|
+
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1648
|
+
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1413
1649
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1414
|
-
throw new ValidationError(
|
|
1415
|
-
|
|
1650
|
+
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// We get the block tx hashes from the proposal or the archiver.
|
|
1654
|
+
const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
|
|
1655
|
+
const blockTxHashes =
|
|
1656
|
+
proposal?.txHashes ??
|
|
1657
|
+
(await this.archiver.getBlock({ archive: request.archiveRoot }))?.body.txEffects.map(e => e.txHash);
|
|
1658
|
+
|
|
1659
|
+
// If we don't have the block, we can't verify membership/order of the returned txs.
|
|
1660
|
+
if (!blockTxHashes) {
|
|
1661
|
+
// This shouldn't happen, since we asked for the block (proposal).
|
|
1662
|
+
// Neither a local proposal nor an archived block: we cannot verify membership/order of the
|
|
1663
|
+
// returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
|
|
1664
|
+
this.logger.warn(
|
|
1665
|
+
`Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`,
|
|
1416
1666
|
);
|
|
1667
|
+
// NOTE: We mark the response as invalid, given the limitations of a true/false return value.
|
|
1668
|
+
return false;
|
|
1417
1669
|
}
|
|
1418
1670
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1671
|
+
// Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
|
|
1672
|
+
const uniqueRequestedHashes = new Set([
|
|
1673
|
+
// tx hashes requested by index.
|
|
1674
|
+
...request.txIndices.getTrueIndices().map(i => blockTxHashes[i].toString()),
|
|
1675
|
+
// explicitly requested tx hashes.
|
|
1676
|
+
...request.txHashes.map(h => h.toString()),
|
|
1677
|
+
]);
|
|
1678
|
+
if (!returnedHashes.every(h => uniqueRequestedHashes.has(h))) {
|
|
1679
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1421
1680
|
throw new ValidationError(
|
|
1422
|
-
|
|
1681
|
+
'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
|
|
1423
1682
|
);
|
|
1424
1683
|
}
|
|
1425
1684
|
|
|
1426
|
-
//
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
|
|
1432
|
-
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1433
|
-
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1434
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1435
|
-
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1685
|
+
// Tx indices information is optional, but if present, we need to verify a few things.
|
|
1686
|
+
if (!response.peerHasBlock()) {
|
|
1687
|
+
this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
|
|
1688
|
+
return true;
|
|
1436
1689
|
}
|
|
1437
|
-
|
|
1690
|
+
|
|
1691
|
+
// If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
|
|
1692
|
+
if (response.txIndices.getLength() !== blockTxHashes.length) {
|
|
1438
1693
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1439
1694
|
throw new ValidationError(
|
|
1440
|
-
`Received
|
|
1695
|
+
`Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
|
|
1441
1696
|
);
|
|
1442
1697
|
}
|
|
1443
1698
|
|
|
1444
|
-
//
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
);
|
|
1454
|
-
const allowedIndexSet = new Set(intersectIdx);
|
|
1455
|
-
const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
|
|
1456
|
-
const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
|
|
1457
|
-
const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
|
|
1458
|
-
if (!allAllowed || !strictlyIncreasing) {
|
|
1459
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1460
|
-
throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
|
|
1461
|
-
}
|
|
1462
|
-
} else {
|
|
1463
|
-
// No local proposal, cannot check the membership/order of the returned txs
|
|
1464
|
-
this.logger.warn(
|
|
1465
|
-
`Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
|
|
1466
|
-
);
|
|
1467
|
-
return false;
|
|
1699
|
+
// For every index we requested that the peer claims to have (per its bitvector), the
|
|
1700
|
+
// corresponding tx must be present in the response. A peer advertising availability but
|
|
1701
|
+
// withholding the tx is inconsistent.
|
|
1702
|
+
const returnedHashSet = new Set(returnedHashes);
|
|
1703
|
+
const expectedIndices = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
|
|
1704
|
+
const missingExpected = expectedIndices.some(i => !returnedHashSet.has(blockTxHashes[i].toString()));
|
|
1705
|
+
if (missingExpected) {
|
|
1706
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1707
|
+
throw new ValidationError('Peer advertised requested txs via indices but did not return them');
|
|
1468
1708
|
}
|
|
1469
1709
|
|
|
1470
|
-
await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
|
|
1471
1710
|
return true;
|
|
1472
1711
|
} catch (e: any) {
|
|
1473
1712
|
if (e instanceof ValidationError) {
|
|
@@ -1480,125 +1719,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1480
1719
|
}
|
|
1481
1720
|
}
|
|
1482
1721
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
*
|
|
1486
|
-
* The core component of this validator is that each tx hash MUST match the requested tx hash,
|
|
1487
|
-
* In order to perform this check, the tx proof must be verified.
|
|
1488
|
-
*
|
|
1489
|
-
* Note: This function is called from within `ReqResp.sendRequest` as part of the
|
|
1490
|
-
* ReqRespSubProtocol.TX subprotocol validation.
|
|
1491
|
-
*
|
|
1492
|
-
* @param requestedTxHash - The collection of the txs that was requested.
|
|
1493
|
-
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
1494
|
-
* @param peerId - The peer ID of the peer that sent the tx.
|
|
1495
|
-
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
1496
|
-
*/
|
|
1497
|
-
@trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
|
|
1498
|
-
[Attributes.TX_HASH]: requestedTxHash.toString(),
|
|
1499
|
-
}))
|
|
1500
|
-
private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
|
|
1501
|
-
const requested = new Set(requestedTxHash.map(h => h.toString()));
|
|
1502
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1503
|
-
|
|
1504
|
-
//TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
|
|
1505
|
-
// I think we should still extract the valid txs and return them, so that we can still use the response.
|
|
1506
|
-
try {
|
|
1507
|
-
await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
|
|
1508
|
-
return true;
|
|
1509
|
-
} catch (e: any) {
|
|
1510
|
-
if (e instanceof ValidationError) {
|
|
1511
|
-
this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
|
|
1512
|
-
} else {
|
|
1513
|
-
this.logger.error(`Error during validation of requested txs`, e);
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
return false;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
/**
|
|
1521
|
-
* Validates a BLOCK response.
|
|
1522
|
-
*
|
|
1523
|
-
* If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
|
|
1524
|
-
* Penalizes on block number mismatch or hash mismatch.
|
|
1525
|
-
*
|
|
1526
|
-
* @param requestedBlockNumber - The requested block number.
|
|
1527
|
-
* @param responseBlock - The block returned by the peer.
|
|
1528
|
-
* @param peerId - The peer that returned the block.
|
|
1529
|
-
* @returns True if the response is valid, false otherwise.
|
|
1530
|
-
*/
|
|
1531
|
-
@trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
|
|
1532
|
-
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
|
|
1533
|
-
}))
|
|
1534
|
-
protected async validateRequestedBlock(
|
|
1535
|
-
requestedBlockNumber: Fr,
|
|
1536
|
-
responseBlock: L2Block,
|
|
1537
|
-
peerId: PeerId,
|
|
1538
|
-
): Promise<boolean> {
|
|
1539
|
-
try {
|
|
1540
|
-
const reqNum = Number(requestedBlockNumber.toString());
|
|
1541
|
-
if (responseBlock.number !== reqNum) {
|
|
1542
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1543
|
-
return false;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
const local = await this.archiver.getBlock(BlockNumber(reqNum));
|
|
1547
|
-
if (!local) {
|
|
1548
|
-
// We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
|
|
1549
|
-
// TODO: Consider extending this validator to accept an expected hash or
|
|
1550
|
-
// performing quorum-based checks when using P2P syncing prior to L1 sync.
|
|
1551
|
-
this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
|
|
1552
|
-
return false;
|
|
1553
|
-
}
|
|
1554
|
-
const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
|
|
1555
|
-
if (!localHash.equals(respHash)) {
|
|
1556
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1557
|
-
return false;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
return true;
|
|
1561
|
-
} catch (e) {
|
|
1562
|
-
this.logger.warn(`Error validating requested block`, e);
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
protected async validateRequestedTx(
|
|
1568
|
-
tx: Tx,
|
|
1569
|
-
peerId: PeerId,
|
|
1570
|
-
txValidator: TxValidator,
|
|
1571
|
-
requested?: Set<`0x${string}`>,
|
|
1572
|
-
) {
|
|
1573
|
-
const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
|
|
1574
|
-
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1575
|
-
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1576
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
const { result } = await txValidator.validateTx(tx);
|
|
1580
|
-
if (result === 'invalid') {
|
|
1581
|
-
penalize(PeerErrorSeverity.LowToleranceError);
|
|
1582
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
protected createRequestedTxValidator(): TxValidator {
|
|
1587
|
-
return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
|
|
1588
|
-
l1ChainId: this.config.l1ChainId,
|
|
1589
|
-
rollupVersion: this.config.rollupVersion,
|
|
1590
|
-
});
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
1594
|
-
if (blockNumber === this.feesCache?.blockNumber) {
|
|
1595
|
-
return this.feesCache.gasFees;
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
const header = await this.archiver.getBlockHeader(blockNumber);
|
|
1599
|
-
const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
|
|
1600
|
-
this.feesCache = { blockNumber, gasFees };
|
|
1601
|
-
return gasFees;
|
|
1722
|
+
private getGasFees(): Promise<GasFees> {
|
|
1723
|
+
return this.blockMinFeesProvider.getCurrentMinFees();
|
|
1602
1724
|
}
|
|
1603
1725
|
|
|
1604
1726
|
/**
|
|
@@ -1612,8 +1734,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1612
1734
|
l1ChainId: this.config.l1ChainId,
|
|
1613
1735
|
rollupVersion: this.config.rollupVersion,
|
|
1614
1736
|
proofVerifier: this.proofVerifier,
|
|
1737
|
+
txValidationCache: this.txValidationCache,
|
|
1615
1738
|
},
|
|
1616
1739
|
peerScoring: this.peerManager,
|
|
1740
|
+
validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
|
|
1617
1741
|
};
|
|
1618
1742
|
}
|
|
1619
1743
|
|
|
@@ -1622,6 +1746,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1622
1746
|
this.proofVerifier,
|
|
1623
1747
|
{ l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
|
|
1624
1748
|
this.logger.getBindings(),
|
|
1749
|
+
this.txValidationCache,
|
|
1625
1750
|
);
|
|
1626
1751
|
|
|
1627
1752
|
const results = await Promise.all(
|
|
@@ -1640,13 +1765,14 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1640
1765
|
currentBlockNumber: BlockNumber,
|
|
1641
1766
|
nextSlotTimestamp: UInt64,
|
|
1642
1767
|
): Promise<Record<string, TransactionValidator>> {
|
|
1643
|
-
const gasFees = await this.getGasFees(
|
|
1768
|
+
const gasFees = await this.getGasFees();
|
|
1644
1769
|
const allowedInSetup = [
|
|
1645
1770
|
...(await getDefaultAllowedSetupFunctions()),
|
|
1646
1771
|
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1647
1772
|
];
|
|
1648
1773
|
const blockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1649
1774
|
const l1Constants = await this.archiver.getL1Constants();
|
|
1775
|
+
const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
|
|
1650
1776
|
|
|
1651
1777
|
return createFirstStageTxValidationsForGossipedTransactions(
|
|
1652
1778
|
nextSlotTimestamp,
|
|
@@ -1661,9 +1787,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1661
1787
|
allowedInSetup,
|
|
1662
1788
|
this.logger.getBindings(),
|
|
1663
1789
|
{
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1790
|
+
maxTxL2Gas: networkTxGasLimits.l2Gas,
|
|
1791
|
+
maxTxDAGas: networkTxGasLimits.daGas,
|
|
1667
1792
|
},
|
|
1668
1793
|
);
|
|
1669
1794
|
}
|