@aztec/p2p 0.0.1-commit.42ee6df9b → 0.0.1-commit.431c48d
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 +32 -23
- package/dest/client/interface.d.ts +44 -11
- 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 +119 -59
- package/dest/config.d.ts +150 -107
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +77 -26
- 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/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 +270 -198
- 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/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 +10 -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 +29 -20
- 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/instrumentation.d.ts +11 -1
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/instrumentation.js +24 -0
- 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 +8 -5
- 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 +16 -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 +83 -32
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +50 -6
- 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 +243 -127
- 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/tx_proof_validator.d.ts +2 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/discv5/discV5_service.d.ts +13 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +119 -23
- package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
- package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
- package/dest/services/discv5/persisted_enr_store.js +145 -0
- package/dest/services/dummy_service.d.ts +18 -18
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +19 -18
- 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 +6 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +21 -0
- package/dest/services/libp2p/libp2p_service.d.ts +40 -43
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +370 -276
- package/dest/services/peer-manager/peer_manager.d.ts +26 -2
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +103 -17
- package/dest/services/peer-manager/peer_scoring.d.ts +30 -1
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +74 -3
- 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_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 +41 -19
- 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/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 +1 -1
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +4 -1
- 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 +33 -9
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +57 -44
- package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +16 -18
- package/dest/test-helpers/test_tx_provider.d.ts +3 -1
- package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
- package/dest/test-helpers/test_tx_provider.js +3 -0
- package/dest/test-helpers/testbench-utils.d.ts +15 -14
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +28 -13
- package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +60 -27
- 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 -8
- package/dest/util.d.ts +11 -6
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +35 -14
- package/dest/versioning.d.ts +3 -6
- package/dest/versioning.d.ts.map +1 -1
- package/dest/versioning.js +3 -24
- package/package.json +17 -17
- package/src/bootstrap/bootstrap.ts +17 -3
- package/src/client/factory.ts +48 -24
- package/src/client/interface.ts +39 -17
- package/src/client/p2p_client.ts +135 -92
- package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
- package/src/config.ts +159 -34
- package/src/errors/reqresp.error.ts +13 -19
- package/src/mem_pools/attestation_pool/attestation_pool.ts +325 -242
- 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/instrumentation.ts +18 -12
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +30 -20
- 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/instrumentation.ts +27 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +31 -4
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +13 -7
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +35 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +94 -31
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +270 -133
- 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/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/tx_proof_validator.ts +2 -0
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
- package/src/services/data_store.ts +5 -13
- package/src/services/discv5/discV5_service.ts +126 -14
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +25 -44
- 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 +22 -0
- package/src/services/libp2p/libp2p_service.ts +433 -302
- package/src/services/peer-manager/peer_manager.ts +112 -16
- package/src/services/peer-manager/peer_scoring.ts +81 -3
- 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_limits.ts +3 -13
- package/src/services/reqresp/reqresp.ts +69 -252
- package/src/services/service.ts +48 -37
- 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/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 +2 -1
- 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 +61 -60
- package/src/test-helpers/reqresp-nodes.ts +12 -22
- package/src/test-helpers/test_tx_provider.ts +5 -0
- package/src/test-helpers/testbench-utils.ts +35 -26
- package/src/testbench/p2p_client_testbench_worker.ts +68 -44
- package/src/testbench/worker_client_manager.ts +59 -19
- package/src/util.ts +32 -17
- package/src/versioning.ts +3 -33
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -315
- 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/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 -355
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
- 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,33 +1,41 @@
|
|
|
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';
|
|
13
|
-
import type {
|
|
12
|
+
import { type BlockMinFeesProvider, GasFees, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
|
|
13
|
+
import type {
|
|
14
|
+
ClientProtocolCircuitVerifier,
|
|
15
|
+
P2PConnectivity,
|
|
16
|
+
PeerInfo,
|
|
17
|
+
WorldStateSynchronizer,
|
|
18
|
+
} from '@aztec/stdlib/interfaces/server';
|
|
14
19
|
import {
|
|
15
20
|
BlockProposal,
|
|
16
21
|
CheckpointAttestation,
|
|
17
22
|
CheckpointProposal,
|
|
18
|
-
type CheckpointProposalCore,
|
|
19
23
|
type Gossipable,
|
|
20
24
|
P2PMessage,
|
|
21
25
|
PeerErrorSeverity,
|
|
22
26
|
PeerErrorSeverityByHarshness,
|
|
23
27
|
TopicType,
|
|
28
|
+
ValidatedBlockProposal,
|
|
29
|
+
ValidatedCheckpointProposalCore,
|
|
24
30
|
createTopicString,
|
|
25
31
|
getTopicsForConfig,
|
|
26
32
|
metricsTopicStrToLabels,
|
|
27
33
|
} from '@aztec/stdlib/p2p';
|
|
34
|
+
import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
|
|
28
35
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
29
|
-
import { Tx, type
|
|
36
|
+
import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
|
|
30
37
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
38
|
+
import { InvalidBlockProposalTxsError } from '@aztec/stdlib/validators';
|
|
31
39
|
import { compressComponentVersions } from '@aztec/stdlib/versioning';
|
|
32
40
|
import {
|
|
33
41
|
Attributes,
|
|
@@ -51,9 +59,10 @@ import { yamux } from '@chainsafe/libp2p-yamux';
|
|
|
51
59
|
import { bootstrap } from '@libp2p/bootstrap';
|
|
52
60
|
import { identify } from '@libp2p/identify';
|
|
53
61
|
import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
54
|
-
import type { ConnectionManager } from '@libp2p/interface-internal';
|
|
62
|
+
import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
|
|
55
63
|
import { mplex } from '@libp2p/mplex';
|
|
56
64
|
import { tcp } from '@libp2p/tcp';
|
|
65
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
|
57
66
|
import { ENR } from '@nethermindeth/enr';
|
|
58
67
|
import { createLibp2p } from 'libp2p';
|
|
59
68
|
|
|
@@ -74,38 +83,34 @@ import {
|
|
|
74
83
|
createFirstStageTxValidationsForGossipedTransactions,
|
|
75
84
|
createSecondStageTxValidationsForGossipedTransactions,
|
|
76
85
|
createTxValidatorForBlockProposalReceivedTxs,
|
|
77
|
-
createTxValidatorForReqResponseReceivedTxs,
|
|
78
86
|
} from '../../msg_validators/tx_validator/factory.js';
|
|
87
|
+
import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
|
|
79
88
|
import { GossipSubEvent } from '../../types/index.js';
|
|
80
89
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
81
90
|
import { getVersions } from '../../versioning.js';
|
|
82
91
|
import { AztecDatastore } from '../data_store.js';
|
|
83
92
|
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
84
|
-
import { SnappyTransform,
|
|
93
|
+
import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
85
94
|
import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
86
95
|
import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
|
|
87
96
|
import type { PeerManagerInterface } from '../peer-manager/interface.js';
|
|
88
97
|
import { PeerManager } from '../peer-manager/peer_manager.js';
|
|
89
98
|
import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
90
99
|
import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
|
|
91
|
-
import type
|
|
100
|
+
import { type P2PReqRespConfig, YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
|
|
92
101
|
import {
|
|
93
102
|
AuthRequest,
|
|
94
103
|
BlockTxsRequest,
|
|
95
104
|
BlockTxsResponse,
|
|
96
|
-
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
97
105
|
type ReqRespInterface,
|
|
98
106
|
type ReqRespResponse,
|
|
99
107
|
ReqRespSubProtocol,
|
|
100
108
|
type ReqRespSubProtocolHandler,
|
|
101
109
|
type ReqRespSubProtocolHandlers,
|
|
102
|
-
type ReqRespSubProtocolValidators,
|
|
103
110
|
StatusMessage,
|
|
104
|
-
type SubProtocolMap,
|
|
105
111
|
ValidationError,
|
|
106
112
|
pingHandler,
|
|
107
113
|
reqGoodbyeHandler,
|
|
108
|
-
reqRespBlockHandler,
|
|
109
114
|
reqRespBlockTxsHandler,
|
|
110
115
|
reqRespStatusHandler,
|
|
111
116
|
reqRespTxHandler,
|
|
@@ -113,13 +118,33 @@ import {
|
|
|
113
118
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
114
119
|
import type {
|
|
115
120
|
P2PBlockReceivedCallback,
|
|
121
|
+
P2PCheckpointAttestationCallback,
|
|
116
122
|
P2PCheckpointReceivedCallback,
|
|
117
123
|
P2PDuplicateAttestationCallback,
|
|
124
|
+
P2POversizedProposalCallback,
|
|
118
125
|
P2PService,
|
|
119
126
|
PeerDiscoveryService,
|
|
120
127
|
} from '../service.js';
|
|
121
128
|
import { P2PInstrumentation } from './instrumentation.js';
|
|
122
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Builds the {@link ConsensusTimetable} shared by the gossip validators for proposal/attestation receive-window
|
|
132
|
+
* bounds. Derived purely from protocol slot-timing constants plus the block sub-slot duration and the consensus
|
|
133
|
+
* materialization grace, so every node agrees on these bounds without depending on proposer operational budgets.
|
|
134
|
+
*/
|
|
135
|
+
function buildConsensusTimetable(
|
|
136
|
+
config: P2PConfig,
|
|
137
|
+
l1Constants: ReturnType<EpochCacheInterface['getL1Constants']>,
|
|
138
|
+
): ConsensusTimetable {
|
|
139
|
+
const blockDuration = config.blockDurationMs / 1000;
|
|
140
|
+
return new ConsensusTimetable({
|
|
141
|
+
l1Constants,
|
|
142
|
+
blockDuration,
|
|
143
|
+
checkpointProposalSyncGrace:
|
|
144
|
+
config.checkpointProposalSyncGraceSeconds ?? getDefaultCheckpointProposalSyncGrace(blockDuration),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
123
148
|
interface ValidationResult {
|
|
124
149
|
name: string;
|
|
125
150
|
isValid: TxValidationResult;
|
|
@@ -148,8 +173,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
148
173
|
private protocolVersion = '';
|
|
149
174
|
private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
|
|
150
175
|
|
|
151
|
-
private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
|
|
152
|
-
|
|
153
176
|
/** Callback invoked when a duplicate proposal is detected (triggers slashing). */
|
|
154
177
|
private duplicateProposalCallback?: (info: {
|
|
155
178
|
slot: SlotNumber;
|
|
@@ -157,9 +180,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
157
180
|
type: 'checkpoint' | 'block';
|
|
158
181
|
}) => void;
|
|
159
182
|
|
|
183
|
+
/** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
|
|
184
|
+
private oversizedProposalCallback?: P2POversizedProposalCallback;
|
|
185
|
+
|
|
160
186
|
/** Callback invoked when a duplicate attestation is detected (triggers slashing). */
|
|
161
187
|
private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
|
|
162
188
|
|
|
189
|
+
/** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
|
|
190
|
+
private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
|
|
191
|
+
|
|
163
192
|
/**
|
|
164
193
|
* Callback for when a block is received from a peer.
|
|
165
194
|
* @param block - The block received from the peer.
|
|
@@ -182,6 +211,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
182
211
|
|
|
183
212
|
private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
|
|
184
213
|
|
|
214
|
+
private ipChangedHandler?: (ip: string) => void;
|
|
215
|
+
private discoveredP2pIp?: string;
|
|
216
|
+
|
|
185
217
|
private instrumentation: P2PInstrumentation;
|
|
186
218
|
|
|
187
219
|
private telemetry: TelemetryClient;
|
|
@@ -199,8 +231,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
199
231
|
private epochCache: EpochCacheInterface,
|
|
200
232
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
201
233
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
234
|
+
private blockMinFeesProvider: BlockMinFeesProvider,
|
|
202
235
|
telemetry: TelemetryClient,
|
|
203
236
|
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
237
|
+
private txValidationCache?: TxValidationCache,
|
|
204
238
|
) {
|
|
205
239
|
super(telemetry, 'LibP2PService');
|
|
206
240
|
this.telemetry = telemetry;
|
|
@@ -230,15 +264,39 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
230
264
|
this.protocolVersion,
|
|
231
265
|
);
|
|
232
266
|
|
|
267
|
+
// Build the consensus timetable once from protocol slot-timing constants and inject it into every
|
|
268
|
+
// validator so they share one set of receive-window bounds, independent of proposer operational budgets.
|
|
269
|
+
const consensusTimetable = buildConsensusTimetable(config, epochCache.getL1Constants());
|
|
233
270
|
const proposalValidatorOpts = {
|
|
234
271
|
txsPermitted: !config.disableTransactions,
|
|
235
272
|
maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
|
|
273
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
|
|
274
|
+
skipSlotValidation: config.skipProposalSlotValidation,
|
|
275
|
+
signatureContext: {
|
|
276
|
+
chainId: config.l1ChainId,
|
|
277
|
+
rollupAddress: config.rollupAddress,
|
|
278
|
+
},
|
|
279
|
+
clockDisparityMs: config.maxGossipClockDisparityMs,
|
|
280
|
+
};
|
|
281
|
+
this.blockProposalValidator = new BlockProposalValidator(epochCache, consensusTimetable, proposalValidatorOpts);
|
|
282
|
+
this.checkpointProposalValidator = new CheckpointProposalValidator(
|
|
283
|
+
epochCache,
|
|
284
|
+
consensusTimetable,
|
|
285
|
+
proposalValidatorOpts,
|
|
286
|
+
);
|
|
287
|
+
const attestationValidatorOpts = {
|
|
288
|
+
signatureContext: proposalValidatorOpts.signatureContext,
|
|
289
|
+
clockDisparityMs: config.maxGossipClockDisparityMs,
|
|
236
290
|
};
|
|
237
|
-
this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
|
|
238
|
-
this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
|
|
239
291
|
this.checkpointAttestationValidator = config.fishermanMode
|
|
240
|
-
? new FishermanAttestationValidator(
|
|
241
|
-
|
|
292
|
+
? new FishermanAttestationValidator(
|
|
293
|
+
epochCache,
|
|
294
|
+
consensusTimetable,
|
|
295
|
+
mempools.attestationPool,
|
|
296
|
+
telemetry,
|
|
297
|
+
attestationValidatorOpts,
|
|
298
|
+
)
|
|
299
|
+
: new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
|
|
242
300
|
|
|
243
301
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
244
302
|
|
|
@@ -251,20 +309,21 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
251
309
|
};
|
|
252
310
|
|
|
253
311
|
this.allNodesCheckpointReceivedCallback = (
|
|
254
|
-
_checkpoint:
|
|
312
|
+
_checkpoint: ValidatedCheckpointProposalCore,
|
|
255
313
|
): Promise<CheckpointAttestation[] | undefined> => {
|
|
256
314
|
throw new CheckpointProposalReceivedCallbackNotRegisteredError();
|
|
257
315
|
};
|
|
258
316
|
|
|
259
317
|
this.validatorCheckpointReceivedCallback = (
|
|
260
|
-
_checkpoint:
|
|
318
|
+
_checkpoint: ValidatedCheckpointProposalCore,
|
|
261
319
|
): Promise<CheckpointAttestation[] | undefined> => {
|
|
262
320
|
return Promise.resolve(undefined);
|
|
263
321
|
};
|
|
264
322
|
}
|
|
265
323
|
|
|
266
|
-
public updateConfig(config: Partial<P2PReqRespConfig
|
|
324
|
+
public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
|
|
267
325
|
this.reqresp.updateConfig(config);
|
|
326
|
+
this.config = merge(this.config, config);
|
|
268
327
|
}
|
|
269
328
|
|
|
270
329
|
/**
|
|
@@ -283,9 +342,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
283
342
|
proofVerifier: ClientProtocolCircuitVerifier;
|
|
284
343
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
285
344
|
peerStore: AztecAsyncKVStore;
|
|
345
|
+
blockMinFeesProvider: BlockMinFeesProvider;
|
|
286
346
|
telemetry: TelemetryClient;
|
|
287
347
|
logger: Logger;
|
|
288
348
|
packageVersion: string;
|
|
349
|
+
txValidationCache?: TxValidationCache;
|
|
289
350
|
},
|
|
290
351
|
) {
|
|
291
352
|
const {
|
|
@@ -295,9 +356,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
295
356
|
mempools,
|
|
296
357
|
proofVerifier,
|
|
297
358
|
peerStore,
|
|
359
|
+
blockMinFeesProvider,
|
|
298
360
|
telemetry,
|
|
299
361
|
logger,
|
|
300
362
|
packageVersion,
|
|
363
|
+
txValidationCache,
|
|
301
364
|
} = deps;
|
|
302
365
|
const { p2pPort, maxPeerCount, listenAddress } = config;
|
|
303
366
|
const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
|
|
@@ -312,6 +375,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
312
375
|
packageVersion,
|
|
313
376
|
telemetry,
|
|
314
377
|
createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
|
|
378
|
+
peerStore,
|
|
315
379
|
);
|
|
316
380
|
|
|
317
381
|
// Seed libp2p's bootstrap discovery with private and trusted peers
|
|
@@ -326,34 +390,49 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
326
390
|
const protocolVersion = compressComponentVersions(versions);
|
|
327
391
|
|
|
328
392
|
const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
329
|
-
const directPeers = (
|
|
393
|
+
const directPeers = compactArray(
|
|
330
394
|
await Promise.all(
|
|
331
395
|
preferredPeersEnrs.map(async enr => {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
396
|
+
try {
|
|
397
|
+
const peerId = await enr.peerId();
|
|
398
|
+
const address = enr.getLocationMultiaddr('tcp');
|
|
399
|
+
if (address === undefined) {
|
|
400
|
+
throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
id: peerId,
|
|
404
|
+
addrs: [address],
|
|
405
|
+
};
|
|
406
|
+
} catch (err) {
|
|
407
|
+
// A malformed configured ENR shouldn't abort node setup — skip it and log.
|
|
408
|
+
logger.warn(`Skipping preferred peer with invalid ENR`, { err });
|
|
409
|
+
return undefined;
|
|
336
410
|
}
|
|
337
|
-
return {
|
|
338
|
-
id: peerId,
|
|
339
|
-
addrs: [address],
|
|
340
|
-
};
|
|
341
411
|
}),
|
|
342
|
-
)
|
|
343
|
-
)
|
|
412
|
+
),
|
|
413
|
+
);
|
|
344
414
|
|
|
345
415
|
const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
|
|
346
416
|
|
|
347
|
-
// Create dynamic topic score params based on network configuration
|
|
417
|
+
// Create dynamic topic score params based on network configuration. Scoring uses the network-wide
|
|
418
|
+
// max-blocks-per-checkpoint config value directly to size expected per-slot message rates; these are
|
|
419
|
+
// peer-rate thresholds, not consensus deadlines, so they need no proposer operational budgets.
|
|
348
420
|
const l1Constants = epochCache.getL1Constants();
|
|
349
421
|
const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
|
|
350
422
|
slotDurationMs: l1Constants.slotDuration * 1000,
|
|
351
423
|
heartbeatIntervalMs: config.gossipsubInterval,
|
|
352
424
|
targetCommitteeSize: l1Constants.targetCommitteeSize,
|
|
353
|
-
|
|
425
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
|
|
354
426
|
expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
|
|
355
427
|
});
|
|
356
428
|
|
|
429
|
+
// Restrict gossipsub to exactly the topics we subscribe to. Without this, an arbitrary-topic
|
|
430
|
+
// message is transformed, msg-id'd and inserted into the seenCache before the subscription check,
|
|
431
|
+
// so a crafted topic colliding on msg id can suppress a real message as a duplicate.
|
|
432
|
+
const allowedTopics = getTopicsForConfig(config.disableTransactions).map(topic =>
|
|
433
|
+
createTopicString(topic, protocolVersion),
|
|
434
|
+
);
|
|
435
|
+
|
|
357
436
|
const node = await createLibp2p({
|
|
358
437
|
start: false,
|
|
359
438
|
peerId,
|
|
@@ -385,7 +464,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
385
464
|
],
|
|
386
465
|
datastore,
|
|
387
466
|
peerDiscovery,
|
|
388
|
-
|
|
467
|
+
// Pin the yamux frame size: MAX_REQRESP_REQUEST_SIZE_BYTES relies on a reqresp request fitting in one frame.
|
|
468
|
+
streamMuxers: [yamux({ maxMessageSize: YAMUX_MAX_MESSAGE_SIZE_BYTES }), mplex()],
|
|
389
469
|
connectionEncryption: [noise()],
|
|
390
470
|
connectionManager: {
|
|
391
471
|
minConnections: 0, // Disable libp2p peer dialing, we do it manually
|
|
@@ -399,7 +479,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
399
479
|
},
|
|
400
480
|
connectionGater: {
|
|
401
481
|
denyInboundConnection: (maConn: MultiaddrConnection) => {
|
|
402
|
-
const allowed = peerManager.
|
|
482
|
+
const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
|
|
403
483
|
if (allowed) {
|
|
404
484
|
return false;
|
|
405
485
|
}
|
|
@@ -410,7 +490,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
410
490
|
denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
|
|
411
491
|
//NOTE: it is not necessary to check address here because this was already done by
|
|
412
492
|
// denyInboundConnection
|
|
413
|
-
const allowed = peerManager.
|
|
493
|
+
const allowed = peerManager.isPeerAllowedToConnect(peerId);
|
|
414
494
|
if (allowed) {
|
|
415
495
|
return false;
|
|
416
496
|
}
|
|
@@ -438,9 +518,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
438
518
|
mcacheLength: config.gossipsubMcacheLength,
|
|
439
519
|
mcacheGossip: config.gossipsubMcacheGossip,
|
|
440
520
|
seenTTL: config.gossipsubSeenTTL,
|
|
521
|
+
allowedTopics,
|
|
522
|
+
// No fastMsgIdFn: the fast-path dedup cache keys on a non-cryptographic 64-bit hash of the
|
|
523
|
+
// raw data only (no topic), so a collision — accidental or engineered via a weak seed — drops
|
|
524
|
+
// a different message with no fallback to the full id. Dedup instead rests solely on the
|
|
525
|
+
// cryptographic, topic-framed msgIdFn below.
|
|
441
526
|
msgIdFn: getMsgIdFn,
|
|
442
527
|
msgIdToStrFn: msgIdToStrFn,
|
|
443
|
-
fastMsgIdFn: fastMsgIdFn,
|
|
444
528
|
dataTransform: new SnappyTransform(),
|
|
445
529
|
metricsRegister: otelMetricsAdapter,
|
|
446
530
|
metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
|
|
@@ -452,8 +536,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
452
536
|
topics: topicScoreParams,
|
|
453
537
|
}),
|
|
454
538
|
}) as (components: GossipSubComponents) => GossipSub,
|
|
455
|
-
components: (components: { connectionManager: ConnectionManager }) => ({
|
|
539
|
+
components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
|
|
456
540
|
connectionManager: components.connectionManager,
|
|
541
|
+
addressManager: components.addressManager,
|
|
457
542
|
}),
|
|
458
543
|
},
|
|
459
544
|
logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
|
|
@@ -475,6 +560,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
475
560
|
epochCache,
|
|
476
561
|
);
|
|
477
562
|
|
|
563
|
+
// Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
|
|
564
|
+
reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
|
|
565
|
+
|
|
478
566
|
// Configure application-specific scoring for gossipsub.
|
|
479
567
|
// The weight scales app score to align with gossipsub thresholds:
|
|
480
568
|
// - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
|
|
@@ -495,8 +583,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
495
583
|
epochCache,
|
|
496
584
|
proofVerifier,
|
|
497
585
|
worldStateSynchronizer,
|
|
586
|
+
blockMinFeesProvider,
|
|
498
587
|
telemetry,
|
|
499
588
|
logger,
|
|
589
|
+
txValidationCache,
|
|
500
590
|
);
|
|
501
591
|
}
|
|
502
592
|
|
|
@@ -510,24 +600,21 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
510
600
|
throw new Error('P2P service already started');
|
|
511
601
|
}
|
|
512
602
|
|
|
513
|
-
// Get listen & announce addresses for logging
|
|
514
603
|
const { p2pIp, p2pPort } = this.config;
|
|
515
|
-
if (!p2pIp) {
|
|
516
|
-
throw new Error('Announce address not provided.');
|
|
604
|
+
if (!p2pIp && !this.config.queryForIp) {
|
|
605
|
+
throw new Error('Announce address not provided and queryForIp is not enabled.');
|
|
517
606
|
}
|
|
518
|
-
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
607
|
+
const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
|
|
519
608
|
|
|
520
609
|
// Create request response protocol handlers
|
|
521
610
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
522
611
|
const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
|
|
523
|
-
const blockHandler = reqRespBlockHandler(this.archiver);
|
|
524
612
|
const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
|
|
525
613
|
|
|
526
614
|
const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
|
|
527
615
|
[ReqRespSubProtocol.PING]: pingHandler,
|
|
528
616
|
[ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
|
|
529
617
|
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
|
|
530
|
-
[ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
|
|
531
618
|
};
|
|
532
619
|
|
|
533
620
|
if (!this.config.disableTransactions) {
|
|
@@ -543,17 +630,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
543
630
|
requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
|
|
544
631
|
}
|
|
545
632
|
|
|
546
|
-
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
547
|
-
const reqrespSubProtocolValidators = {
|
|
548
|
-
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
549
|
-
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
550
|
-
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
551
|
-
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
|
|
552
|
-
};
|
|
553
|
-
|
|
554
633
|
await this.peerManager.initializePeers();
|
|
555
634
|
|
|
556
|
-
await this.reqresp.start(requestResponseHandlers
|
|
635
|
+
await this.reqresp.start(requestResponseHandlers);
|
|
557
636
|
|
|
558
637
|
await this.node.start();
|
|
559
638
|
|
|
@@ -569,6 +648,38 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
569
648
|
if (!this.config.p2pDiscoveryDisabled) {
|
|
570
649
|
await this.peerDiscoveryService.start();
|
|
571
650
|
}
|
|
651
|
+
|
|
652
|
+
// Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
|
|
653
|
+
if (this.config.queryForIp) {
|
|
654
|
+
this.discoveredP2pIp = this.config.p2pIp;
|
|
655
|
+
this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
|
|
656
|
+
this.ipChangedHandler = (ip: string) => {
|
|
657
|
+
const addressManager = this.node.services.components.addressManager;
|
|
658
|
+
const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
|
|
659
|
+
const previousIp = this.discoveredP2pIp;
|
|
660
|
+
|
|
661
|
+
if (previousIp) {
|
|
662
|
+
const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
|
|
663
|
+
addressManager.removeObservedAddr(oldAddr);
|
|
664
|
+
this.logger.info('Libp2p announce address updated due to IP change', {
|
|
665
|
+
previousIp,
|
|
666
|
+
newIp: ip,
|
|
667
|
+
newMultiaddr: newAddr.toString(),
|
|
668
|
+
});
|
|
669
|
+
} else {
|
|
670
|
+
this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
|
|
671
|
+
ip,
|
|
672
|
+
multiaddr: newAddr.toString(),
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
addressManager.addObservedAddr(newAddr);
|
|
677
|
+
addressManager.confirmObservedAddr(newAddr);
|
|
678
|
+
this.discoveredP2pIp = ip;
|
|
679
|
+
};
|
|
680
|
+
this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
|
|
681
|
+
}
|
|
682
|
+
|
|
572
683
|
this.discoveryRunningPromise = new RunningPromise(
|
|
573
684
|
async () => {
|
|
574
685
|
await this.peerManager.heartbeat();
|
|
@@ -594,6 +705,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
594
705
|
// Remove gossip sub listener
|
|
595
706
|
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
596
707
|
|
|
708
|
+
if (this.ipChangedHandler) {
|
|
709
|
+
this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
|
|
710
|
+
this.ipChangedHandler = undefined;
|
|
711
|
+
}
|
|
712
|
+
|
|
597
713
|
// Stop peer manager
|
|
598
714
|
this.logger.debug('Stopping peer manager...');
|
|
599
715
|
await this.peerManager.stop();
|
|
@@ -608,12 +724,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
608
724
|
this.logger.info('LibP2P service stopped');
|
|
609
725
|
}
|
|
610
726
|
|
|
611
|
-
addReqRespSubProtocol(
|
|
612
|
-
subProtocol
|
|
613
|
-
handler: ReqRespSubProtocolHandler,
|
|
614
|
-
validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
|
|
615
|
-
): Promise<void> {
|
|
616
|
-
return this.reqresp.addSubProtocol(subProtocol, handler, validator);
|
|
727
|
+
addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
|
|
728
|
+
return this.reqresp.addSubProtocol(subProtocol, handler);
|
|
617
729
|
}
|
|
618
730
|
|
|
619
731
|
public registerThisValidatorAddresses(address: EthAddress[]): void {
|
|
@@ -624,6 +736,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
624
736
|
return this.peerManager.getPeers(includePending);
|
|
625
737
|
}
|
|
626
738
|
|
|
739
|
+
public getP2PConnectivity(): P2PConnectivity {
|
|
740
|
+
const connectedPeers = this.peerManager.getPeers().filter(peer => peer.status === 'connected').length;
|
|
741
|
+
return { enabled: true, connectedPeers };
|
|
742
|
+
}
|
|
743
|
+
|
|
627
744
|
public getGossipMeshPeerCount(topicType: TopicType): number {
|
|
628
745
|
return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
|
|
629
746
|
}
|
|
@@ -641,20 +758,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
641
758
|
setImmediate(() => void safeJob());
|
|
642
759
|
}
|
|
643
760
|
|
|
644
|
-
/**
|
|
645
|
-
* Send a batch of requests to peers, and return the responses
|
|
646
|
-
* @param protocol - The request response protocol to use
|
|
647
|
-
* @param requests - The requests to send to the peers
|
|
648
|
-
* @returns The responses to the requests
|
|
649
|
-
*/
|
|
650
|
-
sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
|
|
651
|
-
protocol: SubProtocol,
|
|
652
|
-
requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
|
|
653
|
-
pinnedPeerId: PeerId | undefined,
|
|
654
|
-
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
|
|
655
|
-
return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
761
|
public sendRequestToPeer(
|
|
659
762
|
peerId: PeerId,
|
|
660
763
|
subProtocol: ReqRespSubProtocol,
|
|
@@ -684,10 +787,6 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
684
787
|
this.allNodesCheckpointReceivedCallback = callback;
|
|
685
788
|
}
|
|
686
789
|
|
|
687
|
-
public async notifyOwnCheckpointProposal(checkpoint: CheckpointProposalCore): Promise<void> {
|
|
688
|
-
await this.allNodesCheckpointReceivedCallback(checkpoint, this.node.peerId);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
790
|
/**
|
|
692
791
|
* Registers a callback to be invoked when a duplicate proposal is detected.
|
|
693
792
|
* This callback is triggered on the first duplicate (when count goes from 1 to 2).
|
|
@@ -698,6 +797,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
698
797
|
this.duplicateProposalCallback = callback;
|
|
699
798
|
}
|
|
700
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Registers a callback to be invoked when an oversized block proposal is stored as slashing evidence.
|
|
802
|
+
*/
|
|
803
|
+
public registerOversizedProposalCallback(callback: P2POversizedProposalCallback): void {
|
|
804
|
+
this.oversizedProposalCallback = callback;
|
|
805
|
+
}
|
|
806
|
+
|
|
701
807
|
/**
|
|
702
808
|
* Registers a callback to be invoked when a duplicate attestation is detected.
|
|
703
809
|
* A validator signing attestations for different proposals at the same slot.
|
|
@@ -707,6 +813,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
707
813
|
this.duplicateAttestationCallback = callback;
|
|
708
814
|
}
|
|
709
815
|
|
|
816
|
+
public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
|
|
817
|
+
this.checkpointAttestationCallback = callback;
|
|
818
|
+
}
|
|
819
|
+
|
|
710
820
|
/**
|
|
711
821
|
* Subscribes to a topic.
|
|
712
822
|
* @param topic - The topic to subscribe to.
|
|
@@ -835,6 +945,15 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
835
945
|
|
|
836
946
|
// Process the message, optionally within a linked span for trace propagation
|
|
837
947
|
const processMessage = async () => {
|
|
948
|
+
if (
|
|
949
|
+
this.config.skipIncomingProposals &&
|
|
950
|
+
(msg.topic === this.topicStrings[TopicType.block_proposal] ||
|
|
951
|
+
msg.topic === this.topicStrings[TopicType.checkpoint_proposal])
|
|
952
|
+
) {
|
|
953
|
+
this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, { topic: msg.topic });
|
|
954
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
838
957
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
839
958
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
840
959
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
@@ -899,6 +1018,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
899
1018
|
msgId: string,
|
|
900
1019
|
source: PeerId,
|
|
901
1020
|
topicType: TopicType,
|
|
1021
|
+
stageTimings?: Record<string, number>,
|
|
902
1022
|
): Promise<ReceivedMessageValidationResult<T, M>> {
|
|
903
1023
|
// Default to reject result with a penalty if validation function throws an error
|
|
904
1024
|
let resultAndObj: ReceivedMessageValidationResult<T, M> = {
|
|
@@ -912,8 +1032,25 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
912
1032
|
this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
|
|
913
1033
|
}
|
|
914
1034
|
|
|
1035
|
+
const validationTimeMs = timer.ms();
|
|
1036
|
+
const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
|
|
1037
|
+
if (validationTimeMs > mcacheWindowMs * 0.75) {
|
|
1038
|
+
this.instrumentation.incSlowValidation(topicType);
|
|
1039
|
+
this.logger.warn(
|
|
1040
|
+
`Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
|
|
1041
|
+
`Message forwarding may be skipped if validation exceeds the window.`,
|
|
1042
|
+
{ msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs, stageTimings },
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
915
1046
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
916
|
-
this.logger.debug(`Message ${topicType} accepted by validator`, {
|
|
1047
|
+
this.logger.debug(`Message ${topicType} accepted by validator`, {
|
|
1048
|
+
msgId,
|
|
1049
|
+
source: source.toString(),
|
|
1050
|
+
topicType,
|
|
1051
|
+
validationTimeMs,
|
|
1052
|
+
stageTimings,
|
|
1053
|
+
});
|
|
917
1054
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
918
1055
|
} else if (resultAndObj.result === TopicValidatorResult.Reject) {
|
|
919
1056
|
this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
|
|
@@ -945,18 +1082,38 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
945
1082
|
}
|
|
946
1083
|
|
|
947
1084
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
1085
|
+
// Per-stage timings so slow validations can be attributed to a specific stage (see A-1656).
|
|
1086
|
+
const stageTimings: Record<string, number> = {};
|
|
1087
|
+
const timed = async <T>(stage: string, fn: () => T | Promise<T>): Promise<T> => {
|
|
1088
|
+
const timer = new Timer();
|
|
1089
|
+
try {
|
|
1090
|
+
return await fn();
|
|
1091
|
+
} finally {
|
|
1092
|
+
const ms = timer.ms();
|
|
1093
|
+
stageTimings[stage] = Math.ceil(ms);
|
|
1094
|
+
this.instrumentation.recordTxValidationStage(stage, ms);
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
|
|
948
1098
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
949
|
-
const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
|
|
1099
|
+
const tx = await timed('deserialize', () => this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source));
|
|
950
1100
|
if (!tx) {
|
|
951
1101
|
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
|
|
952
1102
|
}
|
|
953
1103
|
|
|
954
|
-
|
|
955
|
-
const {
|
|
1104
|
+
// Stage 1 setup: chain tip, epoch info, gas fees, and L1 constants reads
|
|
1105
|
+
const { currentBlockNumber, firstStageValidators } = await timed('fast_validation_setup', async () => {
|
|
1106
|
+
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1107
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1108
|
+
const firstStageValidators = await this.createFirstStageMessageValidators(
|
|
1109
|
+
currentBlockNumber,
|
|
1110
|
+
nextSlotTimestamp,
|
|
1111
|
+
);
|
|
1112
|
+
return { currentBlockNumber, firstStageValidators };
|
|
1113
|
+
});
|
|
956
1114
|
|
|
957
1115
|
// Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
|
|
958
|
-
const
|
|
959
|
-
const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
|
|
1116
|
+
const firstStageOutcome = await timed('fast_validation', () => this.runValidations(tx, firstStageValidators));
|
|
960
1117
|
if (!firstStageOutcome.allPassed) {
|
|
961
1118
|
const { name } = firstStageOutcome.failure;
|
|
962
1119
|
let { severity } = firstStageOutcome.failure;
|
|
@@ -978,7 +1135,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
978
1135
|
}
|
|
979
1136
|
|
|
980
1137
|
// Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
|
|
981
|
-
const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
|
|
1138
|
+
const canAdd = await timed('pool_precheck', () => this.mempools.txPool.canAddPendingTx(tx));
|
|
982
1139
|
if (canAdd === 'ignored') {
|
|
983
1140
|
this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
|
|
984
1141
|
source: source.toString(),
|
|
@@ -988,7 +1145,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
988
1145
|
|
|
989
1146
|
// Stage 2: expensive proof verification
|
|
990
1147
|
const secondStageValidators = this.createSecondStageMessageValidators();
|
|
991
|
-
const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
|
|
1148
|
+
const secondStageOutcome = await timed('proof_verify', () => this.runValidations(tx, secondStageValidators));
|
|
992
1149
|
if (!secondStageOutcome.allPassed) {
|
|
993
1150
|
const { severity, name } = secondStageOutcome.failure;
|
|
994
1151
|
this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
|
|
@@ -1001,7 +1158,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1001
1158
|
|
|
1002
1159
|
// Pool add: persist the tx
|
|
1003
1160
|
const txHash = tx.getTxHash();
|
|
1004
|
-
const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
|
|
1161
|
+
const addResult = await timed('pool_add', () => this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' }));
|
|
1005
1162
|
|
|
1006
1163
|
const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
|
|
1007
1164
|
const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
|
|
@@ -1025,7 +1182,13 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1025
1182
|
}
|
|
1026
1183
|
};
|
|
1027
1184
|
|
|
1028
|
-
const { result, obj: tx } = await this.validateReceivedMessage<Tx>(
|
|
1185
|
+
const { result, obj: tx } = await this.validateReceivedMessage<Tx>(
|
|
1186
|
+
validationFunc,
|
|
1187
|
+
msgId,
|
|
1188
|
+
source,
|
|
1189
|
+
TopicType.tx,
|
|
1190
|
+
stageTimings,
|
|
1191
|
+
);
|
|
1029
1192
|
if (result !== TopicValidatorResult.Accept || !tx) {
|
|
1030
1193
|
return;
|
|
1031
1194
|
}
|
|
@@ -1147,6 +1310,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1147
1310
|
}
|
|
1148
1311
|
|
|
1149
1312
|
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1313
|
+
this.checkpointAttestationCallback?.(attestation);
|
|
1150
1314
|
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
1151
1315
|
}
|
|
1152
1316
|
|
|
@@ -1154,20 +1318,21 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1154
1318
|
const {
|
|
1155
1319
|
result,
|
|
1156
1320
|
obj: block,
|
|
1157
|
-
metadata: { isEquivocated } = {},
|
|
1158
|
-
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
|
|
1321
|
+
metadata: { isEquivocated, isOversized } = {},
|
|
1322
|
+
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
|
|
1159
1323
|
() => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
|
|
1160
1324
|
msgId,
|
|
1161
1325
|
source,
|
|
1162
1326
|
TopicType.block_proposal,
|
|
1163
1327
|
);
|
|
1164
1328
|
|
|
1165
|
-
// If not accepted or
|
|
1166
|
-
|
|
1329
|
+
// If not accepted, equivocated, or oversized, return. Oversized proposals are re-broadcast as
|
|
1330
|
+
// slashing evidence but never attested or processed.
|
|
1331
|
+
if (result !== TopicValidatorResult.Accept || !block || isEquivocated || isOversized) {
|
|
1167
1332
|
return;
|
|
1168
1333
|
}
|
|
1169
1334
|
|
|
1170
|
-
await this.processValidBlockProposal(block, source);
|
|
1335
|
+
await this.processValidBlockProposal(ValidatedBlockProposal(block), source);
|
|
1171
1336
|
}
|
|
1172
1337
|
|
|
1173
1338
|
/** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
|
|
@@ -1178,7 +1343,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1178
1343
|
protected async validateAndStoreBlockProposal(
|
|
1179
1344
|
peerId: PeerId,
|
|
1180
1345
|
block: BlockProposal,
|
|
1181
|
-
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
|
|
1346
|
+
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
|
|
1182
1347
|
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1183
1348
|
|
|
1184
1349
|
if (validationResult.result === 'reject') {
|
|
@@ -1193,6 +1358,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1193
1358
|
// Try to add the proposal: this handles existence check, cap check, and adding in one call
|
|
1194
1359
|
const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
|
|
1195
1360
|
const isEquivocated = count !== undefined && count > 1;
|
|
1361
|
+
// An oversized proposal (index at or beyond the consensus per-checkpoint limit) is structurally valid
|
|
1362
|
+
// proposer misbehavior: it is stored and re-broadcast as slashing evidence but never processed or
|
|
1363
|
+
// attested to. No-ops when maxBlocksPerCheckpoint is unset (local/test).
|
|
1364
|
+
const isOversized =
|
|
1365
|
+
this.config.maxBlocksPerCheckpoint !== undefined &&
|
|
1366
|
+
block.indexWithinCheckpoint >= this.config.maxBlocksPerCheckpoint;
|
|
1196
1367
|
|
|
1197
1368
|
// Duplicate proposal received, no need to re-broadcast
|
|
1198
1369
|
if (alreadyExists) {
|
|
@@ -1202,7 +1373,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1202
1373
|
proposer: block.getSender()?.toString(),
|
|
1203
1374
|
source: peerId.toString(),
|
|
1204
1375
|
});
|
|
1205
|
-
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
|
|
1376
|
+
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1206
1377
|
}
|
|
1207
1378
|
|
|
1208
1379
|
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
@@ -1216,11 +1387,27 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1216
1387
|
});
|
|
1217
1388
|
return {
|
|
1218
1389
|
result: TopicValidatorResult.Reject,
|
|
1219
|
-
metadata: { isEquivocated },
|
|
1390
|
+
metadata: { isEquivocated, isOversized },
|
|
1220
1391
|
severity: PeerErrorSeverity.HighToleranceError,
|
|
1221
1392
|
};
|
|
1222
1393
|
}
|
|
1223
1394
|
|
|
1395
|
+
// The proposal was stored: if oversized, invoke the oversized callback so the proposer can be
|
|
1396
|
+
// slashed. Fired alongside (not instead of) equivocation detection below.
|
|
1397
|
+
if (isOversized) {
|
|
1398
|
+
const proposer = block.getSender();
|
|
1399
|
+
if (proposer) {
|
|
1400
|
+
this.logger.warn(`Detected oversized block proposal at slot ${block.slotNumber}`, {
|
|
1401
|
+
...block.toBlockInfo(),
|
|
1402
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1403
|
+
maxBlocksPerCheckpoint: this.config.maxBlocksPerCheckpoint,
|
|
1404
|
+
source: peerId.toString(),
|
|
1405
|
+
proposer: proposer.toString(),
|
|
1406
|
+
});
|
|
1407
|
+
this.oversizedProposalCallback?.({ slot: block.slotNumber, proposer });
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1224
1411
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1225
1412
|
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1226
1413
|
if (isEquivocated) {
|
|
@@ -1234,11 +1421,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1234
1421
|
if (proposer && count === 2) {
|
|
1235
1422
|
this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
|
|
1236
1423
|
}
|
|
1237
|
-
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
|
|
1424
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1238
1425
|
}
|
|
1239
1426
|
|
|
1240
1427
|
// Otherwise, we're good to go!
|
|
1241
|
-
return { result: TopicValidatorResult.Accept, obj: block };
|
|
1428
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
|
|
1242
1429
|
}
|
|
1243
1430
|
|
|
1244
1431
|
// REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
|
|
@@ -1248,7 +1435,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1248
1435
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
1249
1436
|
[Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1250
1437
|
}))
|
|
1251
|
-
protected async processValidBlockProposal(block:
|
|
1438
|
+
protected async processValidBlockProposal(block: ValidatedBlockProposal, sender: PeerId) {
|
|
1252
1439
|
const slot = block.slotNumber;
|
|
1253
1440
|
this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
1254
1441
|
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
|
|
@@ -1259,11 +1446,29 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1259
1446
|
// Mark the txs in this proposal as protected
|
|
1260
1447
|
await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
|
|
1261
1448
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1449
|
+
if (!(await this.tryBlockReceivedCallback(block, sender))) {
|
|
1450
|
+
// Release the protections this proposal created so its txs return to pending. Only entries still
|
|
1451
|
+
// keyed to this slot are cleared, so a tx referenced by a live proposal at another slot stays protected.
|
|
1452
|
+
await this.mempools.txPool.unprotectTxs(block.txHashes, slot);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* Runs the block received callback to validate a proposal, and returns whether it was accepted.
|
|
1458
|
+
* A callback that throws is reported as a rejection: the proposal is no more usable than one explicitly
|
|
1459
|
+
* rejected, and letting the error escape would leave the proposal's tx protections in place.
|
|
1460
|
+
* Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
|
|
1461
|
+
*/
|
|
1462
|
+
private async tryBlockReceivedCallback(block: ValidatedBlockProposal, sender: PeerId): Promise<boolean> {
|
|
1463
|
+
try {
|
|
1464
|
+
const isValid = await this.blockReceivedCallback(block, sender);
|
|
1465
|
+
if (!isValid) {
|
|
1466
|
+
this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
|
|
1467
|
+
}
|
|
1468
|
+
return isValid;
|
|
1469
|
+
} catch (err) {
|
|
1470
|
+
this.logger.error(`Error validating block proposal for block ${block.blockNumber}`, err, block.toBlockInfo());
|
|
1471
|
+
return false;
|
|
1267
1472
|
}
|
|
1268
1473
|
}
|
|
1269
1474
|
|
|
@@ -1275,25 +1480,45 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1275
1480
|
const {
|
|
1276
1481
|
result,
|
|
1277
1482
|
obj: checkpoint,
|
|
1278
|
-
metadata: { isEquivocated, processBlock } = {},
|
|
1279
|
-
} = await this.validateReceivedMessage<
|
|
1483
|
+
metadata: { isEquivocated, processBlock, isOversized } = {},
|
|
1484
|
+
} = await this.validateReceivedMessage<
|
|
1485
|
+
CheckpointProposal,
|
|
1486
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1487
|
+
>(
|
|
1280
1488
|
() => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
|
|
1281
1489
|
msgId,
|
|
1282
1490
|
source,
|
|
1283
1491
|
TopicType.checkpoint_proposal,
|
|
1284
1492
|
);
|
|
1285
1493
|
|
|
1494
|
+
// An oversized checkpoint is re-broadcast as slashing evidence but never attested or processed.
|
|
1495
|
+
// Process checkpoint proposal if valid and neither equivocated nor oversized.
|
|
1496
|
+
const processCheckpointFn = () =>
|
|
1497
|
+
result === TopicValidatorResult.Accept && checkpoint && !isEquivocated && !isOversized
|
|
1498
|
+
? this.processValidCheckpointProposal(ValidatedCheckpointProposalCore(checkpoint.toCore()), source)
|
|
1499
|
+
: Promise.resolve();
|
|
1500
|
+
|
|
1286
1501
|
// If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
|
|
1287
1502
|
// TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1503
|
+
const processBlockFn = () =>
|
|
1504
|
+
processBlock && checkpoint && checkpoint.getBlockProposal()
|
|
1505
|
+
? this.processValidBlockProposal(ValidatedBlockProposal(checkpoint.getBlockProposal()!), source)
|
|
1506
|
+
: Promise.resolve();
|
|
1507
|
+
|
|
1508
|
+
// A node that skips checkpoint validation attests without re-executing the embedded last block, so run
|
|
1509
|
+
// the checkpoint callback first: this creates and broadcasts the attestation before the block is
|
|
1510
|
+
// processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
|
|
1511
|
+
// waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
|
|
1512
|
+
// window, after which peers reject it as stale.
|
|
1513
|
+
if (this.config.skipCheckpointProposalValidation) {
|
|
1514
|
+
await processCheckpointFn();
|
|
1515
|
+
await processBlockFn();
|
|
1293
1516
|
return;
|
|
1294
1517
|
}
|
|
1295
1518
|
|
|
1296
|
-
|
|
1519
|
+
// Process the block first, since it's required for the checkpoint proposal validation.
|
|
1520
|
+
await processBlockFn();
|
|
1521
|
+
await processCheckpointFn();
|
|
1297
1522
|
}
|
|
1298
1523
|
|
|
1299
1524
|
/**
|
|
@@ -1306,7 +1531,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1306
1531
|
protected async validateAndStoreCheckpointProposal(
|
|
1307
1532
|
peerId: PeerId,
|
|
1308
1533
|
checkpoint: CheckpointProposal,
|
|
1309
|
-
): Promise<
|
|
1534
|
+
): Promise<
|
|
1535
|
+
ReceivedMessageValidationResult<
|
|
1536
|
+
CheckpointProposal,
|
|
1537
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1538
|
+
>
|
|
1539
|
+
> {
|
|
1310
1540
|
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1311
1541
|
|
|
1312
1542
|
if (validationResult.result === 'reject') {
|
|
@@ -1321,13 +1551,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1321
1551
|
// Extract and try to add the block proposal first if present
|
|
1322
1552
|
const blockProposal = checkpoint.getBlockProposal();
|
|
1323
1553
|
let processBlock = false;
|
|
1554
|
+
let isOversized = false;
|
|
1324
1555
|
if (blockProposal) {
|
|
1325
1556
|
this.logger.debug(`Validating block proposal from propagated checkpoint`, {
|
|
1326
1557
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1327
1558
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1328
1559
|
});
|
|
1329
1560
|
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1330
|
-
const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
|
|
1561
|
+
const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
|
|
1562
|
+
isOversized = blockIsOversized ?? false;
|
|
1563
|
+
|
|
1331
1564
|
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1332
1565
|
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1333
1566
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
@@ -1340,7 +1573,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1340
1573
|
severity:
|
|
1341
1574
|
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1342
1575
|
};
|
|
1343
|
-
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
|
|
1576
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
|
|
1577
|
+
// An oversized terminal block is re-broadcast as slashing evidence but never processed.
|
|
1344
1578
|
processBlock = true;
|
|
1345
1579
|
}
|
|
1346
1580
|
}
|
|
@@ -1360,11 +1594,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1360
1594
|
return {
|
|
1361
1595
|
result: TopicValidatorResult.Ignore,
|
|
1362
1596
|
obj: checkpoint,
|
|
1363
|
-
metadata: { isEquivocated, processBlock },
|
|
1597
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1364
1598
|
};
|
|
1365
1599
|
}
|
|
1366
1600
|
|
|
1367
|
-
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
|
|
1601
|
+
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
|
|
1368
1602
|
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1369
1603
|
if (!added) {
|
|
1370
1604
|
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
@@ -1375,7 +1609,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1375
1609
|
return {
|
|
1376
1610
|
result: TopicValidatorResult.Reject,
|
|
1377
1611
|
obj: checkpoint,
|
|
1378
|
-
metadata: { isEquivocated, processBlock },
|
|
1612
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1379
1613
|
severity: PeerErrorSeverity.HighToleranceError,
|
|
1380
1614
|
};
|
|
1381
1615
|
}
|
|
@@ -1396,12 +1630,16 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1396
1630
|
return {
|
|
1397
1631
|
result: TopicValidatorResult.Accept,
|
|
1398
1632
|
obj: checkpoint,
|
|
1399
|
-
metadata: { isEquivocated, processBlock },
|
|
1633
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1400
1634
|
};
|
|
1401
1635
|
}
|
|
1402
1636
|
|
|
1403
1637
|
// Otherwise, we're good to go!
|
|
1404
|
-
return {
|
|
1638
|
+
return {
|
|
1639
|
+
result: TopicValidatorResult.Accept,
|
|
1640
|
+
obj: checkpoint,
|
|
1641
|
+
metadata: { processBlock, isEquivocated, isOversized },
|
|
1642
|
+
};
|
|
1405
1643
|
}
|
|
1406
1644
|
|
|
1407
1645
|
/**
|
|
@@ -1413,7 +1651,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1413
1651
|
[Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
|
|
1414
1652
|
[Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1415
1653
|
}))
|
|
1416
|
-
protected async processValidCheckpointProposal(checkpoint:
|
|
1654
|
+
protected async processValidCheckpointProposal(checkpoint: ValidatedCheckpointProposalCore, sender: PeerId) {
|
|
1417
1655
|
const slot = checkpoint.slotNumber;
|
|
1418
1656
|
this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
1419
1657
|
p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
|
|
@@ -1449,82 +1687,87 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1449
1687
|
}
|
|
1450
1688
|
|
|
1451
1689
|
/**
|
|
1452
|
-
* Validate the requested block transactions
|
|
1690
|
+
* Validate the requested block transactions request-response consistency.
|
|
1691
|
+
* It does NOT validate the transactions themselves.
|
|
1453
1692
|
* @param request - The block transactions request.
|
|
1454
1693
|
* @param response - The block transactions response.
|
|
1455
1694
|
* @param peerId - The ID of the peer that made the request.
|
|
1456
|
-
* @returns True if the
|
|
1695
|
+
* @returns True if the request-response is consistent, false otherwise.
|
|
1457
1696
|
*/
|
|
1458
|
-
@trackSpan('Libp2pService.
|
|
1697
|
+
@trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
|
|
1459
1698
|
[Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
|
|
1460
1699
|
}))
|
|
1461
|
-
protected async
|
|
1700
|
+
protected async validateRequestedBlockTxsConsistency(
|
|
1462
1701
|
request: BlockTxsRequest,
|
|
1463
1702
|
response: BlockTxsResponse,
|
|
1464
1703
|
peerId: PeerId,
|
|
1465
1704
|
): Promise<boolean> {
|
|
1466
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1467
|
-
|
|
1468
1705
|
try {
|
|
1469
|
-
|
|
1706
|
+
// Check for duplicates txs (or hashes) in the response.
|
|
1707
|
+
const returnedHashes = response.txs.map(tx => tx.getTxHash().toString());
|
|
1708
|
+
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1709
|
+
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1470
1710
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1471
|
-
throw new ValidationError(
|
|
1472
|
-
|
|
1711
|
+
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// We get the block tx hashes from the proposal or the archiver.
|
|
1715
|
+
const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
|
|
1716
|
+
const blockTxHashes =
|
|
1717
|
+
proposal?.txHashes ??
|
|
1718
|
+
(await this.archiver.getBlock({ archive: request.archiveRoot }))?.body.txEffects.map(e => e.txHash);
|
|
1719
|
+
|
|
1720
|
+
// If we don't have the block, we can't verify membership/order of the returned txs.
|
|
1721
|
+
if (!blockTxHashes) {
|
|
1722
|
+
// This shouldn't happen, since we asked for the block (proposal).
|
|
1723
|
+
// Neither a local proposal nor an archived block: we cannot verify membership/order of the
|
|
1724
|
+
// returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
|
|
1725
|
+
this.logger.warn(
|
|
1726
|
+
`Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`,
|
|
1473
1727
|
);
|
|
1728
|
+
// NOTE: We mark the response as invalid, given the limitations of a true/false return value.
|
|
1729
|
+
return false;
|
|
1474
1730
|
}
|
|
1475
1731
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1732
|
+
// Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
|
|
1733
|
+
const uniqueRequestedHashes = new Set([
|
|
1734
|
+
// tx hashes requested by index.
|
|
1735
|
+
...request.txIndices.getTrueIndices().map(i => blockTxHashes[i].toString()),
|
|
1736
|
+
// explicitly requested tx hashes.
|
|
1737
|
+
...request.txHashes.map(h => h.toString()),
|
|
1738
|
+
]);
|
|
1739
|
+
if (!returnedHashes.every(h => uniqueRequestedHashes.has(h))) {
|
|
1740
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1478
1741
|
throw new ValidationError(
|
|
1479
|
-
|
|
1742
|
+
'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
|
|
1480
1743
|
);
|
|
1481
1744
|
}
|
|
1482
1745
|
|
|
1483
|
-
//
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
|
|
1489
|
-
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1490
|
-
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1491
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1492
|
-
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1746
|
+
// Tx indices information is optional, but if present, we need to verify a few things.
|
|
1747
|
+
if (!response.peerHasBlock()) {
|
|
1748
|
+
this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
|
|
1749
|
+
return true;
|
|
1493
1750
|
}
|
|
1494
|
-
|
|
1751
|
+
|
|
1752
|
+
// If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
|
|
1753
|
+
if (response.txIndices.getLength() !== blockTxHashes.length) {
|
|
1495
1754
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1496
1755
|
throw new ValidationError(
|
|
1497
|
-
`Received
|
|
1756
|
+
`Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
|
|
1498
1757
|
);
|
|
1499
1758
|
}
|
|
1500
1759
|
|
|
1501
|
-
//
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
);
|
|
1511
|
-
const allowedIndexSet = new Set(intersectIdx);
|
|
1512
|
-
const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
|
|
1513
|
-
const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
|
|
1514
|
-
const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
|
|
1515
|
-
if (!allAllowed || !strictlyIncreasing) {
|
|
1516
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1517
|
-
throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
|
|
1518
|
-
}
|
|
1519
|
-
} else {
|
|
1520
|
-
// No local proposal, cannot check the membership/order of the returned txs
|
|
1521
|
-
this.logger.warn(
|
|
1522
|
-
`Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
|
|
1523
|
-
);
|
|
1524
|
-
return false;
|
|
1760
|
+
// For every index we requested that the peer claims to have (per its bitvector), the
|
|
1761
|
+
// corresponding tx must be present in the response. A peer advertising availability but
|
|
1762
|
+
// withholding the tx is inconsistent.
|
|
1763
|
+
const returnedHashSet = new Set(returnedHashes);
|
|
1764
|
+
const expectedIndices = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
|
|
1765
|
+
const missingExpected = expectedIndices.some(i => !returnedHashSet.has(blockTxHashes[i].toString()));
|
|
1766
|
+
if (missingExpected) {
|
|
1767
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1768
|
+
throw new ValidationError('Peer advertised requested txs via indices but did not return them');
|
|
1525
1769
|
}
|
|
1526
1770
|
|
|
1527
|
-
await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
|
|
1528
1771
|
return true;
|
|
1529
1772
|
} catch (e: any) {
|
|
1530
1773
|
if (e instanceof ValidationError) {
|
|
@@ -1537,125 +1780,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1537
1780
|
}
|
|
1538
1781
|
}
|
|
1539
1782
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
*
|
|
1543
|
-
* The core component of this validator is that each tx hash MUST match the requested tx hash,
|
|
1544
|
-
* In order to perform this check, the tx proof must be verified.
|
|
1545
|
-
*
|
|
1546
|
-
* Note: This function is called from within `ReqResp.sendRequest` as part of the
|
|
1547
|
-
* ReqRespSubProtocol.TX subprotocol validation.
|
|
1548
|
-
*
|
|
1549
|
-
* @param requestedTxHash - The collection of the txs that was requested.
|
|
1550
|
-
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
1551
|
-
* @param peerId - The peer ID of the peer that sent the tx.
|
|
1552
|
-
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
1553
|
-
*/
|
|
1554
|
-
@trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
|
|
1555
|
-
[Attributes.TX_HASH]: requestedTxHash.toString(),
|
|
1556
|
-
}))
|
|
1557
|
-
private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
|
|
1558
|
-
const requested = new Set(requestedTxHash.map(h => h.toString()));
|
|
1559
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1560
|
-
|
|
1561
|
-
//TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
|
|
1562
|
-
// I think we should still extract the valid txs and return them, so that we can still use the response.
|
|
1563
|
-
try {
|
|
1564
|
-
await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
|
|
1565
|
-
return true;
|
|
1566
|
-
} catch (e: any) {
|
|
1567
|
-
if (e instanceof ValidationError) {
|
|
1568
|
-
this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
|
|
1569
|
-
} else {
|
|
1570
|
-
this.logger.error(`Error during validation of requested txs`, e);
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
return false;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
/**
|
|
1578
|
-
* Validates a BLOCK response.
|
|
1579
|
-
*
|
|
1580
|
-
* If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
|
|
1581
|
-
* Penalizes on block number mismatch or hash mismatch.
|
|
1582
|
-
*
|
|
1583
|
-
* @param requestedBlockNumber - The requested block number.
|
|
1584
|
-
* @param responseBlock - The block returned by the peer.
|
|
1585
|
-
* @param peerId - The peer that returned the block.
|
|
1586
|
-
* @returns True if the response is valid, false otherwise.
|
|
1587
|
-
*/
|
|
1588
|
-
@trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
|
|
1589
|
-
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
|
|
1590
|
-
}))
|
|
1591
|
-
protected async validateRequestedBlock(
|
|
1592
|
-
requestedBlockNumber: Fr,
|
|
1593
|
-
responseBlock: L2Block,
|
|
1594
|
-
peerId: PeerId,
|
|
1595
|
-
): Promise<boolean> {
|
|
1596
|
-
try {
|
|
1597
|
-
const reqNum = Number(requestedBlockNumber.toString());
|
|
1598
|
-
if (responseBlock.number !== reqNum) {
|
|
1599
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1600
|
-
return false;
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
const local = await this.archiver.getBlock(BlockNumber(reqNum));
|
|
1604
|
-
if (!local) {
|
|
1605
|
-
// We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
|
|
1606
|
-
// TODO: Consider extending this validator to accept an expected hash or
|
|
1607
|
-
// performing quorum-based checks when using P2P syncing prior to L1 sync.
|
|
1608
|
-
this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
|
|
1609
|
-
return false;
|
|
1610
|
-
}
|
|
1611
|
-
const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
|
|
1612
|
-
if (!localHash.equals(respHash)) {
|
|
1613
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1614
|
-
return false;
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
return true;
|
|
1618
|
-
} catch (e) {
|
|
1619
|
-
this.logger.warn(`Error validating requested block`, e);
|
|
1620
|
-
return false;
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
protected async validateRequestedTx(
|
|
1625
|
-
tx: Tx,
|
|
1626
|
-
peerId: PeerId,
|
|
1627
|
-
txValidator: TxValidator,
|
|
1628
|
-
requested?: Set<`0x${string}`>,
|
|
1629
|
-
) {
|
|
1630
|
-
const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
|
|
1631
|
-
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1632
|
-
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1633
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
const { result } = await txValidator.validateTx(tx);
|
|
1637
|
-
if (result === 'invalid') {
|
|
1638
|
-
penalize(PeerErrorSeverity.LowToleranceError);
|
|
1639
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
protected createRequestedTxValidator(): TxValidator {
|
|
1644
|
-
return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
|
|
1645
|
-
l1ChainId: this.config.l1ChainId,
|
|
1646
|
-
rollupVersion: this.config.rollupVersion,
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
1651
|
-
if (blockNumber === this.feesCache?.blockNumber) {
|
|
1652
|
-
return this.feesCache.gasFees;
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
const header = await this.archiver.getBlockHeader(blockNumber);
|
|
1656
|
-
const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
|
|
1657
|
-
this.feesCache = { blockNumber, gasFees };
|
|
1658
|
-
return gasFees;
|
|
1783
|
+
private getGasFees(): Promise<GasFees> {
|
|
1784
|
+
return this.blockMinFeesProvider.getCurrentMinFees();
|
|
1659
1785
|
}
|
|
1660
1786
|
|
|
1661
1787
|
/**
|
|
@@ -1669,8 +1795,10 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1669
1795
|
l1ChainId: this.config.l1ChainId,
|
|
1670
1796
|
rollupVersion: this.config.rollupVersion,
|
|
1671
1797
|
proofVerifier: this.proofVerifier,
|
|
1798
|
+
txValidationCache: this.txValidationCache,
|
|
1672
1799
|
},
|
|
1673
1800
|
peerScoring: this.peerManager,
|
|
1801
|
+
validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
|
|
1674
1802
|
};
|
|
1675
1803
|
}
|
|
1676
1804
|
|
|
@@ -1679,16 +1807,19 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1679
1807
|
this.proofVerifier,
|
|
1680
1808
|
{ l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
|
|
1681
1809
|
this.logger.getBindings(),
|
|
1810
|
+
this.txValidationCache,
|
|
1682
1811
|
);
|
|
1683
1812
|
|
|
1684
1813
|
const results = await Promise.all(
|
|
1685
|
-
txs.map(async tx => {
|
|
1686
|
-
const result = await validator.validateTx(tx);
|
|
1687
|
-
return result.result !== 'invalid';
|
|
1688
|
-
}),
|
|
1814
|
+
txs.map(async tx => ({ txHash: tx.getTxHash(), result: await validator.validateTx(tx) })),
|
|
1689
1815
|
);
|
|
1690
|
-
|
|
1691
|
-
|
|
1816
|
+
|
|
1817
|
+
const invalidTxs = results.flatMap(({ txHash, result }) =>
|
|
1818
|
+
result.result === 'invalid' ? [{ txHash, reasons: result.reason }] : [],
|
|
1819
|
+
);
|
|
1820
|
+
|
|
1821
|
+
if (invalidTxs.length > 0) {
|
|
1822
|
+
throw new InvalidBlockProposalTxsError(invalidTxs);
|
|
1692
1823
|
}
|
|
1693
1824
|
}
|
|
1694
1825
|
|
|
@@ -1697,13 +1828,14 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1697
1828
|
currentBlockNumber: BlockNumber,
|
|
1698
1829
|
nextSlotTimestamp: UInt64,
|
|
1699
1830
|
): Promise<Record<string, TransactionValidator>> {
|
|
1700
|
-
const gasFees = await this.getGasFees(
|
|
1831
|
+
const gasFees = await this.getGasFees();
|
|
1701
1832
|
const allowedInSetup = [
|
|
1702
1833
|
...(await getDefaultAllowedSetupFunctions()),
|
|
1703
1834
|
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1704
1835
|
];
|
|
1705
1836
|
const blockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1706
1837
|
const l1Constants = await this.archiver.getL1Constants();
|
|
1838
|
+
const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
|
|
1707
1839
|
|
|
1708
1840
|
return createFirstStageTxValidationsForGossipedTransactions(
|
|
1709
1841
|
nextSlotTimestamp,
|
|
@@ -1718,9 +1850,8 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
1718
1850
|
allowedInSetup,
|
|
1719
1851
|
this.logger.getBindings(),
|
|
1720
1852
|
{
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1853
|
+
maxTxL2Gas: networkTxGasLimits.l2Gas,
|
|
1854
|
+
maxTxDAGas: networkTxGasLimits.daGas,
|
|
1724
1855
|
},
|
|
1725
1856
|
);
|
|
1726
1857
|
}
|