@aztec/p2p 0.0.1-commit.c2eed6949 → 0.0.1-commit.c52d6e7
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 +7 -6
- package/dest/bootstrap/bootstrap.d.ts +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +22 -6
- package/dest/client/factory.d.ts +4 -3
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +36 -26
- 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 +105 -46
- package/dest/config.d.ts +143 -107
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +72 -26
- 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 +98 -56
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +269 -196
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +218 -59
- package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +6 -4
- package/dest/mem_pools/index.d.ts +1 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.d.ts +9 -4
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +28 -9
- 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 +3 -3
- 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 +33 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +13 -6
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +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 +31 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +9 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +19 -8
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +17 -4
- 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 +208 -118
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +85 -15
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +8 -15
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
- package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
- package/dest/msg_validators/tx_validator/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 +1 -0
- package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +29 -16
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
- package/dest/msg_validators/tx_validator/index.d.ts +3 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +2 -0
- package/dest/msg_validators/tx_validator/phases_validator.js +2 -2
- 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 -25
- 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 +19 -6
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +25 -15
- package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +4 -14
- package/dest/services/libp2p/instrumentation.d.ts +3 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +14 -0
- package/dest/services/libp2p/libp2p_service.d.ts +41 -40
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +323 -275
- 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 +77 -23
- package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +102 -13
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +35 -44
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
- package/dest/services/reqresp/config.d.ts +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 +59 -209
- package/dest/services/service.d.ts +27 -16
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +2 -23
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +2 -55
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
- package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +39 -29
- package/dest/services/tx_collection/index.d.ts +2 -3
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +0 -1
- package/dest/services/tx_collection/instrumentation.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
- package/dest/services/tx_collection/instrumentation.js +0 -2
- package/dest/services/tx_collection/tx_collection.d.ts +35 -51
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +275 -119
- package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +2 -2
- package/dest/services/tx_collection/tx_source.d.ts +6 -5
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +13 -8
- package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_file_store/tx_file_store.js +4 -14
- package/dest/services/tx_provider.d.ts +3 -1
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +3 -0
- package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +5 -2
- package/dest/test-helpers/mock-pubsub.d.ts +21 -12
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +39 -47
- 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 +30 -14
- 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 +101 -39
- package/dest/testbench/worker_client_manager.d.ts +10 -6
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +51 -9
- package/dest/util.d.ts +8 -8
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +38 -10
- 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 +54 -25
- package/src/client/interface.ts +48 -18
- package/src/client/p2p_client.ts +125 -79
- package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
- package/src/config.ts +146 -34
- 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 +324 -239
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +238 -63
- package/src/mem_pools/attestation_pool/mocks.ts +13 -8
- package/src/mem_pools/index.ts +0 -3
- package/src/mem_pools/instrumentation.ts +18 -12
- 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 +2 -4
- 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 +31 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +20 -8
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +35 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +22 -8
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +233 -123
- package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
- package/src/msg_validators/proposal_validator/README.md +1 -1
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
- package/src/msg_validators/proposal_validator/proposal_validator.ts +94 -12
- package/src/msg_validators/tx_validator/README.md +13 -3
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
- package/src/msg_validators/tx_validator/data_validator.ts +2 -0
- package/src/msg_validators/tx_validator/factory.ts +37 -20
- package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
- package/src/msg_validators/tx_validator/index.ts +2 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
- 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 -16
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +20 -39
- package/src/services/encoding.ts +28 -20
- 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 +371 -301
- package/src/services/peer-manager/peer_manager.ts +84 -22
- package/src/services/peer-manager/peer_scoring.ts +102 -8
- package/src/services/reqresp/README.md +2 -37
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +39 -52
- package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
- package/src/services/reqresp/config.ts +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 +69 -252
- package/src/services/service.ts +32 -32
- package/src/services/tx_collection/config.ts +3 -80
- package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
- package/src/services/tx_collection/file_store_tx_source.ts +43 -31
- package/src/services/tx_collection/index.ts +1 -6
- package/src/services/tx_collection/instrumentation.ts +1 -7
- package/src/services/tx_collection/tx_collection.ts +330 -173
- package/src/services/tx_collection/tx_collection_sink.ts +2 -2
- package/src/services/tx_collection/tx_source.ts +10 -8
- package/src/services/tx_file_store/tx_file_store.ts +5 -17
- package/src/services/tx_provider.ts +5 -0
- package/src/test-helpers/make-test-p2p-clients.ts +3 -0
- package/src/test-helpers/mock-pubsub.ts +41 -64
- package/src/test-helpers/reqresp-nodes.ts +12 -22
- package/src/test-helpers/test_tx_provider.ts +5 -0
- package/src/test-helpers/testbench-utils.ts +36 -26
- package/src/testbench/p2p_client_testbench_worker.ts +108 -53
- package/src/testbench/worker_client_manager.ts +59 -20
- package/src/util.ts +33 -14
- 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 -123
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
- package/dest/mem_pools/tx_pool/index.d.ts +0 -3
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.js +0 -2
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.js +0 -15
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
- package/dest/msg_validators/clock_tolerance.d.ts +0 -21
- package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
- package/dest/msg_validators/clock_tolerance.js +0 -38
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
- package/dest/services/reqresp/protocols/block.d.ts +0 -9
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.js +0 -32
- package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/fast_tx_collection.js +0 -311
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
- package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/slow_tx_collection.js +0 -211
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -343
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
- package/src/mem_pools/tx_pool/README.md +0 -270
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
- package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
- package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
- package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
- package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
- package/src/mem_pools/tx_pool/index.ts +0 -2
- package/src/mem_pools/tx_pool/priority.ts +0 -20
- package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
- package/src/msg_validators/clock_tolerance.ts +0 -52
- package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
- package/src/services/reqresp/protocols/block.ts +0 -37
- package/src/services/tx_collection/fast_tx_collection.ts +0 -361
- package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
- package/src/services/tx_collection/slow_tx_collection.ts +0 -266
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
2
|
import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { maxBy } from '@aztec/foundation/collection';
|
|
4
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
|
|
5
4
|
import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
6
5
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
7
6
|
import { Timer } from '@aztec/foundation/timer';
|
|
8
7
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
9
8
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
10
|
-
import type { EthAddress,
|
|
9
|
+
import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
|
|
10
|
+
import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/stdlib/config';
|
|
11
11
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
12
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
12
|
+
import { type BlockMinFeesProvider, GasFees, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
|
|
13
13
|
import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
14
14
|
import {
|
|
15
15
|
BlockProposal,
|
|
@@ -25,9 +25,11 @@ import {
|
|
|
25
25
|
getTopicsForConfig,
|
|
26
26
|
metricsTopicStrToLabels,
|
|
27
27
|
} from '@aztec/stdlib/p2p';
|
|
28
|
+
import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
|
|
28
29
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
29
|
-
import { Tx, type
|
|
30
|
+
import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
|
|
30
31
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
32
|
+
import { InvalidBlockProposalTxsError } from '@aztec/stdlib/validators';
|
|
31
33
|
import { compressComponentVersions } from '@aztec/stdlib/versioning';
|
|
32
34
|
import {
|
|
33
35
|
Attributes,
|
|
@@ -59,6 +61,7 @@ import { ENR } from '@nethermindeth/enr';
|
|
|
59
61
|
import { createLibp2p } from 'libp2p';
|
|
60
62
|
|
|
61
63
|
import type { P2PConfig } from '../../config.js';
|
|
64
|
+
import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
|
|
62
65
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
63
66
|
import {
|
|
64
67
|
BlockProposalValidator,
|
|
@@ -74,38 +77,34 @@ import {
|
|
|
74
77
|
createFirstStageTxValidationsForGossipedTransactions,
|
|
75
78
|
createSecondStageTxValidationsForGossipedTransactions,
|
|
76
79
|
createTxValidatorForBlockProposalReceivedTxs,
|
|
77
|
-
createTxValidatorForReqResponseReceivedTxs,
|
|
78
80
|
} from '../../msg_validators/tx_validator/factory.js';
|
|
81
|
+
import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
|
|
79
82
|
import { GossipSubEvent } from '../../types/index.js';
|
|
80
83
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
81
84
|
import { getVersions } from '../../versioning.js';
|
|
82
85
|
import { AztecDatastore } from '../data_store.js';
|
|
83
86
|
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
84
|
-
import { SnappyTransform,
|
|
87
|
+
import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
85
88
|
import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
86
89
|
import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
|
|
87
90
|
import type { PeerManagerInterface } from '../peer-manager/interface.js';
|
|
88
91
|
import { PeerManager } from '../peer-manager/peer_manager.js';
|
|
89
92
|
import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
90
93
|
import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
|
|
91
|
-
import type
|
|
94
|
+
import { type P2PReqRespConfig, YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
|
|
92
95
|
import {
|
|
93
96
|
AuthRequest,
|
|
94
97
|
BlockTxsRequest,
|
|
95
98
|
BlockTxsResponse,
|
|
96
|
-
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
97
99
|
type ReqRespInterface,
|
|
98
100
|
type ReqRespResponse,
|
|
99
101
|
ReqRespSubProtocol,
|
|
100
102
|
type ReqRespSubProtocolHandler,
|
|
101
103
|
type ReqRespSubProtocolHandlers,
|
|
102
|
-
type ReqRespSubProtocolValidators,
|
|
103
104
|
StatusMessage,
|
|
104
|
-
type SubProtocolMap,
|
|
105
105
|
ValidationError,
|
|
106
106
|
pingHandler,
|
|
107
107
|
reqGoodbyeHandler,
|
|
108
|
-
reqRespBlockHandler,
|
|
109
108
|
reqRespBlockTxsHandler,
|
|
110
109
|
reqRespStatusHandler,
|
|
111
110
|
reqRespTxHandler,
|
|
@@ -113,13 +112,33 @@ import {
|
|
|
113
112
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
114
113
|
import type {
|
|
115
114
|
P2PBlockReceivedCallback,
|
|
115
|
+
P2PCheckpointAttestationCallback,
|
|
116
116
|
P2PCheckpointReceivedCallback,
|
|
117
117
|
P2PDuplicateAttestationCallback,
|
|
118
|
+
P2POversizedProposalCallback,
|
|
118
119
|
P2PService,
|
|
119
120
|
PeerDiscoveryService,
|
|
120
121
|
} from '../service.js';
|
|
121
122
|
import { P2PInstrumentation } from './instrumentation.js';
|
|
122
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
|
+
|
|
123
142
|
interface ValidationResult {
|
|
124
143
|
name: string;
|
|
125
144
|
isValid: TxValidationResult;
|
|
@@ -148,8 +167,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
148
167
|
private protocolVersion = '';
|
|
149
168
|
private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
|
|
150
169
|
|
|
151
|
-
private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
|
|
152
|
-
|
|
153
170
|
/** Callback invoked when a duplicate proposal is detected (triggers slashing). */
|
|
154
171
|
private duplicateProposalCallback?: (info: {
|
|
155
172
|
slot: SlotNumber;
|
|
@@ -157,9 +174,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
157
174
|
type: 'checkpoint' | 'block';
|
|
158
175
|
}) => void;
|
|
159
176
|
|
|
177
|
+
/** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
|
|
178
|
+
private oversizedProposalCallback?: P2POversizedProposalCallback;
|
|
179
|
+
|
|
160
180
|
/** Callback invoked when a duplicate attestation is detected (triggers slashing). */
|
|
161
181
|
private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
|
|
162
182
|
|
|
183
|
+
/** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
|
|
184
|
+
private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
|
|
185
|
+
|
|
163
186
|
/**
|
|
164
187
|
* Callback for when a block is received from a peer.
|
|
165
188
|
* @param block - The block received from the peer.
|
|
@@ -172,12 +195,17 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
172
195
|
* @param checkpoint - The checkpoint proposal received from the peer.
|
|
173
196
|
* @returns The attestations for the checkpoint, if any.
|
|
174
197
|
*/
|
|
175
|
-
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;
|
|
176
205
|
|
|
177
206
|
private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
|
|
178
|
-
private ipChangedHandler?: (ip: string) => void;
|
|
179
207
|
|
|
180
|
-
|
|
208
|
+
private ipChangedHandler?: (ip: string) => void;
|
|
181
209
|
private discoveredP2pIp?: string;
|
|
182
210
|
|
|
183
211
|
private instrumentation: P2PInstrumentation;
|
|
@@ -197,8 +225,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
197
225
|
private epochCache: EpochCacheInterface,
|
|
198
226
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
199
227
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
228
|
+
private blockMinFeesProvider: BlockMinFeesProvider,
|
|
200
229
|
telemetry: TelemetryClient,
|
|
201
230
|
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
231
|
+
private txValidationCache?: TxValidationCache,
|
|
202
232
|
) {
|
|
203
233
|
super(telemetry, 'LibP2PService');
|
|
204
234
|
this.telemetry = telemetry;
|
|
@@ -228,15 +258,39 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
228
258
|
this.protocolVersion,
|
|
229
259
|
);
|
|
230
260
|
|
|
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());
|
|
231
264
|
const proposalValidatorOpts = {
|
|
232
265
|
txsPermitted: !config.disableTransactions,
|
|
233
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,
|
|
234
284
|
};
|
|
235
|
-
this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
|
|
236
|
-
this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
|
|
237
285
|
this.checkpointAttestationValidator = config.fishermanMode
|
|
238
|
-
? new FishermanAttestationValidator(
|
|
239
|
-
|
|
286
|
+
? new FishermanAttestationValidator(
|
|
287
|
+
epochCache,
|
|
288
|
+
consensusTimetable,
|
|
289
|
+
mempools.attestationPool,
|
|
290
|
+
telemetry,
|
|
291
|
+
attestationValidatorOpts,
|
|
292
|
+
)
|
|
293
|
+
: new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
|
|
240
294
|
|
|
241
295
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
242
296
|
|
|
@@ -248,18 +302,22 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
248
302
|
return true;
|
|
249
303
|
};
|
|
250
304
|
|
|
251
|
-
this.
|
|
252
|
-
|
|
305
|
+
this.allNodesCheckpointReceivedCallback = (
|
|
306
|
+
_checkpoint: CheckpointProposalCore,
|
|
307
|
+
): Promise<CheckpointAttestation[] | undefined> => {
|
|
308
|
+
throw new CheckpointProposalReceivedCallbackNotRegisteredError();
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
this.validatorCheckpointReceivedCallback = (
|
|
312
|
+
_checkpoint: CheckpointProposalCore,
|
|
253
313
|
): Promise<CheckpointAttestation[] | undefined> => {
|
|
254
|
-
this.logger.debug(
|
|
255
|
-
`Handler not yet registered: Checkpoint received callback not set. Received checkpoint for slot ${checkpoint.slotNumber} from peer.`,
|
|
256
|
-
);
|
|
257
314
|
return Promise.resolve(undefined);
|
|
258
315
|
};
|
|
259
316
|
}
|
|
260
317
|
|
|
261
|
-
public updateConfig(config: Partial<P2PReqRespConfig
|
|
318
|
+
public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
|
|
262
319
|
this.reqresp.updateConfig(config);
|
|
320
|
+
this.config = merge(this.config, config);
|
|
263
321
|
}
|
|
264
322
|
|
|
265
323
|
/**
|
|
@@ -278,9 +336,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
278
336
|
proofVerifier: ClientProtocolCircuitVerifier;
|
|
279
337
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
280
338
|
peerStore: AztecAsyncKVStore;
|
|
339
|
+
blockMinFeesProvider: BlockMinFeesProvider;
|
|
281
340
|
telemetry: TelemetryClient;
|
|
282
341
|
logger: Logger;
|
|
283
342
|
packageVersion: string;
|
|
343
|
+
txValidationCache?: TxValidationCache;
|
|
284
344
|
},
|
|
285
345
|
) {
|
|
286
346
|
const {
|
|
@@ -290,9 +350,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
290
350
|
mempools,
|
|
291
351
|
proofVerifier,
|
|
292
352
|
peerStore,
|
|
353
|
+
blockMinFeesProvider,
|
|
293
354
|
telemetry,
|
|
294
355
|
logger,
|
|
295
356
|
packageVersion,
|
|
357
|
+
txValidationCache,
|
|
296
358
|
} = deps;
|
|
297
359
|
const { p2pPort, maxPeerCount, listenAddress } = config;
|
|
298
360
|
const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
|
|
@@ -307,6 +369,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
307
369
|
packageVersion,
|
|
308
370
|
telemetry,
|
|
309
371
|
createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
|
|
372
|
+
peerStore,
|
|
310
373
|
);
|
|
311
374
|
|
|
312
375
|
// Seed libp2p's bootstrap discovery with private and trusted peers
|
|
@@ -321,34 +384,49 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
321
384
|
const protocolVersion = compressComponentVersions(versions);
|
|
322
385
|
|
|
323
386
|
const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
324
|
-
const directPeers = (
|
|
387
|
+
const directPeers = compactArray(
|
|
325
388
|
await Promise.all(
|
|
326
389
|
preferredPeersEnrs.map(async enr => {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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;
|
|
331
404
|
}
|
|
332
|
-
return {
|
|
333
|
-
id: peerId,
|
|
334
|
-
addrs: [address],
|
|
335
|
-
};
|
|
336
405
|
}),
|
|
337
|
-
)
|
|
338
|
-
)
|
|
406
|
+
),
|
|
407
|
+
);
|
|
339
408
|
|
|
340
409
|
const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
|
|
341
410
|
|
|
342
|
-
// 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.
|
|
343
414
|
const l1Constants = epochCache.getL1Constants();
|
|
344
415
|
const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
|
|
345
416
|
slotDurationMs: l1Constants.slotDuration * 1000,
|
|
346
417
|
heartbeatIntervalMs: config.gossipsubInterval,
|
|
347
418
|
targetCommitteeSize: l1Constants.targetCommitteeSize,
|
|
348
|
-
|
|
419
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
|
|
349
420
|
expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
|
|
350
421
|
});
|
|
351
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
|
+
|
|
352
430
|
const node = await createLibp2p({
|
|
353
431
|
start: false,
|
|
354
432
|
peerId,
|
|
@@ -380,7 +458,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
380
458
|
],
|
|
381
459
|
datastore,
|
|
382
460
|
peerDiscovery,
|
|
383
|
-
|
|
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()],
|
|
384
463
|
connectionEncryption: [noise()],
|
|
385
464
|
connectionManager: {
|
|
386
465
|
minConnections: 0, // Disable libp2p peer dialing, we do it manually
|
|
@@ -394,7 +473,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
394
473
|
},
|
|
395
474
|
connectionGater: {
|
|
396
475
|
denyInboundConnection: (maConn: MultiaddrConnection) => {
|
|
397
|
-
const allowed = peerManager.
|
|
476
|
+
const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
|
|
398
477
|
if (allowed) {
|
|
399
478
|
return false;
|
|
400
479
|
}
|
|
@@ -405,7 +484,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
405
484
|
denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
|
|
406
485
|
//NOTE: it is not necessary to check address here because this was already done by
|
|
407
486
|
// denyInboundConnection
|
|
408
|
-
const allowed = peerManager.
|
|
487
|
+
const allowed = peerManager.isPeerAllowedToConnect(peerId);
|
|
409
488
|
if (allowed) {
|
|
410
489
|
return false;
|
|
411
490
|
}
|
|
@@ -433,9 +512,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
433
512
|
mcacheLength: config.gossipsubMcacheLength,
|
|
434
513
|
mcacheGossip: config.gossipsubMcacheGossip,
|
|
435
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.
|
|
436
520
|
msgIdFn: getMsgIdFn,
|
|
437
521
|
msgIdToStrFn: msgIdToStrFn,
|
|
438
|
-
fastMsgIdFn: fastMsgIdFn,
|
|
439
522
|
dataTransform: new SnappyTransform(),
|
|
440
523
|
metricsRegister: otelMetricsAdapter,
|
|
441
524
|
metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
|
|
@@ -471,6 +554,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
471
554
|
epochCache,
|
|
472
555
|
);
|
|
473
556
|
|
|
557
|
+
// Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
|
|
558
|
+
reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
|
|
559
|
+
|
|
474
560
|
// Configure application-specific scoring for gossipsub.
|
|
475
561
|
// The weight scales app score to align with gossipsub thresholds:
|
|
476
562
|
// - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
|
|
@@ -491,8 +577,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
491
577
|
epochCache,
|
|
492
578
|
proofVerifier,
|
|
493
579
|
worldStateSynchronizer,
|
|
580
|
+
blockMinFeesProvider,
|
|
494
581
|
telemetry,
|
|
495
582
|
logger,
|
|
583
|
+
txValidationCache,
|
|
496
584
|
);
|
|
497
585
|
}
|
|
498
586
|
|
|
@@ -506,24 +594,21 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
506
594
|
throw new Error('P2P service already started');
|
|
507
595
|
}
|
|
508
596
|
|
|
509
|
-
// Get listen & announce addresses for logging
|
|
510
597
|
const { p2pIp, p2pPort } = this.config;
|
|
511
598
|
if (!p2pIp && !this.config.queryForIp) {
|
|
512
|
-
throw new Error('Announce address not provided.');
|
|
599
|
+
throw new Error('Announce address not provided and queryForIp is not enabled.');
|
|
513
600
|
}
|
|
514
601
|
const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
|
|
515
602
|
|
|
516
603
|
// Create request response protocol handlers
|
|
517
604
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
518
605
|
const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
|
|
519
|
-
const blockHandler = reqRespBlockHandler(this.archiver);
|
|
520
606
|
const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
|
|
521
607
|
|
|
522
608
|
const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
|
|
523
609
|
[ReqRespSubProtocol.PING]: pingHandler,
|
|
524
610
|
[ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
|
|
525
611
|
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
|
|
526
|
-
[ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
|
|
527
612
|
};
|
|
528
613
|
|
|
529
614
|
if (!this.config.disableTransactions) {
|
|
@@ -539,17 +624,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
539
624
|
requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
|
|
540
625
|
}
|
|
541
626
|
|
|
542
|
-
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
543
|
-
const reqrespSubProtocolValidators = {
|
|
544
|
-
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
545
|
-
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
546
|
-
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
547
|
-
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
|
|
548
|
-
};
|
|
549
|
-
|
|
550
627
|
await this.peerManager.initializePeers();
|
|
551
628
|
|
|
552
|
-
await this.reqresp.start(requestResponseHandlers
|
|
629
|
+
await this.reqresp.start(requestResponseHandlers);
|
|
553
630
|
|
|
554
631
|
await this.node.start();
|
|
555
632
|
|
|
@@ -566,26 +643,33 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
566
643
|
await this.peerDiscoveryService.start();
|
|
567
644
|
}
|
|
568
645
|
|
|
569
|
-
//
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (this.config.queryForIp && !p2pIp) {
|
|
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');
|
|
574
650
|
this.ipChangedHandler = (ip: string) => {
|
|
575
651
|
const addressManager = this.node.services.components.addressManager;
|
|
576
652
|
const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
|
|
653
|
+
const previousIp = this.discoveredP2pIp;
|
|
577
654
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const oldAddr = multiaddr(convertToMultiaddr(this.discoveredP2pIp, this.config.p2pPort, 'tcp'));
|
|
655
|
+
if (previousIp) {
|
|
656
|
+
const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
|
|
581
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
|
+
});
|
|
582
668
|
}
|
|
583
669
|
|
|
584
670
|
addressManager.addObservedAddr(newAddr);
|
|
585
671
|
addressManager.confirmObservedAddr(newAddr);
|
|
586
|
-
// Store discovered IP
|
|
587
672
|
this.discoveredP2pIp = ip;
|
|
588
|
-
this.logger.info('Public IP discovered via discv5', { ip });
|
|
589
673
|
};
|
|
590
674
|
this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
|
|
591
675
|
}
|
|
@@ -602,7 +686,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
602
686
|
this.logger.info(`Started P2P service`, {
|
|
603
687
|
listen: this.config.listenAddress,
|
|
604
688
|
port: this.config.p2pPort,
|
|
605
|
-
announce: announceTcpMultiaddr
|
|
689
|
+
announce: announceTcpMultiaddr,
|
|
606
690
|
peerId: this.node.peerId.toString(),
|
|
607
691
|
});
|
|
608
692
|
}
|
|
@@ -615,9 +699,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
615
699
|
// Remove gossip sub listener
|
|
616
700
|
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
617
701
|
|
|
618
|
-
// Remove ip:changed listener if registered
|
|
619
702
|
if (this.ipChangedHandler) {
|
|
620
|
-
this.peerDiscoveryService.
|
|
703
|
+
this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
|
|
621
704
|
this.ipChangedHandler = undefined;
|
|
622
705
|
}
|
|
623
706
|
|
|
@@ -635,12 +718,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
635
718
|
this.logger.info('LibP2P service stopped');
|
|
636
719
|
}
|
|
637
720
|
|
|
638
|
-
addReqRespSubProtocol(
|
|
639
|
-
subProtocol
|
|
640
|
-
handler: ReqRespSubProtocolHandler,
|
|
641
|
-
validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
|
|
642
|
-
): Promise<void> {
|
|
643
|
-
return this.reqresp.addSubProtocol(subProtocol, handler, validator);
|
|
721
|
+
addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
|
|
722
|
+
return this.reqresp.addSubProtocol(subProtocol, handler);
|
|
644
723
|
}
|
|
645
724
|
|
|
646
725
|
public registerThisValidatorAddresses(address: EthAddress[]): void {
|
|
@@ -668,20 +747,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
668
747
|
setImmediate(() => void safeJob());
|
|
669
748
|
}
|
|
670
749
|
|
|
671
|
-
/**
|
|
672
|
-
* Send a batch of requests to peers, and return the responses
|
|
673
|
-
* @param protocol - The request response protocol to use
|
|
674
|
-
* @param requests - The requests to send to the peers
|
|
675
|
-
* @returns The responses to the requests
|
|
676
|
-
*/
|
|
677
|
-
sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
|
|
678
|
-
protocol: SubProtocol,
|
|
679
|
-
requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
|
|
680
|
-
pinnedPeerId: PeerId | undefined,
|
|
681
|
-
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
|
|
682
|
-
return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
750
|
public sendRequestToPeer(
|
|
686
751
|
peerId: PeerId,
|
|
687
752
|
subProtocol: ReqRespSubProtocol,
|
|
@@ -703,8 +768,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
703
768
|
this.blockReceivedCallback = callback;
|
|
704
769
|
}
|
|
705
770
|
|
|
706
|
-
public
|
|
707
|
-
this.
|
|
771
|
+
public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
|
|
772
|
+
this.validatorCheckpointReceivedCallback = callback;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
|
|
776
|
+
this.allNodesCheckpointReceivedCallback = callback;
|
|
708
777
|
}
|
|
709
778
|
|
|
710
779
|
/**
|
|
@@ -717,6 +786,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
717
786
|
this.duplicateProposalCallback = callback;
|
|
718
787
|
}
|
|
719
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
|
+
|
|
720
796
|
/**
|
|
721
797
|
* Registers a callback to be invoked when a duplicate attestation is detected.
|
|
722
798
|
* A validator signing attestations for different proposals at the same slot.
|
|
@@ -726,6 +802,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
726
802
|
this.duplicateAttestationCallback = callback;
|
|
727
803
|
}
|
|
728
804
|
|
|
805
|
+
public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
|
|
806
|
+
this.checkpointAttestationCallback = callback;
|
|
807
|
+
}
|
|
808
|
+
|
|
729
809
|
/**
|
|
730
810
|
* Subscribes to a topic.
|
|
731
811
|
* @param topic - The topic to subscribe to.
|
|
@@ -854,6 +934,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
854
934
|
|
|
855
935
|
// Process the message, optionally within a linked span for trace propagation
|
|
856
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
|
+
}
|
|
857
946
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
858
947
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
859
948
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
@@ -931,6 +1020,17 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
931
1020
|
this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
|
|
932
1021
|
}
|
|
933
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
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
934
1034
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
935
1035
|
this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
|
|
936
1036
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
@@ -1166,6 +1266,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1166
1266
|
}
|
|
1167
1267
|
|
|
1168
1268
|
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1269
|
+
this.checkpointAttestationCallback?.(attestation);
|
|
1169
1270
|
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
1170
1271
|
}
|
|
1171
1272
|
|
|
@@ -1173,16 +1274,17 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1173
1274
|
const {
|
|
1174
1275
|
result,
|
|
1175
1276
|
obj: block,
|
|
1176
|
-
metadata: { isEquivocated } = {},
|
|
1177
|
-
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
|
|
1277
|
+
metadata: { isEquivocated, isOversized } = {},
|
|
1278
|
+
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
|
|
1178
1279
|
() => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
|
|
1179
1280
|
msgId,
|
|
1180
1281
|
source,
|
|
1181
1282
|
TopicType.block_proposal,
|
|
1182
1283
|
);
|
|
1183
1284
|
|
|
1184
|
-
// If not accepted or
|
|
1185
|
-
|
|
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) {
|
|
1186
1288
|
return;
|
|
1187
1289
|
}
|
|
1188
1290
|
|
|
@@ -1197,7 +1299,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1197
1299
|
protected async validateAndStoreBlockProposal(
|
|
1198
1300
|
peerId: PeerId,
|
|
1199
1301
|
block: BlockProposal,
|
|
1200
|
-
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
|
|
1302
|
+
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
|
|
1201
1303
|
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1202
1304
|
|
|
1203
1305
|
if (validationResult.result === 'reject') {
|
|
@@ -1212,6 +1314,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1212
1314
|
// Try to add the proposal: this handles existence check, cap check, and adding in one call
|
|
1213
1315
|
const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
|
|
1214
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;
|
|
1215
1323
|
|
|
1216
1324
|
// Duplicate proposal received, no need to re-broadcast
|
|
1217
1325
|
if (alreadyExists) {
|
|
@@ -1221,7 +1329,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1221
1329
|
proposer: block.getSender()?.toString(),
|
|
1222
1330
|
source: peerId.toString(),
|
|
1223
1331
|
});
|
|
1224
|
-
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
|
|
1332
|
+
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1225
1333
|
}
|
|
1226
1334
|
|
|
1227
1335
|
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
@@ -1235,11 +1343,27 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1235
1343
|
});
|
|
1236
1344
|
return {
|
|
1237
1345
|
result: TopicValidatorResult.Reject,
|
|
1238
|
-
metadata: { isEquivocated },
|
|
1346
|
+
metadata: { isEquivocated, isOversized },
|
|
1239
1347
|
severity: PeerErrorSeverity.HighToleranceError,
|
|
1240
1348
|
};
|
|
1241
1349
|
}
|
|
1242
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
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1243
1367
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1244
1368
|
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1245
1369
|
if (isEquivocated) {
|
|
@@ -1253,11 +1377,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1253
1377
|
if (proposer && count === 2) {
|
|
1254
1378
|
this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
|
|
1255
1379
|
}
|
|
1256
|
-
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
|
|
1380
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1257
1381
|
}
|
|
1258
1382
|
|
|
1259
1383
|
// Otherwise, we're good to go!
|
|
1260
|
-
return { result: TopicValidatorResult.Accept, obj: block };
|
|
1384
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
|
|
1261
1385
|
}
|
|
1262
1386
|
|
|
1263
1387
|
// REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
|
|
@@ -1278,11 +1402,29 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1278
1402
|
// Mark the txs in this proposal as protected
|
|
1279
1403
|
await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
|
|
1280
1404
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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;
|
|
1286
1428
|
}
|
|
1287
1429
|
}
|
|
1288
1430
|
|
|
@@ -1294,25 +1436,45 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1294
1436
|
const {
|
|
1295
1437
|
result,
|
|
1296
1438
|
obj: checkpoint,
|
|
1297
|
-
metadata: { isEquivocated, processBlock } = {},
|
|
1298
|
-
} = await this.validateReceivedMessage<
|
|
1439
|
+
metadata: { isEquivocated, processBlock, isOversized } = {},
|
|
1440
|
+
} = await this.validateReceivedMessage<
|
|
1441
|
+
CheckpointProposal,
|
|
1442
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1443
|
+
>(
|
|
1299
1444
|
() => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
|
|
1300
1445
|
msgId,
|
|
1301
1446
|
source,
|
|
1302
1447
|
TopicType.checkpoint_proposal,
|
|
1303
1448
|
);
|
|
1304
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
|
+
|
|
1305
1457
|
// If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
|
|
1306
1458
|
// TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
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();
|
|
1312
1472
|
return;
|
|
1313
1473
|
}
|
|
1314
1474
|
|
|
1315
|
-
|
|
1475
|
+
// Process the block first, since it's required for the checkpoint proposal validation.
|
|
1476
|
+
await processBlockFn();
|
|
1477
|
+
await processCheckpointFn();
|
|
1316
1478
|
}
|
|
1317
1479
|
|
|
1318
1480
|
/**
|
|
@@ -1325,7 +1487,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1325
1487
|
protected async validateAndStoreCheckpointProposal(
|
|
1326
1488
|
peerId: PeerId,
|
|
1327
1489
|
checkpoint: CheckpointProposal,
|
|
1328
|
-
): Promise<
|
|
1490
|
+
): Promise<
|
|
1491
|
+
ReceivedMessageValidationResult<
|
|
1492
|
+
CheckpointProposal,
|
|
1493
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1494
|
+
>
|
|
1495
|
+
> {
|
|
1329
1496
|
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1330
1497
|
|
|
1331
1498
|
if (validationResult.result === 'reject') {
|
|
@@ -1340,13 +1507,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1340
1507
|
// Extract and try to add the block proposal first if present
|
|
1341
1508
|
const blockProposal = checkpoint.getBlockProposal();
|
|
1342
1509
|
let processBlock = false;
|
|
1510
|
+
let isOversized = false;
|
|
1343
1511
|
if (blockProposal) {
|
|
1344
1512
|
this.logger.debug(`Validating block proposal from propagated checkpoint`, {
|
|
1345
1513
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1346
1514
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1347
1515
|
});
|
|
1348
1516
|
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1349
|
-
const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
|
|
1517
|
+
const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
|
|
1518
|
+
isOversized = blockIsOversized ?? false;
|
|
1519
|
+
|
|
1350
1520
|
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1351
1521
|
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1352
1522
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
@@ -1359,7 +1529,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1359
1529
|
severity:
|
|
1360
1530
|
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1361
1531
|
};
|
|
1362
|
-
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
|
|
1532
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
|
|
1533
|
+
// An oversized terminal block is re-broadcast as slashing evidence but never processed.
|
|
1363
1534
|
processBlock = true;
|
|
1364
1535
|
}
|
|
1365
1536
|
}
|
|
@@ -1379,11 +1550,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1379
1550
|
return {
|
|
1380
1551
|
result: TopicValidatorResult.Ignore,
|
|
1381
1552
|
obj: checkpoint,
|
|
1382
|
-
metadata: { isEquivocated, processBlock },
|
|
1553
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1383
1554
|
};
|
|
1384
1555
|
}
|
|
1385
1556
|
|
|
1386
|
-
// 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.
|
|
1387
1558
|
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1388
1559
|
if (!added) {
|
|
1389
1560
|
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
@@ -1394,7 +1565,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1394
1565
|
return {
|
|
1395
1566
|
result: TopicValidatorResult.Reject,
|
|
1396
1567
|
obj: checkpoint,
|
|
1397
|
-
metadata: { isEquivocated, processBlock },
|
|
1568
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1398
1569
|
severity: PeerErrorSeverity.HighToleranceError,
|
|
1399
1570
|
};
|
|
1400
1571
|
}
|
|
@@ -1415,12 +1586,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1415
1586
|
return {
|
|
1416
1587
|
result: TopicValidatorResult.Accept,
|
|
1417
1588
|
obj: checkpoint,
|
|
1418
|
-
metadata: { isEquivocated, processBlock },
|
|
1589
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1419
1590
|
};
|
|
1420
1591
|
}
|
|
1421
1592
|
|
|
1422
1593
|
// Otherwise, we're good to go!
|
|
1423
|
-
return {
|
|
1594
|
+
return {
|
|
1595
|
+
result: TopicValidatorResult.Accept,
|
|
1596
|
+
obj: checkpoint,
|
|
1597
|
+
metadata: { processBlock, isEquivocated, isOversized },
|
|
1598
|
+
};
|
|
1424
1599
|
}
|
|
1425
1600
|
|
|
1426
1601
|
/**
|
|
@@ -1441,9 +1616,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1441
1616
|
source: sender.toString(),
|
|
1442
1617
|
});
|
|
1443
1618
|
|
|
1619
|
+
await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
|
|
1620
|
+
|
|
1444
1621
|
// Call the checkpoint received callback with the core version (without lastBlock)
|
|
1445
1622
|
// to validate and potentially generate attestations
|
|
1446
|
-
const attestations = await this.
|
|
1623
|
+
const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
|
|
1447
1624
|
if (attestations && attestations.length > 0) {
|
|
1448
1625
|
// If the callback returned attestations, add them to the pool and propagate them
|
|
1449
1626
|
await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
|
|
@@ -1466,82 +1643,87 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1466
1643
|
}
|
|
1467
1644
|
|
|
1468
1645
|
/**
|
|
1469
|
-
* Validate the requested block transactions
|
|
1646
|
+
* Validate the requested block transactions request-response consistency.
|
|
1647
|
+
* It does NOT validate the transactions themselves.
|
|
1470
1648
|
* @param request - The block transactions request.
|
|
1471
1649
|
* @param response - The block transactions response.
|
|
1472
1650
|
* @param peerId - The ID of the peer that made the request.
|
|
1473
|
-
* @returns True if the
|
|
1651
|
+
* @returns True if the request-response is consistent, false otherwise.
|
|
1474
1652
|
*/
|
|
1475
|
-
@trackSpan('Libp2pService.
|
|
1653
|
+
@trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
|
|
1476
1654
|
[Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
|
|
1477
1655
|
}))
|
|
1478
|
-
protected async
|
|
1656
|
+
protected async validateRequestedBlockTxsConsistency(
|
|
1479
1657
|
request: BlockTxsRequest,
|
|
1480
1658
|
response: BlockTxsResponse,
|
|
1481
1659
|
peerId: PeerId,
|
|
1482
1660
|
): Promise<boolean> {
|
|
1483
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1484
|
-
|
|
1485
1661
|
try {
|
|
1486
|
-
|
|
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) {
|
|
1487
1666
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1488
|
-
throw new ValidationError(
|
|
1489
|
-
|
|
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`,
|
|
1490
1683
|
);
|
|
1684
|
+
// NOTE: We mark the response as invalid, given the limitations of a true/false return value.
|
|
1685
|
+
return false;
|
|
1491
1686
|
}
|
|
1492
1687
|
|
|
1493
|
-
|
|
1494
|
-
|
|
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);
|
|
1495
1697
|
throw new ValidationError(
|
|
1496
|
-
|
|
1698
|
+
'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
|
|
1497
1699
|
);
|
|
1498
1700
|
}
|
|
1499
1701
|
|
|
1500
|
-
//
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
|
|
1506
|
-
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1507
|
-
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1508
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1509
|
-
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;
|
|
1510
1706
|
}
|
|
1511
|
-
|
|
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) {
|
|
1512
1710
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1513
1711
|
throw new ValidationError(
|
|
1514
|
-
`Received
|
|
1712
|
+
`Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
|
|
1515
1713
|
);
|
|
1516
1714
|
}
|
|
1517
1715
|
|
|
1518
|
-
//
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
);
|
|
1528
|
-
const allowedIndexSet = new Set(intersectIdx);
|
|
1529
|
-
const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
|
|
1530
|
-
const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
|
|
1531
|
-
const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
|
|
1532
|
-
if (!allAllowed || !strictlyIncreasing) {
|
|
1533
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1534
|
-
throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
|
|
1535
|
-
}
|
|
1536
|
-
} else {
|
|
1537
|
-
// No local proposal, cannot check the membership/order of the returned txs
|
|
1538
|
-
this.logger.warn(
|
|
1539
|
-
`Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
|
|
1540
|
-
);
|
|
1541
|
-
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');
|
|
1542
1725
|
}
|
|
1543
1726
|
|
|
1544
|
-
await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
|
|
1545
1727
|
return true;
|
|
1546
1728
|
} catch (e: any) {
|
|
1547
1729
|
if (e instanceof ValidationError) {
|
|
@@ -1554,125 +1736,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1554
1736
|
}
|
|
1555
1737
|
}
|
|
1556
1738
|
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
*
|
|
1560
|
-
* The core component of this validator is that each tx hash MUST match the requested tx hash,
|
|
1561
|
-
* In order to perform this check, the tx proof must be verified.
|
|
1562
|
-
*
|
|
1563
|
-
* Note: This function is called from within `ReqResp.sendRequest` as part of the
|
|
1564
|
-
* ReqRespSubProtocol.TX subprotocol validation.
|
|
1565
|
-
*
|
|
1566
|
-
* @param requestedTxHash - The collection of the txs that was requested.
|
|
1567
|
-
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
1568
|
-
* @param peerId - The peer ID of the peer that sent the tx.
|
|
1569
|
-
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
1570
|
-
*/
|
|
1571
|
-
@trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
|
|
1572
|
-
[Attributes.TX_HASH]: requestedTxHash.toString(),
|
|
1573
|
-
}))
|
|
1574
|
-
private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
|
|
1575
|
-
const requested = new Set(requestedTxHash.map(h => h.toString()));
|
|
1576
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1577
|
-
|
|
1578
|
-
//TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
|
|
1579
|
-
// I think we should still extract the valid txs and return them, so that we can still use the response.
|
|
1580
|
-
try {
|
|
1581
|
-
await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
|
|
1582
|
-
return true;
|
|
1583
|
-
} catch (e: any) {
|
|
1584
|
-
if (e instanceof ValidationError) {
|
|
1585
|
-
this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
|
|
1586
|
-
} else {
|
|
1587
|
-
this.logger.error(`Error during validation of requested txs`, e);
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
return false;
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
/**
|
|
1595
|
-
* Validates a BLOCK response.
|
|
1596
|
-
*
|
|
1597
|
-
* If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
|
|
1598
|
-
* Penalizes on block number mismatch or hash mismatch.
|
|
1599
|
-
*
|
|
1600
|
-
* @param requestedBlockNumber - The requested block number.
|
|
1601
|
-
* @param responseBlock - The block returned by the peer.
|
|
1602
|
-
* @param peerId - The peer that returned the block.
|
|
1603
|
-
* @returns True if the response is valid, false otherwise.
|
|
1604
|
-
*/
|
|
1605
|
-
@trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
|
|
1606
|
-
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
|
|
1607
|
-
}))
|
|
1608
|
-
protected async validateRequestedBlock(
|
|
1609
|
-
requestedBlockNumber: Fr,
|
|
1610
|
-
responseBlock: L2Block,
|
|
1611
|
-
peerId: PeerId,
|
|
1612
|
-
): Promise<boolean> {
|
|
1613
|
-
try {
|
|
1614
|
-
const reqNum = Number(requestedBlockNumber.toString());
|
|
1615
|
-
if (responseBlock.number !== reqNum) {
|
|
1616
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1617
|
-
return false;
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
const local = await this.archiver.getBlock(BlockNumber(reqNum));
|
|
1621
|
-
if (!local) {
|
|
1622
|
-
// We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
|
|
1623
|
-
// TODO: Consider extending this validator to accept an expected hash or
|
|
1624
|
-
// performing quorum-based checks when using P2P syncing prior to L1 sync.
|
|
1625
|
-
this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
|
|
1626
|
-
return false;
|
|
1627
|
-
}
|
|
1628
|
-
const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
|
|
1629
|
-
if (!localHash.equals(respHash)) {
|
|
1630
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1631
|
-
return false;
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
return true;
|
|
1635
|
-
} catch (e) {
|
|
1636
|
-
this.logger.warn(`Error validating requested block`, e);
|
|
1637
|
-
return false;
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
protected async validateRequestedTx(
|
|
1642
|
-
tx: Tx,
|
|
1643
|
-
peerId: PeerId,
|
|
1644
|
-
txValidator: TxValidator,
|
|
1645
|
-
requested?: Set<`0x${string}`>,
|
|
1646
|
-
) {
|
|
1647
|
-
const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
|
|
1648
|
-
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1649
|
-
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1650
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
const { result } = await txValidator.validateTx(tx);
|
|
1654
|
-
if (result === 'invalid') {
|
|
1655
|
-
penalize(PeerErrorSeverity.LowToleranceError);
|
|
1656
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
protected createRequestedTxValidator(): TxValidator {
|
|
1661
|
-
return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
|
|
1662
|
-
l1ChainId: this.config.l1ChainId,
|
|
1663
|
-
rollupVersion: this.config.rollupVersion,
|
|
1664
|
-
});
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
1668
|
-
if (blockNumber === this.feesCache?.blockNumber) {
|
|
1669
|
-
return this.feesCache.gasFees;
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
const header = await this.archiver.getBlockHeader(blockNumber);
|
|
1673
|
-
const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
|
|
1674
|
-
this.feesCache = { blockNumber, gasFees };
|
|
1675
|
-
return gasFees;
|
|
1739
|
+
private getGasFees(): Promise<GasFees> {
|
|
1740
|
+
return this.blockMinFeesProvider.getCurrentMinFees();
|
|
1676
1741
|
}
|
|
1677
1742
|
|
|
1678
1743
|
/**
|
|
@@ -1686,8 +1751,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1686
1751
|
l1ChainId: this.config.l1ChainId,
|
|
1687
1752
|
rollupVersion: this.config.rollupVersion,
|
|
1688
1753
|
proofVerifier: this.proofVerifier,
|
|
1754
|
+
txValidationCache: this.txValidationCache,
|
|
1689
1755
|
},
|
|
1690
1756
|
peerScoring: this.peerManager,
|
|
1757
|
+
validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
|
|
1691
1758
|
};
|
|
1692
1759
|
}
|
|
1693
1760
|
|
|
@@ -1696,16 +1763,19 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1696
1763
|
this.proofVerifier,
|
|
1697
1764
|
{ l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
|
|
1698
1765
|
this.logger.getBindings(),
|
|
1766
|
+
this.txValidationCache,
|
|
1699
1767
|
);
|
|
1700
1768
|
|
|
1701
1769
|
const results = await Promise.all(
|
|
1702
|
-
txs.map(async tx => {
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1770
|
+
txs.map(async tx => ({ txHash: tx.getTxHash(), result: await validator.validateTx(tx) })),
|
|
1771
|
+
);
|
|
1772
|
+
|
|
1773
|
+
const invalidTxs = results.flatMap(({ txHash, result }) =>
|
|
1774
|
+
result.result === 'invalid' ? [{ txHash, reasons: result.reason }] : [],
|
|
1706
1775
|
);
|
|
1707
|
-
|
|
1708
|
-
|
|
1776
|
+
|
|
1777
|
+
if (invalidTxs.length > 0) {
|
|
1778
|
+
throw new InvalidBlockProposalTxsError(invalidTxs);
|
|
1709
1779
|
}
|
|
1710
1780
|
}
|
|
1711
1781
|
|
|
@@ -1714,13 +1784,14 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1714
1784
|
currentBlockNumber: BlockNumber,
|
|
1715
1785
|
nextSlotTimestamp: UInt64,
|
|
1716
1786
|
): Promise<Record<string, TransactionValidator>> {
|
|
1717
|
-
const gasFees = await this.getGasFees(
|
|
1787
|
+
const gasFees = await this.getGasFees();
|
|
1718
1788
|
const allowedInSetup = [
|
|
1719
1789
|
...(await getDefaultAllowedSetupFunctions()),
|
|
1720
1790
|
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1721
1791
|
];
|
|
1722
1792
|
const blockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1723
1793
|
const l1Constants = await this.archiver.getL1Constants();
|
|
1794
|
+
const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
|
|
1724
1795
|
|
|
1725
1796
|
return createFirstStageTxValidationsForGossipedTransactions(
|
|
1726
1797
|
nextSlotTimestamp,
|
|
@@ -1735,9 +1806,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1735
1806
|
allowedInSetup,
|
|
1736
1807
|
this.logger.getBindings(),
|
|
1737
1808
|
{
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1809
|
+
maxTxL2Gas: networkTxGasLimits.l2Gas,
|
|
1810
|
+
maxTxDAGas: networkTxGasLimits.daGas,
|
|
1741
1811
|
},
|
|
1742
1812
|
);
|
|
1743
1813
|
}
|