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