@aztec/p2p 0.0.1-commit.b655e406 → 0.0.1-commit.b8a057fa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -3
- package/dest/bootstrap/bootstrap.d.ts +4 -3
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +26 -10
- package/dest/client/factory.d.ts +13 -12
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +70 -21
- package/dest/client/index.d.ts +1 -1
- package/dest/client/interface.d.ts +104 -39
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +76 -92
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +685 -344
- package/dest/config.d.ts +175 -87
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +180 -59
- package/dest/enr/generate-enr.d.ts +1 -1
- package/dest/enr/index.d.ts +1 -1
- package/dest/errors/attestation-pool.error.d.ts +7 -0
- package/dest/errors/attestation-pool.error.d.ts.map +1 -0
- package/dest/errors/attestation-pool.error.js +12 -0
- package/dest/errors/p2p-service.error.d.ts +9 -0
- package/dest/errors/p2p-service.error.d.ts.map +1 -0
- package/dest/errors/p2p-service.error.js +10 -0
- package/dest/errors/reqresp.error.d.ts +9 -16
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/errors/reqresp.error.js +10 -17
- package/dest/errors/tx-pool.error.d.ts +8 -0
- package/dest/errors/tx-pool.error.d.ts.map +1 -0
- package/dest/errors/tx-pool.error.js +9 -0
- package/dest/index.d.ts +2 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +156 -57
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +518 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
- 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 +694 -294
- package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
- package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/index.js +1 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts +234 -10
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +19 -13
- package/dest/mem_pools/index.d.ts +3 -3
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/index.js +1 -1
- package/dest/mem_pools/instrumentation.d.ts +16 -1
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +58 -10
- package/dest/mem_pools/interface.d.ts +6 -7
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/archive/index.d.ts +2 -0
- package/dest/mem_pools/tx_pool_v2/archive/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +39 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +97 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +47 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +128 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +94 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +19 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +97 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +11 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +12 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +180 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +25 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +65 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +93 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +78 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +20 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +75 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +19 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts +6 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/index.js +5 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +243 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.js +11 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +137 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +223 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +122 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +368 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +68 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +178 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +91 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +1013 -0
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +15 -7
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +82 -26
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +24 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +75 -0
- package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
- package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/index.js +1 -0
- package/dest/msg_validators/index.d.ts +2 -2
- package/dest/msg_validators/index.d.ts.map +1 -1
- package/dest/msg_validators/index.js +1 -1
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +1 -1
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +16 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +14 -0
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +16 -0
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +26 -0
- package/dest/msg_validators/proposal_validator/index.d.ts +4 -0
- package/dest/msg_validators/proposal_validator/index.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/index.js +3 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +40 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.js +186 -0
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +11 -18
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +25 -21
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
- package/dest/msg_validators/tx_validator/archive_cache.d.ts +3 -3
- package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +20 -6
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +4 -3
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
- package/dest/msg_validators/tx_validator/data_validator.d.ts +4 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +40 -3
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +15 -4
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.js +7 -6
- package/dest/msg_validators/tx_validator/factory.d.ts +138 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +268 -58
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +10 -0
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/fee_payer_balance.js +24 -0
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +105 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +146 -67
- package/dest/msg_validators/tx_validator/index.d.ts +6 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +5 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +4 -3
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +6 -6
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +24 -3
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +78 -28
- package/dest/msg_validators/tx_validator/size_validator.d.ts +8 -0
- package/dest/msg_validators/tx_validator/size_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/size_validator.js +23 -0
- package/dest/msg_validators/tx_validator/test_utils.d.ts +2 -2
- package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +23 -5
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.js +8 -8
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +3 -2
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_permitted_validator.js +2 -2
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +4 -2
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +4 -2
- 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 +14 -10
- 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 +120 -24
- 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 +40 -16
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +61 -14
- package/dest/services/encoding.d.ts +24 -7
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +48 -30
- package/dest/services/gossipsub/index.d.ts +3 -0
- package/dest/services/gossipsub/index.d.ts.map +1 -0
- package/dest/services/gossipsub/index.js +2 -0
- package/dest/services/gossipsub/scoring.d.ts +21 -3
- package/dest/services/gossipsub/scoring.d.ts.map +1 -1
- package/dest/services/gossipsub/scoring.js +24 -7
- package/dest/services/gossipsub/topic_score_params.d.ts +167 -0
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
- package/dest/services/gossipsub/topic_score_params.js +336 -0
- package/dest/services/index.d.ts +2 -1
- package/dest/services/index.d.ts.map +1 -1
- package/dest/services/index.js +1 -0
- package/dest/services/libp2p/instrumentation.d.ts +5 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +49 -70
- package/dest/services/libp2p/libp2p_service.d.ts +127 -110
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +1385 -447
- package/dest/services/peer-manager/interface.d.ts +1 -1
- package/dest/services/peer-manager/metrics.d.ts +11 -2
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +45 -16
- package/dest/services/peer-manager/peer_manager.d.ts +21 -35
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +89 -38
- package/dest/services/peer-manager/peer_scoring.d.ts +39 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +168 -15
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +44 -0
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -0
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +534 -0
- package/dest/services/reqresp/batch-tx-requester/config.d.ts +17 -0
- package/dest/services/reqresp/batch-tx-requester/config.d.ts.map +1 -0
- package/dest/services/reqresp/batch-tx-requester/config.js +27 -0
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +50 -0
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -0
- package/dest/services/reqresp/batch-tx-requester/interface.js +1 -0
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +35 -0
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -0
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +136 -0
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +62 -0
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -0
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +176 -0
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +11 -0
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -0
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +7 -0
- 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/connection-sampler/connection_sampler.d.ts +2 -4
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.js +12 -0
- package/dest/services/reqresp/constants.d.ts +12 -0
- package/dest/services/reqresp/constants.d.ts.map +1 -0
- package/dest/services/reqresp/constants.js +7 -0
- package/dest/services/reqresp/index.d.ts +1 -1
- package/dest/services/reqresp/interface.d.ts +27 -18
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +23 -19
- package/dest/services/reqresp/metrics.d.ts +6 -5
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/metrics.js +16 -21
- package/dest/services/reqresp/protocols/auth.d.ts +2 -2
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/auth.js +2 -2
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +8 -3
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +15 -2
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
- 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 +35 -14
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +37 -11
- 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 +85 -21
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +1 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts +1 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.d.ts +1 -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/ping.d.ts +1 -1
- package/dest/services/reqresp/protocols/status.d.ts +6 -5
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.js +7 -3
- package/dest/services/reqresp/protocols/tx.d.ts +8 -3
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +21 -3
- package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +6 -5
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -13
- package/dest/services/reqresp/reqresp.d.ts +12 -69
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +480 -200
- package/dest/services/reqresp/status.d.ts +2 -2
- package/dest/services/reqresp/status.d.ts.map +1 -1
- package/dest/services/service.d.ts +78 -13
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +11 -11
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +28 -27
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +37 -0
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_collection.js +127 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts +38 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_source.js +100 -0
- package/dest/services/tx_collection/index.d.ts +3 -2
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +1 -0
- 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 +9 -13
- package/dest/services/tx_collection/request_tracker.d.ts +53 -0
- package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
- package/dest/services/tx_collection/request_tracker.js +84 -0
- package/dest/services/tx_collection/tx_collection.d.ts +51 -57
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +298 -70
- package/dest/services/tx_collection/tx_collection_sink.d.ts +20 -10
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +28 -31
- package/dest/services/tx_collection/tx_source.d.ts +13 -7
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +29 -7
- package/dest/services/tx_file_store/config.d.ts +16 -0
- package/dest/services/tx_file_store/config.d.ts.map +1 -0
- package/dest/services/tx_file_store/config.js +22 -0
- package/dest/services/tx_file_store/index.d.ts +4 -0
- package/dest/services/tx_file_store/index.d.ts.map +1 -0
- package/dest/services/tx_file_store/index.js +3 -0
- package/dest/services/tx_file_store/instrumentation.d.ts +15 -0
- package/dest/services/tx_file_store/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_file_store/instrumentation.js +29 -0
- package/dest/services/tx_file_store/tx_file_store.d.ts +46 -0
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -0
- package/dest/services/tx_file_store/tx_file_store.js +142 -0
- package/dest/services/tx_provider.d.ts +9 -5
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +23 -10
- package/dest/services/tx_provider_instrumentation.d.ts +5 -2
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
- package/dest/services/tx_provider_instrumentation.js +14 -14
- package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
- package/dest/test-helpers/get-ports.d.ts +1 -1
- package/dest/test-helpers/get-ports.d.ts.map +1 -1
- package/dest/test-helpers/index.d.ts +3 -1
- package/dest/test-helpers/index.d.ts.map +1 -1
- package/dest/test-helpers/index.js +2 -0
- package/dest/test-helpers/make-enrs.d.ts +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +5 -3
- package/dest/test-helpers/mock-pubsub.d.ts +48 -8
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +115 -14
- package/dest/test-helpers/mock-tx-helpers.d.ts +2 -2
- package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -1
- package/dest/test-helpers/mock-tx-helpers.js +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts +5 -7
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +19 -20
- package/dest/test-helpers/test_tx_provider.d.ts +42 -0
- package/dest/test-helpers/test_tx_provider.d.ts.map +1 -0
- package/dest/test-helpers/test_tx_provider.js +44 -0
- package/dest/test-helpers/testbench-utils.d.ts +164 -0
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -0
- package/dest/test-helpers/testbench-utils.js +400 -0
- package/dest/testbench/p2p_client_testbench_worker.d.ts +26 -2
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +289 -128
- package/dest/testbench/parse_log_file.d.ts +1 -1
- package/dest/testbench/testbench.d.ts +1 -1
- package/dest/testbench/worker_client_manager.d.ts +57 -6
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +277 -44
- package/dest/types/index.d.ts +1 -1
- package/dest/util.d.ts +14 -8
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +41 -11
- package/dest/versioning.d.ts +3 -6
- package/dest/versioning.d.ts.map +1 -1
- package/dest/versioning.js +3 -24
- package/package.json +21 -20
- package/src/bootstrap/bootstrap.ts +24 -7
- package/src/client/factory.ts +141 -46
- package/src/client/interface.ts +114 -47
- package/src/client/p2p_client.ts +356 -424
- package/src/client/test/p2p_client.batch_tx_requester.bench.README.md +197 -0
- package/src/config.ts +316 -67
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/errors/p2p-service.error.ts +11 -0
- package/src/errors/reqresp.error.ts +13 -19
- package/src/errors/tx-pool.error.ts +12 -0
- package/src/index.ts +1 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +598 -56
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +796 -316
- package/src/mem_pools/attestation_pool/index.ts +9 -2
- package/src/mem_pools/attestation_pool/mocks.ts +29 -17
- package/src/mem_pools/index.ts +2 -2
- package/src/mem_pools/instrumentation.ts +58 -10
- package/src/mem_pools/interface.ts +5 -7
- package/src/mem_pools/tx_pool_v2/README.md +283 -0
- package/src/mem_pools/tx_pool_v2/archive/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +112 -0
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +160 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +122 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +125 -0
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +28 -0
- package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +219 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +74 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +101 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +91 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +99 -0
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +32 -0
- package/src/mem_pools/tx_pool_v2/index.ts +12 -0
- package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +274 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +349 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +465 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +252 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +1215 -0
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +78 -33
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +102 -0
- package/src/msg_validators/attestation_validator/index.ts +1 -0
- package/src/msg_validators/index.ts +1 -1
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +32 -0
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +49 -0
- package/src/msg_validators/proposal_validator/index.ts +3 -0
- package/src/msg_validators/proposal_validator/proposal_validator.ts +198 -0
- package/src/msg_validators/tx_validator/README.md +129 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +8 -17
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
- package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
- package/src/msg_validators/tx_validator/archive_cache.ts +2 -2
- package/src/msg_validators/tx_validator/block_header_validator.ts +21 -8
- package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/data_validator.ts +62 -7
- package/src/msg_validators/tx_validator/double_spend_validator.ts +15 -9
- package/src/msg_validators/tx_validator/factory.ts +433 -58
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +44 -0
- package/src/msg_validators/tx_validator/gas_validator.ts +206 -76
- package/src/msg_validators/tx_validator/index.ts +5 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +31 -12
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +90 -31
- package/src/msg_validators/tx_validator/size_validator.ts +22 -0
- package/src/msg_validators/tx_validator/test_utils.ts +1 -1
- package/src/msg_validators/tx_validator/timestamp_validator.ts +30 -19
- package/src/msg_validators/tx_validator/tx_permitted_validator.ts +8 -3
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +10 -3
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
- package/src/services/data_store.ts +14 -19
- package/src/services/discv5/discV5_service.ts +127 -15
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +82 -38
- package/src/services/encoding.ts +46 -29
- package/src/services/gossipsub/README.md +663 -0
- package/src/services/gossipsub/index.ts +2 -0
- package/src/services/gossipsub/scoring.ts +29 -5
- package/src/services/gossipsub/topic_score_params.ts +478 -0
- package/src/services/index.ts +1 -0
- package/src/services/libp2p/instrumentation.ts +54 -72
- package/src/services/libp2p/libp2p_service.ts +1152 -497
- package/src/services/peer-manager/metrics.ts +51 -16
- package/src/services/peer-manager/peer_manager.ts +98 -29
- package/src/services/peer-manager/peer_scoring.ts +170 -9
- package/src/services/reqresp/README.md +194 -0
- package/src/services/reqresp/batch-tx-requester/README.md +344 -0
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +660 -0
- package/src/services/reqresp/batch-tx-requester/config.ts +40 -0
- package/src/services/reqresp/batch-tx-requester/interface.ts +61 -0
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +168 -0
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +249 -0
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +24 -0
- package/src/services/reqresp/config.ts +19 -2
- package/src/services/reqresp/connection-sampler/connection_sampler.ts +19 -1
- package/src/services/reqresp/constants.ts +14 -0
- package/src/services/reqresp/interface.ts +48 -46
- package/src/services/reqresp/metrics.ts +35 -27
- package/src/services/reqresp/protocols/auth.ts +2 -2
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +19 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +44 -17
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +109 -17
- package/src/services/reqresp/protocols/index.ts +0 -1
- package/src/services/reqresp/protocols/status.ts +16 -12
- package/src/services/reqresp/protocols/tx.ts +24 -5
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
- package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
- package/src/services/reqresp/reqresp.ts +107 -223
- package/src/services/service.ts +102 -31
- package/src/services/tx_collection/config.ts +42 -37
- package/src/services/tx_collection/file_store_tx_collection.ts +153 -0
- package/src/services/tx_collection/file_store_tx_source.ts +129 -0
- package/src/services/tx_collection/index.ts +2 -1
- package/src/services/tx_collection/instrumentation.ts +5 -13
- package/src/services/tx_collection/request_tracker.ts +127 -0
- package/src/services/tx_collection/tx_collection.ts +368 -115
- package/src/services/tx_collection/tx_collection_sink.ts +32 -36
- package/src/services/tx_collection/tx_source.ts +29 -8
- package/src/services/tx_file_store/config.ts +37 -0
- package/src/services/tx_file_store/index.ts +3 -0
- package/src/services/tx_file_store/instrumentation.ts +36 -0
- package/src/services/tx_file_store/tx_file_store.ts +163 -0
- package/src/services/tx_provider.ts +34 -12
- package/src/services/tx_provider_instrumentation.ts +24 -14
- package/src/test-helpers/index.ts +2 -0
- package/src/test-helpers/make-test-p2p-clients.ts +7 -6
- package/src/test-helpers/mock-pubsub.ts +138 -15
- package/src/test-helpers/mock-tx-helpers.ts +1 -1
- package/src/test-helpers/reqresp-nodes.ts +18 -30
- package/src/test-helpers/test_tx_provider.ts +69 -0
- package/src/test-helpers/testbench-utils.ts +465 -0
- package/src/testbench/p2p_client_testbench_worker.ts +406 -127
- package/src/testbench/worker_client_manager.ts +355 -46
- package/src/util.ts +47 -16
- package/src/versioning.ts +3 -33
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -30
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -190
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -25
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -197
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -150
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -617
- package/dest/mem_pools/tx_pool/index.d.ts +0 -4
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.js +0 -3
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -80
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +0 -238
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -8
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.js +0 -10
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -122
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -394
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -12
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +0 -70
- package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
- package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
- package/dest/msg_validators/block_proposal_validator/index.js +0 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -45
- 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 -92
- 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 -31
- package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -56
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/fast_tx_collection.js +0 -300
- package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -54
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/slow_tx_collection.js +0 -176
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -256
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -253
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -750
- package/src/mem_pools/tx_pool/index.ts +0 -3
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +0 -283
- package/src/mem_pools/tx_pool/priority.ts +0 -13
- package/src/mem_pools/tx_pool/tx_pool.ts +0 -135
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -313
- package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +0 -81
- package/src/msg_validators/block_proposal_validator/index.ts +0 -1
- package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -100
- package/src/services/reqresp/protocols/block.ts +0 -36
- package/src/services/tx_collection/fast_tx_collection.ts +0 -340
- package/src/services/tx_collection/slow_tx_collection.ts +0 -232
|
@@ -1,34 +1,43 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
|
|
4
4
|
import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
5
|
-
import { SerialQueue } from '@aztec/foundation/queue';
|
|
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
|
-
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
10
8
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
11
9
|
import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
|
|
10
|
+
import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/stdlib/config';
|
|
12
11
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
13
|
-
import { GasFees } from '@aztec/stdlib/gas';
|
|
12
|
+
import { type BlockMinFeesProvider, GasFees, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
|
|
14
13
|
import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
15
14
|
import {
|
|
16
|
-
BlockAttestation,
|
|
17
15
|
BlockProposal,
|
|
16
|
+
CheckpointAttestation,
|
|
17
|
+
CheckpointProposal,
|
|
18
|
+
type CheckpointProposalCore,
|
|
18
19
|
type Gossipable,
|
|
19
|
-
P2PClientType,
|
|
20
20
|
P2PMessage,
|
|
21
21
|
PeerErrorSeverity,
|
|
22
|
+
PeerErrorSeverityByHarshness,
|
|
22
23
|
TopicType,
|
|
23
24
|
createTopicString,
|
|
24
|
-
|
|
25
|
+
getTopicsForConfig,
|
|
25
26
|
metricsTopicStrToLabels,
|
|
26
27
|
} from '@aztec/stdlib/p2p';
|
|
28
|
+
import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
|
|
27
29
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
28
|
-
import { Tx, type
|
|
30
|
+
import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
|
|
29
31
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
30
32
|
import { compressComponentVersions } from '@aztec/stdlib/versioning';
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
Attributes,
|
|
35
|
+
OtelMetricsAdapter,
|
|
36
|
+
SpanStatusCode,
|
|
37
|
+
type TelemetryClient,
|
|
38
|
+
WithTracer,
|
|
39
|
+
trackSpan,
|
|
40
|
+
} from '@aztec/telemetry-client';
|
|
32
41
|
|
|
33
42
|
import {
|
|
34
43
|
type GossipSub,
|
|
@@ -36,69 +45,99 @@ import {
|
|
|
36
45
|
type GossipsubMessage,
|
|
37
46
|
gossipsub,
|
|
38
47
|
} from '@chainsafe/libp2p-gossipsub';
|
|
39
|
-
import { createPeerScoreParams
|
|
48
|
+
import { createPeerScoreParams } from '@chainsafe/libp2p-gossipsub/score';
|
|
40
49
|
import { SignaturePolicy } from '@chainsafe/libp2p-gossipsub/types';
|
|
41
50
|
import { noise } from '@chainsafe/libp2p-noise';
|
|
42
51
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
43
52
|
import { bootstrap } from '@libp2p/bootstrap';
|
|
44
53
|
import { identify } from '@libp2p/identify';
|
|
45
54
|
import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
46
|
-
import type { ConnectionManager } from '@libp2p/interface-internal';
|
|
55
|
+
import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
|
|
47
56
|
import { mplex } from '@libp2p/mplex';
|
|
48
57
|
import { tcp } from '@libp2p/tcp';
|
|
58
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
|
49
59
|
import { ENR } from '@nethermindeth/enr';
|
|
50
60
|
import { createLibp2p } from 'libp2p';
|
|
51
61
|
|
|
52
62
|
import type { P2PConfig } from '../../config.js';
|
|
63
|
+
import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
|
|
53
64
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
54
|
-
import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
|
|
55
|
-
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
56
|
-
import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
|
|
57
|
-
import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
|
|
58
65
|
import {
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
BlockProposalValidator,
|
|
67
|
+
CheckpointAttestationValidator,
|
|
68
|
+
CheckpointProposalValidator,
|
|
61
69
|
DoubleSpendTxValidator,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
} from '../../msg_validators/
|
|
70
|
+
FishermanAttestationValidator,
|
|
71
|
+
getDefaultAllowedSetupFunctions,
|
|
72
|
+
} from '../../msg_validators/index.js';
|
|
73
|
+
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
74
|
+
import {
|
|
75
|
+
type TransactionValidator,
|
|
76
|
+
createFirstStageTxValidationsForGossipedTransactions,
|
|
77
|
+
createSecondStageTxValidationsForGossipedTransactions,
|
|
78
|
+
createTxValidatorForBlockProposalReceivedTxs,
|
|
79
|
+
} from '../../msg_validators/tx_validator/factory.js';
|
|
80
|
+
import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
|
|
65
81
|
import { GossipSubEvent } from '../../types/index.js';
|
|
66
82
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
67
83
|
import { getVersions } from '../../versioning.js';
|
|
68
84
|
import { AztecDatastore } from '../data_store.js';
|
|
69
85
|
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
70
|
-
import { SnappyTransform,
|
|
71
|
-
import { gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
86
|
+
import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
87
|
+
import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
88
|
+
import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
|
|
72
89
|
import type { PeerManagerInterface } from '../peer-manager/interface.js';
|
|
73
90
|
import { PeerManager } from '../peer-manager/peer_manager.js';
|
|
74
91
|
import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
75
|
-
import type {
|
|
92
|
+
import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
|
|
93
|
+
import { type P2PReqRespConfig, YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
|
|
76
94
|
import {
|
|
77
|
-
|
|
95
|
+
AuthRequest,
|
|
96
|
+
BlockTxsRequest,
|
|
97
|
+
BlockTxsResponse,
|
|
78
98
|
type ReqRespInterface,
|
|
99
|
+
type ReqRespResponse,
|
|
79
100
|
ReqRespSubProtocol,
|
|
80
101
|
type ReqRespSubProtocolHandler,
|
|
81
102
|
type ReqRespSubProtocolHandlers,
|
|
82
|
-
type ReqRespSubProtocolValidators,
|
|
83
|
-
type SubProtocolMap,
|
|
84
|
-
ValidationError,
|
|
85
|
-
} from '../reqresp/interface.js';
|
|
86
|
-
import { reqRespBlockTxsHandler } from '../reqresp/protocols/block_txs/block_txs_handler.js';
|
|
87
|
-
import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
|
|
88
|
-
import {
|
|
89
|
-
AuthRequest,
|
|
90
|
-
BlockTxsRequest,
|
|
91
|
-
BlockTxsResponse,
|
|
92
103
|
StatusMessage,
|
|
104
|
+
ValidationError,
|
|
93
105
|
pingHandler,
|
|
94
|
-
|
|
106
|
+
reqGoodbyeHandler,
|
|
107
|
+
reqRespBlockTxsHandler,
|
|
95
108
|
reqRespStatusHandler,
|
|
96
109
|
reqRespTxHandler,
|
|
97
|
-
} from '../reqresp/
|
|
110
|
+
} from '../reqresp/index.js';
|
|
98
111
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
99
|
-
import type {
|
|
112
|
+
import type {
|
|
113
|
+
P2PBlockReceivedCallback,
|
|
114
|
+
P2PCheckpointAttestationCallback,
|
|
115
|
+
P2PCheckpointReceivedCallback,
|
|
116
|
+
P2PDuplicateAttestationCallback,
|
|
117
|
+
P2POversizedProposalCallback,
|
|
118
|
+
P2PService,
|
|
119
|
+
PeerDiscoveryService,
|
|
120
|
+
} from '../service.js';
|
|
100
121
|
import { P2PInstrumentation } from './instrumentation.js';
|
|
101
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Builds the {@link ConsensusTimetable} shared by the gossip validators for proposal/attestation receive-window
|
|
125
|
+
* bounds. Derived purely from protocol slot-timing constants plus the block sub-slot duration and the consensus
|
|
126
|
+
* materialization grace, so every node agrees on these bounds without depending on proposer operational budgets.
|
|
127
|
+
*/
|
|
128
|
+
function buildConsensusTimetable(
|
|
129
|
+
config: P2PConfig,
|
|
130
|
+
l1Constants: ReturnType<EpochCacheInterface['getL1Constants']>,
|
|
131
|
+
): ConsensusTimetable {
|
|
132
|
+
const blockDuration = config.blockDurationMs / 1000;
|
|
133
|
+
return new ConsensusTimetable({
|
|
134
|
+
l1Constants,
|
|
135
|
+
blockDuration,
|
|
136
|
+
checkpointProposalSyncGrace:
|
|
137
|
+
config.checkpointProposalSyncGraceSeconds ?? getDefaultCheckpointProposalSyncGrace(blockDuration),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
102
141
|
interface ValidationResult {
|
|
103
142
|
name: string;
|
|
104
143
|
isValid: TxValidationResult;
|
|
@@ -108,26 +147,40 @@ interface ValidationResult {
|
|
|
108
147
|
type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
|
|
109
148
|
|
|
110
149
|
// REFACTOR: Unify with the type above
|
|
111
|
-
type ReceivedMessageValidationResult<T> =
|
|
112
|
-
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject
|
|
113
|
-
| { obj?:
|
|
150
|
+
type ReceivedMessageValidationResult<T, M = undefined> =
|
|
151
|
+
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
|
|
152
|
+
| { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
|
|
114
153
|
|
|
115
154
|
/**
|
|
116
155
|
* Lib P2P implementation of the P2PService interface.
|
|
117
156
|
*/
|
|
118
|
-
export class LibP2PService
|
|
119
|
-
private jobQueue: SerialQueue = new SerialQueue();
|
|
157
|
+
export class LibP2PService extends WithTracer implements P2PService {
|
|
120
158
|
private discoveryRunningPromise?: RunningPromise;
|
|
121
159
|
private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
|
|
122
160
|
|
|
123
161
|
// Message validators
|
|
124
|
-
private attestationValidator: AttestationValidator;
|
|
125
162
|
private blockProposalValidator: BlockProposalValidator;
|
|
163
|
+
private checkpointProposalValidator: CheckpointProposalValidator;
|
|
164
|
+
private checkpointAttestationValidator: CheckpointAttestationValidator;
|
|
126
165
|
|
|
127
166
|
private protocolVersion = '';
|
|
128
167
|
private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
|
|
129
168
|
|
|
130
|
-
|
|
169
|
+
/** Callback invoked when a duplicate proposal is detected (triggers slashing). */
|
|
170
|
+
private duplicateProposalCallback?: (info: {
|
|
171
|
+
slot: SlotNumber;
|
|
172
|
+
proposer: EthAddress;
|
|
173
|
+
type: 'checkpoint' | 'block';
|
|
174
|
+
}) => void;
|
|
175
|
+
|
|
176
|
+
/** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
|
|
177
|
+
private oversizedProposalCallback?: P2POversizedProposalCallback;
|
|
178
|
+
|
|
179
|
+
/** Callback invoked when a duplicate attestation is detected (triggers slashing). */
|
|
180
|
+
private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
|
|
181
|
+
|
|
182
|
+
/** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
|
|
183
|
+
private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
|
|
131
184
|
|
|
132
185
|
/**
|
|
133
186
|
* Callback for when a block is received from a peer.
|
|
@@ -136,32 +189,58 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
136
189
|
*/
|
|
137
190
|
private blockReceivedCallback: P2PBlockReceivedCallback;
|
|
138
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Callback for when a checkpoint proposal is received from a peer.
|
|
194
|
+
* @param checkpoint - The checkpoint proposal received from the peer.
|
|
195
|
+
* @returns The attestations for the checkpoint, if any.
|
|
196
|
+
*/
|
|
197
|
+
private allNodesCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
|
|
198
|
+
/**
|
|
199
|
+
* Callback for when a checkpoint proposal is received - specifically for validators - from a peer.
|
|
200
|
+
* @param checkpoint - The checkpoint proposal received from the peer.
|
|
201
|
+
* @returns The attestations for the checkpoint, if any.
|
|
202
|
+
*/
|
|
203
|
+
private validatorCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
|
|
204
|
+
|
|
139
205
|
private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
|
|
140
206
|
|
|
207
|
+
private ipChangedHandler?: (ip: string) => void;
|
|
208
|
+
private discoveredP2pIp?: string;
|
|
209
|
+
|
|
141
210
|
private instrumentation: P2PInstrumentation;
|
|
142
211
|
|
|
212
|
+
private telemetry: TelemetryClient;
|
|
213
|
+
|
|
214
|
+
protected logger: Logger;
|
|
215
|
+
|
|
143
216
|
constructor(
|
|
144
|
-
private clientType: T,
|
|
145
217
|
private config: P2PConfig,
|
|
146
218
|
protected node: PubSubLibp2p,
|
|
147
219
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
148
220
|
private reqresp: ReqRespInterface,
|
|
149
|
-
|
|
150
|
-
protected mempools: MemPools
|
|
151
|
-
|
|
221
|
+
protected peerManager: PeerManagerInterface,
|
|
222
|
+
protected mempools: MemPools,
|
|
223
|
+
protected archiver: L2BlockSource & ContractDataSource,
|
|
152
224
|
private epochCache: EpochCacheInterface,
|
|
153
225
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
154
226
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
227
|
+
private blockMinFeesProvider: BlockMinFeesProvider,
|
|
155
228
|
telemetry: TelemetryClient,
|
|
156
|
-
|
|
229
|
+
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
230
|
+
private txValidationCache?: TxValidationCache,
|
|
157
231
|
) {
|
|
158
232
|
super(telemetry, 'LibP2PService');
|
|
233
|
+
this.telemetry = telemetry;
|
|
234
|
+
|
|
235
|
+
// Create child logger with fisherman prefix if in fisherman mode
|
|
236
|
+
this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
|
|
159
237
|
|
|
160
238
|
this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
|
|
161
239
|
|
|
162
240
|
this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
163
241
|
this.msgIdSeenValidators[TopicType.block_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
164
|
-
this.msgIdSeenValidators[TopicType.
|
|
242
|
+
this.msgIdSeenValidators[TopicType.checkpoint_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
243
|
+
this.msgIdSeenValidators[TopicType.checkpoint_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
165
244
|
|
|
166
245
|
const versions = getVersions(config);
|
|
167
246
|
this.protocolVersion = compressComponentVersions(versions);
|
|
@@ -169,27 +248,75 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
169
248
|
|
|
170
249
|
this.topicStrings[TopicType.tx] = createTopicString(TopicType.tx, this.protocolVersion);
|
|
171
250
|
this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
|
|
172
|
-
this.topicStrings[TopicType.
|
|
173
|
-
TopicType.
|
|
251
|
+
this.topicStrings[TopicType.checkpoint_proposal] = createTopicString(
|
|
252
|
+
TopicType.checkpoint_proposal,
|
|
253
|
+
this.protocolVersion,
|
|
254
|
+
);
|
|
255
|
+
this.topicStrings[TopicType.checkpoint_attestation] = createTopicString(
|
|
256
|
+
TopicType.checkpoint_attestation,
|
|
174
257
|
this.protocolVersion,
|
|
175
258
|
);
|
|
176
259
|
|
|
177
|
-
|
|
178
|
-
|
|
260
|
+
// Build the consensus timetable once from protocol slot-timing constants and inject it into every
|
|
261
|
+
// validator so they share one set of receive-window bounds, independent of proposer operational budgets.
|
|
262
|
+
const consensusTimetable = buildConsensusTimetable(config, epochCache.getL1Constants());
|
|
263
|
+
const proposalValidatorOpts = {
|
|
264
|
+
txsPermitted: !config.disableTransactions,
|
|
265
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
|
|
266
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
|
|
267
|
+
skipSlotValidation: config.skipProposalSlotValidation,
|
|
268
|
+
signatureContext: {
|
|
269
|
+
chainId: config.l1ChainId,
|
|
270
|
+
rollupAddress: config.rollupAddress,
|
|
271
|
+
},
|
|
272
|
+
clockDisparityMs: config.maxGossipClockDisparityMs,
|
|
273
|
+
};
|
|
274
|
+
this.blockProposalValidator = new BlockProposalValidator(epochCache, consensusTimetable, proposalValidatorOpts);
|
|
275
|
+
this.checkpointProposalValidator = new CheckpointProposalValidator(
|
|
276
|
+
epochCache,
|
|
277
|
+
consensusTimetable,
|
|
278
|
+
proposalValidatorOpts,
|
|
279
|
+
);
|
|
280
|
+
const attestationValidatorOpts = {
|
|
281
|
+
signatureContext: proposalValidatorOpts.signatureContext,
|
|
282
|
+
clockDisparityMs: config.maxGossipClockDisparityMs,
|
|
283
|
+
};
|
|
284
|
+
this.checkpointAttestationValidator = config.fishermanMode
|
|
285
|
+
? new FishermanAttestationValidator(
|
|
286
|
+
epochCache,
|
|
287
|
+
consensusTimetable,
|
|
288
|
+
mempools.attestationPool,
|
|
289
|
+
telemetry,
|
|
290
|
+
attestationValidatorOpts,
|
|
291
|
+
)
|
|
292
|
+
: new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
|
|
179
293
|
|
|
180
294
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
181
295
|
|
|
182
|
-
this.blockReceivedCallback = async (block: BlockProposal): Promise<
|
|
183
|
-
this.logger.
|
|
184
|
-
`Handler not yet registered
|
|
185
|
-
{ p2pMessageIdentifier: await block.
|
|
296
|
+
this.blockReceivedCallback = async (block: BlockProposal): Promise<boolean> => {
|
|
297
|
+
this.logger.warn(
|
|
298
|
+
`Handler for block received not yet registered on P2P service. Received block ${block.blockNumber} for slot ${block.slotNumber} from peer.`,
|
|
299
|
+
{ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
|
|
186
300
|
);
|
|
187
|
-
return
|
|
301
|
+
return true;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
this.allNodesCheckpointReceivedCallback = (
|
|
305
|
+
_checkpoint: CheckpointProposalCore,
|
|
306
|
+
): Promise<CheckpointAttestation[] | undefined> => {
|
|
307
|
+
throw new CheckpointProposalReceivedCallbackNotRegisteredError();
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
this.validatorCheckpointReceivedCallback = (
|
|
311
|
+
_checkpoint: CheckpointProposalCore,
|
|
312
|
+
): Promise<CheckpointAttestation[] | undefined> => {
|
|
313
|
+
return Promise.resolve(undefined);
|
|
188
314
|
};
|
|
189
315
|
}
|
|
190
316
|
|
|
191
|
-
public updateConfig(config: Partial<P2PReqRespConfig
|
|
317
|
+
public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
|
|
192
318
|
this.reqresp.updateConfig(config);
|
|
319
|
+
this.config = merge(this.config, config);
|
|
193
320
|
}
|
|
194
321
|
|
|
195
322
|
/**
|
|
@@ -198,20 +325,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
198
325
|
* @param txPool - The transaction pool to be accessed by the service.
|
|
199
326
|
* @returns The new service.
|
|
200
327
|
*/
|
|
201
|
-
public static async new
|
|
202
|
-
clientType: T,
|
|
328
|
+
public static async new(
|
|
203
329
|
config: P2PConfig,
|
|
204
330
|
peerId: PeerId,
|
|
205
331
|
deps: {
|
|
206
|
-
mempools: MemPools
|
|
332
|
+
mempools: MemPools;
|
|
207
333
|
l2BlockSource: L2BlockSource & ContractDataSource;
|
|
208
334
|
epochCache: EpochCacheInterface;
|
|
209
335
|
proofVerifier: ClientProtocolCircuitVerifier;
|
|
210
336
|
worldStateSynchronizer: WorldStateSynchronizer;
|
|
211
337
|
peerStore: AztecAsyncKVStore;
|
|
338
|
+
blockMinFeesProvider: BlockMinFeesProvider;
|
|
212
339
|
telemetry: TelemetryClient;
|
|
213
340
|
logger: Logger;
|
|
214
341
|
packageVersion: string;
|
|
342
|
+
txValidationCache?: TxValidationCache;
|
|
215
343
|
},
|
|
216
344
|
) {
|
|
217
345
|
const {
|
|
@@ -221,23 +349,26 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
221
349
|
mempools,
|
|
222
350
|
proofVerifier,
|
|
223
351
|
peerStore,
|
|
352
|
+
blockMinFeesProvider,
|
|
224
353
|
telemetry,
|
|
225
354
|
logger,
|
|
226
355
|
packageVersion,
|
|
356
|
+
txValidationCache,
|
|
227
357
|
} = deps;
|
|
228
358
|
const { p2pPort, maxPeerCount, listenAddress } = config;
|
|
229
359
|
const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
|
|
230
360
|
|
|
231
361
|
const datastore = new AztecDatastore(peerStore);
|
|
232
362
|
|
|
233
|
-
const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
|
|
363
|
+
const otelMetricsAdapter = new OtelMetricsAdapter(telemetry, logger.getBindings());
|
|
234
364
|
|
|
235
365
|
const peerDiscoveryService = new DiscV5Service(
|
|
236
366
|
peerId,
|
|
237
367
|
config,
|
|
238
368
|
packageVersion,
|
|
239
369
|
telemetry,
|
|
240
|
-
createLogger(`${logger.module}:discv5_service
|
|
370
|
+
createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
|
|
371
|
+
peerStore,
|
|
241
372
|
);
|
|
242
373
|
|
|
243
374
|
// Seed libp2p's bootstrap discovery with private and trusted peers
|
|
@@ -251,29 +382,50 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
251
382
|
const versions = getVersions(config);
|
|
252
383
|
const protocolVersion = compressComponentVersions(versions);
|
|
253
384
|
|
|
254
|
-
const txTopic = createTopicString(TopicType.tx, protocolVersion);
|
|
255
|
-
const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion);
|
|
256
|
-
const blockAttestationTopic = createTopicString(TopicType.block_attestation, protocolVersion);
|
|
257
|
-
|
|
258
385
|
const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
259
|
-
const directPeers = (
|
|
386
|
+
const directPeers = compactArray(
|
|
260
387
|
await Promise.all(
|
|
261
388
|
preferredPeersEnrs.map(async enr => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
389
|
+
try {
|
|
390
|
+
const peerId = await enr.peerId();
|
|
391
|
+
const address = enr.getLocationMultiaddr('tcp');
|
|
392
|
+
if (address === undefined) {
|
|
393
|
+
throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
id: peerId,
|
|
397
|
+
addrs: [address],
|
|
398
|
+
};
|
|
399
|
+
} catch (err) {
|
|
400
|
+
// A malformed configured ENR shouldn't abort node setup — skip it and log.
|
|
401
|
+
logger.warn(`Skipping preferred peer with invalid ENR`, { err });
|
|
402
|
+
return undefined;
|
|
266
403
|
}
|
|
267
|
-
return {
|
|
268
|
-
id: peerId,
|
|
269
|
-
addrs: [address],
|
|
270
|
-
};
|
|
271
404
|
}),
|
|
272
|
-
)
|
|
273
|
-
)
|
|
405
|
+
),
|
|
406
|
+
);
|
|
274
407
|
|
|
275
408
|
const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
|
|
276
409
|
|
|
410
|
+
// Create dynamic topic score params based on network configuration. Scoring uses the network-wide
|
|
411
|
+
// max-blocks-per-checkpoint config value directly to size expected per-slot message rates; these are
|
|
412
|
+
// peer-rate thresholds, not consensus deadlines, so they need no proposer operational budgets.
|
|
413
|
+
const l1Constants = epochCache.getL1Constants();
|
|
414
|
+
const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
|
|
415
|
+
slotDurationMs: l1Constants.slotDuration * 1000,
|
|
416
|
+
heartbeatIntervalMs: config.gossipsubInterval,
|
|
417
|
+
targetCommitteeSize: l1Constants.targetCommitteeSize,
|
|
418
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
|
|
419
|
+
expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Restrict gossipsub to exactly the topics we subscribe to. Without this, an arbitrary-topic
|
|
423
|
+
// message is transformed, msg-id'd and inserted into the seenCache before the subscription check,
|
|
424
|
+
// so a crafted topic colliding on msg id can suppress a real message as a duplicate.
|
|
425
|
+
const allowedTopics = getTopicsForConfig(config.disableTransactions).map(topic =>
|
|
426
|
+
createTopicString(topic, protocolVersion),
|
|
427
|
+
);
|
|
428
|
+
|
|
277
429
|
const node = await createLibp2p({
|
|
278
430
|
start: false,
|
|
279
431
|
peerId,
|
|
@@ -305,7 +457,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
305
457
|
],
|
|
306
458
|
datastore,
|
|
307
459
|
peerDiscovery,
|
|
308
|
-
|
|
460
|
+
// Pin the yamux frame size: MAX_REQRESP_REQUEST_SIZE_BYTES relies on a reqresp request fitting in one frame.
|
|
461
|
+
streamMuxers: [yamux({ maxMessageSize: YAMUX_MAX_MESSAGE_SIZE_BYTES }), mplex()],
|
|
309
462
|
connectionEncryption: [noise()],
|
|
310
463
|
connectionManager: {
|
|
311
464
|
minConnections: 0, // Disable libp2p peer dialing, we do it manually
|
|
@@ -319,7 +472,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
319
472
|
},
|
|
320
473
|
connectionGater: {
|
|
321
474
|
denyInboundConnection: (maConn: MultiaddrConnection) => {
|
|
322
|
-
const allowed = peerManager.
|
|
475
|
+
const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
|
|
323
476
|
if (allowed) {
|
|
324
477
|
return false;
|
|
325
478
|
}
|
|
@@ -330,7 +483,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
330
483
|
denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
|
|
331
484
|
//NOTE: it is not necessary to check address here because this was already done by
|
|
332
485
|
// denyInboundConnection
|
|
333
|
-
const allowed = peerManager.
|
|
486
|
+
const allowed = peerManager.isPeerAllowedToConnect(peerId);
|
|
334
487
|
if (allowed) {
|
|
335
488
|
return false;
|
|
336
489
|
}
|
|
@@ -358,9 +511,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
358
511
|
mcacheLength: config.gossipsubMcacheLength,
|
|
359
512
|
mcacheGossip: config.gossipsubMcacheGossip,
|
|
360
513
|
seenTTL: config.gossipsubSeenTTL,
|
|
514
|
+
allowedTopics,
|
|
515
|
+
// No fastMsgIdFn: the fast-path dedup cache keys on a non-cryptographic 64-bit hash of the
|
|
516
|
+
// raw data only (no topic), so a collision — accidental or engineered via a weak seed — drops
|
|
517
|
+
// a different message with no fallback to the full id. Dedup instead rests solely on the
|
|
518
|
+
// cryptographic, topic-framed msgIdFn below.
|
|
361
519
|
msgIdFn: getMsgIdFn,
|
|
362
520
|
msgIdToStrFn: msgIdToStrFn,
|
|
363
|
-
fastMsgIdFn: fastMsgIdFn,
|
|
364
521
|
dataTransform: new SnappyTransform(),
|
|
365
522
|
metricsRegister: otelMetricsAdapter,
|
|
366
523
|
metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
|
|
@@ -369,33 +526,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
369
526
|
scoreParams: createPeerScoreParams({
|
|
370
527
|
// IPColocation factor can be disabled for local testing - default to -5
|
|
371
528
|
IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
|
|
372
|
-
topics:
|
|
373
|
-
[txTopic]: createTopicScoreParams({
|
|
374
|
-
topicWeight: 1,
|
|
375
|
-
invalidMessageDeliveriesWeight: -20,
|
|
376
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
377
|
-
}),
|
|
378
|
-
[blockAttestationTopic]: createTopicScoreParams({
|
|
379
|
-
topicWeight: 1,
|
|
380
|
-
invalidMessageDeliveriesWeight: -20,
|
|
381
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
382
|
-
}),
|
|
383
|
-
[blockProposalTopic]: createTopicScoreParams({
|
|
384
|
-
topicWeight: 1,
|
|
385
|
-
invalidMessageDeliveriesWeight: -20,
|
|
386
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
387
|
-
}),
|
|
388
|
-
},
|
|
529
|
+
topics: topicScoreParams,
|
|
389
530
|
}),
|
|
390
531
|
}) as (components: GossipSubComponents) => GossipSub,
|
|
391
|
-
components: (components: { connectionManager: ConnectionManager }) => ({
|
|
532
|
+
components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
|
|
392
533
|
connectionManager: components.connectionManager,
|
|
534
|
+
addressManager: components.addressManager,
|
|
393
535
|
}),
|
|
394
536
|
},
|
|
395
|
-
logger: createLibp2pComponentLogger(logger.module),
|
|
537
|
+
logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
|
|
396
538
|
});
|
|
397
539
|
|
|
398
|
-
const peerScoring = new PeerScoring(config);
|
|
540
|
+
const peerScoring = new PeerScoring(config, telemetry);
|
|
399
541
|
const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
|
|
400
542
|
|
|
401
543
|
const peerManager = new PeerManager(
|
|
@@ -411,13 +553,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
411
553
|
epochCache,
|
|
412
554
|
);
|
|
413
555
|
|
|
414
|
-
//
|
|
415
|
-
|
|
556
|
+
// Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
|
|
557
|
+
reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
|
|
558
|
+
|
|
559
|
+
// Configure application-specific scoring for gossipsub.
|
|
560
|
+
// The weight scales app score to align with gossipsub thresholds:
|
|
561
|
+
// - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
|
|
562
|
+
// - Ban (-100) × 10 = -1000 = publishThreshold (cannot publish)
|
|
563
|
+
// Note: positive topic scores can offset penalties, so alignment is best-effort.
|
|
564
|
+
node.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
|
|
416
565
|
node.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
|
|
417
566
|
peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
|
|
418
567
|
|
|
419
568
|
return new LibP2PService(
|
|
420
|
-
clientType,
|
|
421
569
|
config,
|
|
422
570
|
node,
|
|
423
571
|
peerDiscoveryService,
|
|
@@ -428,8 +576,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
428
576
|
epochCache,
|
|
429
577
|
proofVerifier,
|
|
430
578
|
worldStateSynchronizer,
|
|
579
|
+
blockMinFeesProvider,
|
|
431
580
|
telemetry,
|
|
432
581
|
logger,
|
|
582
|
+
txValidationCache,
|
|
433
583
|
);
|
|
434
584
|
}
|
|
435
585
|
|
|
@@ -443,43 +593,29 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
443
593
|
throw new Error('P2P service already started');
|
|
444
594
|
}
|
|
445
595
|
|
|
446
|
-
// Get listen & announce addresses for logging
|
|
447
596
|
const { p2pIp, p2pPort } = this.config;
|
|
448
|
-
if (!p2pIp) {
|
|
449
|
-
throw new Error('Announce address not provided.');
|
|
450
|
-
}
|
|
451
|
-
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
452
|
-
|
|
453
|
-
// Start job queue, peer discovery service and libp2p node
|
|
454
|
-
this.jobQueue.start();
|
|
455
|
-
|
|
456
|
-
await this.peerManager.initializePeers();
|
|
457
|
-
if (!this.config.p2pDiscoveryDisabled) {
|
|
458
|
-
await this.peerDiscoveryService.start();
|
|
459
|
-
}
|
|
460
|
-
await this.node.start();
|
|
461
|
-
|
|
462
|
-
// Subscribe to standard GossipSub topics by default
|
|
463
|
-
for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
|
|
464
|
-
this.subscribeToTopic(this.topicStrings[topic]);
|
|
597
|
+
if (!p2pIp && !this.config.queryForIp) {
|
|
598
|
+
throw new Error('Announce address not provided and queryForIp is not enabled.');
|
|
465
599
|
}
|
|
600
|
+
const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
|
|
466
601
|
|
|
467
602
|
// Create request response protocol handlers
|
|
468
603
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
469
604
|
const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
|
|
470
|
-
const blockHandler = reqRespBlockHandler(this.archiver);
|
|
471
605
|
const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
|
|
472
606
|
|
|
473
607
|
const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
|
|
474
608
|
[ReqRespSubProtocol.PING]: pingHandler,
|
|
475
609
|
[ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
|
|
476
610
|
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
|
|
477
|
-
[ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
|
|
478
611
|
};
|
|
479
612
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
613
|
+
if (!this.config.disableTransactions) {
|
|
614
|
+
const blockTxsHandler = reqRespBlockTxsHandler(
|
|
615
|
+
this.mempools.attestationPool,
|
|
616
|
+
this.archiver,
|
|
617
|
+
this.mempools.txPool,
|
|
618
|
+
);
|
|
483
619
|
requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
|
|
484
620
|
}
|
|
485
621
|
|
|
@@ -487,25 +623,65 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
487
623
|
requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
|
|
488
624
|
}
|
|
489
625
|
|
|
626
|
+
await this.peerManager.initializePeers();
|
|
627
|
+
|
|
628
|
+
await this.reqresp.start(requestResponseHandlers);
|
|
629
|
+
|
|
630
|
+
await this.node.start();
|
|
631
|
+
|
|
632
|
+
// Subscribe to standard GossipSub topics by default
|
|
633
|
+
for (const topic of getTopicsForConfig(this.config.disableTransactions)) {
|
|
634
|
+
this.subscribeToTopic(this.topicStrings[topic]);
|
|
635
|
+
}
|
|
636
|
+
|
|
490
637
|
// add GossipSub listener
|
|
491
638
|
this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
492
639
|
|
|
493
|
-
// Start running promise for peer discovery
|
|
640
|
+
// Start running promise for peer discovery and metrics collection
|
|
641
|
+
if (!this.config.p2pDiscoveryDisabled) {
|
|
642
|
+
await this.peerDiscoveryService.start();
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
|
|
646
|
+
if (this.config.queryForIp) {
|
|
647
|
+
this.discoveredP2pIp = this.config.p2pIp;
|
|
648
|
+
this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
|
|
649
|
+
this.ipChangedHandler = (ip: string) => {
|
|
650
|
+
const addressManager = this.node.services.components.addressManager;
|
|
651
|
+
const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
|
|
652
|
+
const previousIp = this.discoveredP2pIp;
|
|
653
|
+
|
|
654
|
+
if (previousIp) {
|
|
655
|
+
const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
|
|
656
|
+
addressManager.removeObservedAddr(oldAddr);
|
|
657
|
+
this.logger.info('Libp2p announce address updated due to IP change', {
|
|
658
|
+
previousIp,
|
|
659
|
+
newIp: ip,
|
|
660
|
+
newMultiaddr: newAddr.toString(),
|
|
661
|
+
});
|
|
662
|
+
} else {
|
|
663
|
+
this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
|
|
664
|
+
ip,
|
|
665
|
+
multiaddr: newAddr.toString(),
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
addressManager.addObservedAddr(newAddr);
|
|
670
|
+
addressManager.confirmObservedAddr(newAddr);
|
|
671
|
+
this.discoveredP2pIp = ip;
|
|
672
|
+
};
|
|
673
|
+
this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
|
|
674
|
+
}
|
|
675
|
+
|
|
494
676
|
this.discoveryRunningPromise = new RunningPromise(
|
|
495
|
-
() =>
|
|
677
|
+
async () => {
|
|
678
|
+
await this.peerManager.heartbeat();
|
|
679
|
+
},
|
|
496
680
|
this.logger,
|
|
497
681
|
this.config.peerCheckIntervalMS,
|
|
498
682
|
);
|
|
499
683
|
this.discoveryRunningPromise.start();
|
|
500
684
|
|
|
501
|
-
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
502
|
-
const reqrespSubProtocolValidators = {
|
|
503
|
-
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
504
|
-
// TODO(#11336): A request validator for blocks
|
|
505
|
-
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
506
|
-
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
507
|
-
};
|
|
508
|
-
await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
|
|
509
685
|
this.logger.info(`Started P2P service`, {
|
|
510
686
|
listen: this.config.listenAddress,
|
|
511
687
|
port: this.config.p2pPort,
|
|
@@ -522,12 +698,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
522
698
|
// Remove gossip sub listener
|
|
523
699
|
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
524
700
|
|
|
701
|
+
if (this.ipChangedHandler) {
|
|
702
|
+
this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
|
|
703
|
+
this.ipChangedHandler = undefined;
|
|
704
|
+
}
|
|
705
|
+
|
|
525
706
|
// Stop peer manager
|
|
526
707
|
this.logger.debug('Stopping peer manager...');
|
|
527
708
|
await this.peerManager.stop();
|
|
528
|
-
|
|
529
|
-
this.logger.debug('Stopping job queue...');
|
|
530
|
-
await this.jobQueue.end();
|
|
531
709
|
this.logger.debug('Stopping running promise...');
|
|
532
710
|
await this.discoveryRunningPromise?.stop();
|
|
533
711
|
this.logger.debug('Stopping peer discovery service...');
|
|
@@ -539,12 +717,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
539
717
|
this.logger.info('LibP2P service stopped');
|
|
540
718
|
}
|
|
541
719
|
|
|
542
|
-
addReqRespSubProtocol(
|
|
543
|
-
subProtocol
|
|
544
|
-
handler: ReqRespSubProtocolHandler,
|
|
545
|
-
validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
|
|
546
|
-
): Promise<void> {
|
|
547
|
-
return this.reqresp.addSubProtocol(subProtocol, handler, validator);
|
|
720
|
+
addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
|
|
721
|
+
return this.reqresp.addSubProtocol(subProtocol, handler);
|
|
548
722
|
}
|
|
549
723
|
|
|
550
724
|
public registerThisValidatorAddresses(address: EthAddress[]): void {
|
|
@@ -555,6 +729,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
555
729
|
return this.peerManager.getPeers(includePending);
|
|
556
730
|
}
|
|
557
731
|
|
|
732
|
+
public getGossipMeshPeerCount(topicType: TopicType): number {
|
|
733
|
+
return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
|
|
734
|
+
}
|
|
735
|
+
|
|
558
736
|
private handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
|
|
559
737
|
this.logger.trace(`Received PUBSUB message.`);
|
|
560
738
|
|
|
@@ -568,18 +746,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
568
746
|
setImmediate(() => void safeJob());
|
|
569
747
|
}
|
|
570
748
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
protocol: SubProtocol,
|
|
579
|
-
requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
|
|
580
|
-
pinnedPeerId: PeerId | undefined,
|
|
581
|
-
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
|
|
582
|
-
return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
|
|
749
|
+
public sendRequestToPeer(
|
|
750
|
+
peerId: PeerId,
|
|
751
|
+
subProtocol: ReqRespSubProtocol,
|
|
752
|
+
payload: Buffer,
|
|
753
|
+
dialTimeout?: number,
|
|
754
|
+
): Promise<ReqRespResponse> {
|
|
755
|
+
return this.reqresp.sendRequestToPeer(peerId, subProtocol, payload, dialTimeout);
|
|
583
756
|
}
|
|
584
757
|
|
|
585
758
|
/**
|
|
@@ -594,6 +767,44 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
594
767
|
this.blockReceivedCallback = callback;
|
|
595
768
|
}
|
|
596
769
|
|
|
770
|
+
public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
|
|
771
|
+
this.validatorCheckpointReceivedCallback = callback;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
|
|
775
|
+
this.allNodesCheckpointReceivedCallback = callback;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Registers a callback to be invoked when a duplicate proposal is detected.
|
|
780
|
+
* This callback is triggered on the first duplicate (when count goes from 1 to 2).
|
|
781
|
+
*/
|
|
782
|
+
public registerDuplicateProposalCallback(
|
|
783
|
+
callback: (info: { slot: SlotNumber; proposer: EthAddress; type: 'checkpoint' | 'block' }) => void,
|
|
784
|
+
): void {
|
|
785
|
+
this.duplicateProposalCallback = callback;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Registers a callback to be invoked when an oversized block proposal is stored as slashing evidence.
|
|
790
|
+
*/
|
|
791
|
+
public registerOversizedProposalCallback(callback: P2POversizedProposalCallback): void {
|
|
792
|
+
this.oversizedProposalCallback = callback;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Registers a callback to be invoked when a duplicate attestation is detected.
|
|
797
|
+
* A validator signing attestations for different proposals at the same slot.
|
|
798
|
+
* This callback is triggered on the first duplicate (when count goes from 1 to 2).
|
|
799
|
+
*/
|
|
800
|
+
public registerDuplicateAttestationCallback(callback: P2PDuplicateAttestationCallback): void {
|
|
801
|
+
this.duplicateAttestationCallback = callback;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
|
|
805
|
+
this.checkpointAttestationCallback = callback;
|
|
806
|
+
}
|
|
807
|
+
|
|
597
808
|
/**
|
|
598
809
|
* Subscribes to a topic.
|
|
599
810
|
* @param topic - The topic to subscribe to.
|
|
@@ -615,7 +826,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
615
826
|
if (!this.node.services.pubsub) {
|
|
616
827
|
throw new Error('Pubsub service not available.');
|
|
617
828
|
}
|
|
618
|
-
const
|
|
829
|
+
const isBlockProposal = topic === this.topicStrings[TopicType.block_proposal];
|
|
830
|
+
const traceContext =
|
|
831
|
+
this.config.debugP2PInstrumentMessages && isBlockProposal ? this.telemetry.getTraceContext() : undefined;
|
|
832
|
+
const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages, traceContext);
|
|
619
833
|
const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
|
|
620
834
|
return result.recipients.length;
|
|
621
835
|
}
|
|
@@ -636,12 +850,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
636
850
|
case this.topicStrings[TopicType.tx]:
|
|
637
851
|
topicType = TopicType.tx;
|
|
638
852
|
break;
|
|
639
|
-
case this.topicStrings[TopicType.block_attestation]:
|
|
640
|
-
topicType = TopicType.block_attestation;
|
|
641
|
-
break;
|
|
642
853
|
case this.topicStrings[TopicType.block_proposal]:
|
|
643
854
|
topicType = TopicType.block_proposal;
|
|
644
855
|
break;
|
|
856
|
+
case this.topicStrings[TopicType.checkpoint_proposal]:
|
|
857
|
+
topicType = TopicType.checkpoint_proposal;
|
|
858
|
+
break;
|
|
859
|
+
case this.topicStrings[TopicType.checkpoint_attestation]:
|
|
860
|
+
topicType = TopicType.checkpoint_attestation;
|
|
861
|
+
break;
|
|
645
862
|
default:
|
|
646
863
|
this.logger.error(`Received message on unknown topic: ${msg.topic}`);
|
|
647
864
|
break;
|
|
@@ -652,6 +869,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
652
869
|
if (!validator || !validator.addMessage(msgId)) {
|
|
653
870
|
this.instrumentation.incMessagePrevalidationStatus(false, topicType);
|
|
654
871
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
|
|
872
|
+
if (topicType === TopicType.tx) {
|
|
873
|
+
this.logger.verbose(`Ignoring already-seen tx gossip message`, { msgId, source: source.toString() });
|
|
874
|
+
}
|
|
655
875
|
return { result: false, topicType };
|
|
656
876
|
}
|
|
657
877
|
|
|
@@ -660,13 +880,39 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
660
880
|
return { result: true, topicType };
|
|
661
881
|
}
|
|
662
882
|
|
|
883
|
+
/**
|
|
884
|
+
* Safely deserializes a P2PMessage from raw message data.
|
|
885
|
+
* @param msgId - The message ID.
|
|
886
|
+
* @param source - The peer ID of the message source.
|
|
887
|
+
* @param data - The raw message data.
|
|
888
|
+
* @returns The deserialized P2PMessage or undefined if deserialization fails.
|
|
889
|
+
*/
|
|
890
|
+
private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
|
|
891
|
+
try {
|
|
892
|
+
return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
|
|
893
|
+
} catch (err) {
|
|
894
|
+
this.logger.error(`Error deserializing P2PMessage`, err, {
|
|
895
|
+
msgId,
|
|
896
|
+
source: source.toString(),
|
|
897
|
+
});
|
|
898
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
|
|
899
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
663
904
|
/**
|
|
664
905
|
* Handles a new gossip message that was received by the client.
|
|
665
906
|
* @param topic - The message's topic.
|
|
666
907
|
* @param data - The message data
|
|
667
908
|
*/
|
|
668
909
|
protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
|
|
669
|
-
const
|
|
910
|
+
const msgReceivedTime = Date.now();
|
|
911
|
+
let topicType: TopicType | undefined;
|
|
912
|
+
const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
|
|
913
|
+
if (!p2pMessage) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
670
916
|
|
|
671
917
|
const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
|
|
672
918
|
|
|
@@ -674,63 +920,226 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
674
920
|
return;
|
|
675
921
|
}
|
|
676
922
|
|
|
923
|
+
// Determine topic type for attributes
|
|
677
924
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
678
|
-
|
|
925
|
+
topicType = TopicType.tx;
|
|
926
|
+
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
927
|
+
topicType = TopicType.checkpoint_attestation;
|
|
928
|
+
} else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
929
|
+
topicType = TopicType.block_proposal;
|
|
930
|
+
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
|
|
931
|
+
topicType = TopicType.checkpoint_proposal;
|
|
679
932
|
}
|
|
680
|
-
|
|
681
|
-
|
|
933
|
+
|
|
934
|
+
// Process the message, optionally within a linked span for trace propagation
|
|
935
|
+
const processMessage = async () => {
|
|
936
|
+
if (
|
|
937
|
+
this.config.skipIncomingProposals &&
|
|
938
|
+
(msg.topic === this.topicStrings[TopicType.block_proposal] ||
|
|
939
|
+
msg.topic === this.topicStrings[TopicType.checkpoint_proposal])
|
|
940
|
+
) {
|
|
941
|
+
this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, { topic: msg.topic });
|
|
942
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
946
|
+
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
947
|
+
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
948
|
+
await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
949
|
+
} else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
950
|
+
await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
|
|
951
|
+
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
|
|
952
|
+
await this.handleGossipedCheckpointProposal(p2pMessage.payload, msgId, source);
|
|
953
|
+
} else {
|
|
954
|
+
this.logger.error(`Received message on unknown topic: ${msg.topic}`);
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
const latency = p2pMessage.timestamp !== undefined ? msgReceivedTime - p2pMessage.timestamp.getTime() : undefined;
|
|
959
|
+
const propagatedContext = p2pMessage.traceContext
|
|
960
|
+
? this.telemetry.extractPropagatedContext(p2pMessage.traceContext)
|
|
961
|
+
: undefined;
|
|
962
|
+
|
|
963
|
+
if (propagatedContext) {
|
|
964
|
+
await this.tracer.startActiveSpan(
|
|
965
|
+
'LibP2PService.processMessage',
|
|
966
|
+
{
|
|
967
|
+
attributes: {
|
|
968
|
+
[Attributes.TOPIC_NAME]: topicType!,
|
|
969
|
+
[Attributes.PEER_ID]: source.toString(),
|
|
970
|
+
},
|
|
971
|
+
},
|
|
972
|
+
propagatedContext,
|
|
973
|
+
async span => {
|
|
974
|
+
try {
|
|
975
|
+
await processMessage();
|
|
976
|
+
span.setStatus({
|
|
977
|
+
code: SpanStatusCode.OK,
|
|
978
|
+
});
|
|
979
|
+
} catch (err) {
|
|
980
|
+
span.setStatus({
|
|
981
|
+
code: SpanStatusCode.ERROR,
|
|
982
|
+
message: String(err),
|
|
983
|
+
});
|
|
984
|
+
if (typeof err === 'string' || (err && err instanceof Error)) {
|
|
985
|
+
span.recordException(err);
|
|
986
|
+
}
|
|
987
|
+
throw err;
|
|
988
|
+
} finally {
|
|
989
|
+
span.end();
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
);
|
|
993
|
+
} else {
|
|
994
|
+
await processMessage();
|
|
682
995
|
}
|
|
683
|
-
|
|
684
|
-
|
|
996
|
+
|
|
997
|
+
if (latency !== undefined && topicType !== undefined) {
|
|
998
|
+
this.instrumentation.recordMessageLatency(topicType, latency);
|
|
685
999
|
}
|
|
686
1000
|
|
|
687
1001
|
return;
|
|
688
1002
|
}
|
|
689
1003
|
|
|
690
|
-
protected async validateReceivedMessage<T>(
|
|
691
|
-
validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
|
|
1004
|
+
protected async validateReceivedMessage<T, M = undefined>(
|
|
1005
|
+
validationFunc: () => Promise<ReceivedMessageValidationResult<T, M>>,
|
|
692
1006
|
msgId: string,
|
|
693
1007
|
source: PeerId,
|
|
694
1008
|
topicType: TopicType,
|
|
695
|
-
): Promise<ReceivedMessageValidationResult<T>> {
|
|
696
|
-
|
|
1009
|
+
): Promise<ReceivedMessageValidationResult<T, M>> {
|
|
1010
|
+
// Default to reject result with a penalty if validation function throws an error
|
|
1011
|
+
let resultAndObj: ReceivedMessageValidationResult<T, M> = {
|
|
1012
|
+
result: TopicValidatorResult.Reject,
|
|
1013
|
+
severity: PeerErrorSeverity.MidToleranceError,
|
|
1014
|
+
};
|
|
697
1015
|
const timer = new Timer();
|
|
698
1016
|
try {
|
|
699
1017
|
resultAndObj = await validationFunc();
|
|
700
1018
|
} catch (err) {
|
|
701
|
-
this.logger.error(`Error
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1019
|
+
this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
const validationTimeMs = timer.ms();
|
|
1023
|
+
const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
|
|
1024
|
+
if (validationTimeMs > mcacheWindowMs * 0.75) {
|
|
1025
|
+
this.instrumentation.incSlowValidation(topicType);
|
|
1026
|
+
this.logger.warn(
|
|
1027
|
+
`Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
|
|
1028
|
+
`Message forwarding may be skipped if validation exceeds the window.`,
|
|
1029
|
+
{ msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs },
|
|
1030
|
+
);
|
|
706
1031
|
}
|
|
707
1032
|
|
|
708
1033
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
1034
|
+
this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
|
|
709
1035
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
1036
|
+
} else if (resultAndObj.result === TopicValidatorResult.Reject) {
|
|
1037
|
+
this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
|
|
1038
|
+
msgId,
|
|
1039
|
+
source: source.toString(),
|
|
1040
|
+
topicType,
|
|
1041
|
+
severity: resultAndObj.severity,
|
|
1042
|
+
});
|
|
1043
|
+
this.peerManager.penalizePeer(source, resultAndObj.severity);
|
|
1044
|
+
} else {
|
|
1045
|
+
this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
|
|
710
1046
|
}
|
|
711
1047
|
|
|
712
1048
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
713
1049
|
return resultAndObj;
|
|
714
1050
|
}
|
|
715
1051
|
|
|
1052
|
+
private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
|
|
1053
|
+
try {
|
|
1054
|
+
return deserializeFunc();
|
|
1055
|
+
} catch (err) {
|
|
1056
|
+
this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
|
|
1057
|
+
err,
|
|
1058
|
+
msgId,
|
|
1059
|
+
source: source.toString(),
|
|
1060
|
+
});
|
|
1061
|
+
return undefined;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
716
1065
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
717
1066
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
718
|
-
const tx = Tx.fromBuffer(payloadData);
|
|
719
|
-
|
|
720
|
-
|
|
1067
|
+
const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
|
|
1068
|
+
if (!tx) {
|
|
1069
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1073
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1074
|
+
|
|
1075
|
+
// Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
|
|
1076
|
+
const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
1077
|
+
const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
|
|
1078
|
+
if (!firstStageOutcome.allPassed) {
|
|
1079
|
+
const { name } = firstStageOutcome.failure;
|
|
1080
|
+
let { severity } = firstStageOutcome.failure;
|
|
1081
|
+
|
|
1082
|
+
// Double spend validator has a special case handler. We perform more detailed examination
|
|
1083
|
+
// as to how recently the nullifier was entered into the tree and if the transaction should
|
|
1084
|
+
// have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
|
|
1085
|
+
if (name === 'doubleSpendValidator') {
|
|
1086
|
+
const txBlockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1087
|
+
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 1 validation failed`, {
|
|
1091
|
+
validator: name,
|
|
1092
|
+
severity,
|
|
1093
|
+
source: source.toString(),
|
|
1094
|
+
});
|
|
1095
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
1096
|
+
}
|
|
721
1097
|
|
|
722
|
-
this
|
|
723
|
-
|
|
724
|
-
|
|
1098
|
+
// Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
|
|
1099
|
+
const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
|
|
1100
|
+
if (canAdd === 'ignored') {
|
|
1101
|
+
this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
|
|
1102
|
+
source: source.toString(),
|
|
1103
|
+
});
|
|
1104
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Stage 2: expensive proof verification
|
|
1108
|
+
const secondStageValidators = this.createSecondStageMessageValidators();
|
|
1109
|
+
const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
|
|
1110
|
+
if (!secondStageOutcome.allPassed) {
|
|
1111
|
+
const { severity, name } = secondStageOutcome.failure;
|
|
1112
|
+
this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
|
|
1113
|
+
validator: name,
|
|
1114
|
+
severity,
|
|
1115
|
+
source: source.toString(),
|
|
1116
|
+
});
|
|
1117
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// Pool add: persist the tx
|
|
1121
|
+
const txHash = tx.getTxHash();
|
|
1122
|
+
const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
|
|
1123
|
+
|
|
1124
|
+
const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
|
|
1125
|
+
const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
|
|
1126
|
+
|
|
1127
|
+
this.logger.verbose(`Validate propagated tx ${txHash.toString()}`, {
|
|
1128
|
+
wasAccepted,
|
|
1129
|
+
wasIgnored,
|
|
725
1130
|
[Attributes.P2P_ID]: source.toString(),
|
|
726
1131
|
});
|
|
727
1132
|
|
|
728
|
-
if (
|
|
729
|
-
return { result: TopicValidatorResult.
|
|
730
|
-
} else if (
|
|
1133
|
+
if (wasAccepted) {
|
|
1134
|
+
return { result: TopicValidatorResult.Accept, obj: tx };
|
|
1135
|
+
} else if (wasIgnored) {
|
|
731
1136
|
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
732
1137
|
} else {
|
|
733
|
-
|
|
1138
|
+
this.logger.warn(`Gossiped tx ${txHash.toString()} unexpectedly rejected by pool`, {
|
|
1139
|
+
source: source.toString(),
|
|
1140
|
+
txHash: txHash.toString(),
|
|
1141
|
+
});
|
|
1142
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
734
1143
|
}
|
|
735
1144
|
};
|
|
736
1145
|
|
|
@@ -739,6 +1148,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
739
1148
|
return;
|
|
740
1149
|
}
|
|
741
1150
|
|
|
1151
|
+
// Tx was accepted into pool and will be propagated - just log and record metrics
|
|
742
1152
|
const txHash = tx.getTxHash();
|
|
743
1153
|
const txHashString = txHash.toString();
|
|
744
1154
|
this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
|
|
@@ -746,155 +1156,462 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
746
1156
|
txHash: txHashString,
|
|
747
1157
|
});
|
|
748
1158
|
|
|
749
|
-
|
|
750
|
-
this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
await this.mempools.txPool.addTxs([tx]);
|
|
1159
|
+
this.instrumentation.incrementTxReceived(1);
|
|
755
1160
|
}
|
|
756
1161
|
|
|
757
1162
|
/**
|
|
758
|
-
* Process
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* @param attestation - The attestation to process.
|
|
1163
|
+
* Process a checkpoint attestation from a peer.
|
|
1164
|
+
* Validates the attestation and adds it to the pool.
|
|
762
1165
|
*/
|
|
763
|
-
private async
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
return
|
|
778
|
-
}
|
|
779
|
-
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
780
|
-
} else {
|
|
781
|
-
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
|
|
786
|
-
validationFunc,
|
|
1166
|
+
private async processCheckpointAttestationFromPeer(
|
|
1167
|
+
payloadData: Buffer,
|
|
1168
|
+
msgId: string,
|
|
1169
|
+
source: PeerId,
|
|
1170
|
+
): Promise<void> {
|
|
1171
|
+
const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
|
|
1172
|
+
() => {
|
|
1173
|
+
const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
|
|
1174
|
+
if (!attestation) {
|
|
1175
|
+
return Promise.resolve({
|
|
1176
|
+
result: TopicValidatorResult.Reject,
|
|
1177
|
+
severity: PeerErrorSeverity.LowToleranceError,
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
return this.validateAndStoreCheckpointAttestation(source, attestation);
|
|
1181
|
+
},
|
|
787
1182
|
msgId,
|
|
788
1183
|
source,
|
|
789
|
-
TopicType.
|
|
1184
|
+
TopicType.checkpoint_attestation,
|
|
790
1185
|
);
|
|
791
1186
|
|
|
792
1187
|
if (result !== TopicValidatorResult.Accept || !attestation) {
|
|
793
1188
|
return;
|
|
794
1189
|
}
|
|
795
1190
|
|
|
796
|
-
this.logger.
|
|
797
|
-
`Received attestation for slot ${attestation.slotNumber
|
|
1191
|
+
this.logger.verbose(
|
|
1192
|
+
`Received valid checkpoint attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
|
|
798
1193
|
{
|
|
799
|
-
p2pMessageIdentifier: await attestation.
|
|
800
|
-
slot: attestation.slotNumber
|
|
1194
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
1195
|
+
slot: attestation.slotNumber,
|
|
801
1196
|
archive: attestation.archive.toString(),
|
|
802
1197
|
source: source.toString(),
|
|
803
1198
|
},
|
|
804
1199
|
);
|
|
805
|
-
|
|
806
|
-
await this.mempools.attestationPool!.addAttestations([attestation]);
|
|
807
1200
|
}
|
|
808
1201
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1202
|
+
/** Validates a checkpoint attestation and adds it to the pool. Penalizes the peer if validation fails. */
|
|
1203
|
+
@trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation) => ({
|
|
1204
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
1205
|
+
}))
|
|
1206
|
+
protected async validateAndStoreCheckpointAttestation(
|
|
1207
|
+
peerId: PeerId,
|
|
1208
|
+
attestation: CheckpointAttestation,
|
|
1209
|
+
): Promise<ReceivedMessageValidationResult<CheckpointAttestation>> {
|
|
1210
|
+
const validationResult = await this.checkpointAttestationValidator.validate(attestation);
|
|
813
1211
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1212
|
+
if (validationResult.result === 'reject') {
|
|
1213
|
+
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1214
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1215
|
+
}
|
|
817
1216
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1217
|
+
if (validationResult.result === 'ignore') {
|
|
1218
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// Try to add the attestation: this handles existence check, cap check, and adding in one call
|
|
1222
|
+
// count is the number of attestations by this signer for this slot (for duplicate detection)
|
|
1223
|
+
const slot = attestation.payload.header.slotNumber;
|
|
1224
|
+
const { added, alreadyExists, count } =
|
|
1225
|
+
await this.mempools.attestationPool.tryAddCheckpointAttestation(attestation);
|
|
1226
|
+
|
|
1227
|
+
this.logger.trace(`Validate propagated checkpoint attestation`, {
|
|
1228
|
+
added,
|
|
1229
|
+
alreadyExists,
|
|
1230
|
+
count,
|
|
1231
|
+
[Attributes.SLOT_NUMBER]: slot.toString(),
|
|
1232
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
// Exact same attestation received, no need to re-broadcast
|
|
1236
|
+
if (alreadyExists) {
|
|
1237
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Could not add (cap reached for signer), penalize and do not re-broadcast
|
|
1241
|
+
if (!added) {
|
|
1242
|
+
this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
|
|
1243
|
+
slot: slot.toString(),
|
|
1244
|
+
archive: attestation.archive.toString(),
|
|
1245
|
+
source: peerId.toString(),
|
|
1246
|
+
attester: attestation.getSender()?.toString(),
|
|
1247
|
+
count,
|
|
823
1248
|
});
|
|
1249
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
1250
|
+
}
|
|
824
1251
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1252
|
+
// Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
|
|
1253
|
+
// count is the number of attestations by this signer for this slot
|
|
1254
|
+
if (count === 2) {
|
|
1255
|
+
const attester = attestation.getSender();
|
|
1256
|
+
if (attester) {
|
|
1257
|
+
this.logger.warn(`Detected duplicate attestation (equivocation) at slot ${slot}`, {
|
|
1258
|
+
slot: slot.toString(),
|
|
1259
|
+
archive: attestation.archive.toString(),
|
|
1260
|
+
source: peerId.toString(),
|
|
1261
|
+
attester: attester.toString(),
|
|
1262
|
+
});
|
|
1263
|
+
this.duplicateAttestationCallback?.({ slot, attester });
|
|
831
1264
|
}
|
|
832
|
-
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1268
|
+
this.checkpointAttestationCallback?.(attestation);
|
|
1269
|
+
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
1270
|
+
}
|
|
833
1271
|
|
|
834
|
-
|
|
835
|
-
|
|
1272
|
+
protected async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
1273
|
+
const {
|
|
1274
|
+
result,
|
|
1275
|
+
obj: block,
|
|
1276
|
+
metadata: { isEquivocated, isOversized } = {},
|
|
1277
|
+
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
|
|
1278
|
+
() => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
|
|
836
1279
|
msgId,
|
|
837
1280
|
source,
|
|
838
1281
|
TopicType.block_proposal,
|
|
839
1282
|
);
|
|
840
1283
|
|
|
841
|
-
|
|
1284
|
+
// If not accepted, equivocated, or oversized, return. Oversized proposals are re-broadcast as
|
|
1285
|
+
// slashing evidence but never attested or processed.
|
|
1286
|
+
if (result !== TopicValidatorResult.Accept || !block || isEquivocated || isOversized) {
|
|
842
1287
|
return;
|
|
843
1288
|
}
|
|
844
1289
|
|
|
845
1290
|
await this.processValidBlockProposal(block, source);
|
|
846
1291
|
}
|
|
847
1292
|
|
|
848
|
-
|
|
1293
|
+
/** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
|
|
1294
|
+
@trackSpan('Libp2pService.validateAndStoreBlockProposal', (_peerId, block) => ({
|
|
1295
|
+
[Attributes.BLOCK_NUMBER]: block.blockNumber.toString(),
|
|
1296
|
+
[Attributes.SLOT_NUMBER]: block.slotNumber.toString(),
|
|
1297
|
+
}))
|
|
1298
|
+
protected async validateAndStoreBlockProposal(
|
|
1299
|
+
peerId: PeerId,
|
|
1300
|
+
block: BlockProposal,
|
|
1301
|
+
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
|
|
1302
|
+
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1303
|
+
|
|
1304
|
+
if (validationResult.result === 'reject') {
|
|
1305
|
+
this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1306
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
if (validationResult.result === 'ignore') {
|
|
1310
|
+
return { result: TopicValidatorResult.Ignore, obj: block };
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Try to add the proposal: this handles existence check, cap check, and adding in one call
|
|
1314
|
+
const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
|
|
1315
|
+
const isEquivocated = count !== undefined && count > 1;
|
|
1316
|
+
// An oversized proposal (index at or beyond the consensus per-checkpoint limit) is structurally valid
|
|
1317
|
+
// proposer misbehavior: it is stored and re-broadcast as slashing evidence but never processed or
|
|
1318
|
+
// attested to. No-ops when maxBlocksPerCheckpoint is unset (local/test).
|
|
1319
|
+
const isOversized =
|
|
1320
|
+
this.config.maxBlocksPerCheckpoint !== undefined &&
|
|
1321
|
+
block.indexWithinCheckpoint >= this.config.maxBlocksPerCheckpoint;
|
|
1322
|
+
|
|
1323
|
+
// Duplicate proposal received, no need to re-broadcast
|
|
1324
|
+
if (alreadyExists) {
|
|
1325
|
+
this.logger.debug(`Ignoring duplicate block proposal received`, {
|
|
1326
|
+
...block.toBlockInfo(),
|
|
1327
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1328
|
+
proposer: block.getSender()?.toString(),
|
|
1329
|
+
source: peerId.toString(),
|
|
1330
|
+
});
|
|
1331
|
+
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
1335
|
+
if (!added) {
|
|
1336
|
+
this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
|
|
1337
|
+
...block.toBlockInfo(),
|
|
1338
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1339
|
+
count,
|
|
1340
|
+
proposer: block.getSender()?.toString(),
|
|
1341
|
+
source: peerId.toString(),
|
|
1342
|
+
});
|
|
1343
|
+
return {
|
|
1344
|
+
result: TopicValidatorResult.Reject,
|
|
1345
|
+
metadata: { isEquivocated, isOversized },
|
|
1346
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// The proposal was stored: if oversized, invoke the oversized callback so the proposer can be
|
|
1351
|
+
// slashed. Fired alongside (not instead of) equivocation detection below.
|
|
1352
|
+
if (isOversized) {
|
|
1353
|
+
const proposer = block.getSender();
|
|
1354
|
+
if (proposer) {
|
|
1355
|
+
this.logger.warn(`Detected oversized block proposal at slot ${block.slotNumber}`, {
|
|
1356
|
+
...block.toBlockInfo(),
|
|
1357
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1358
|
+
maxBlocksPerCheckpoint: this.config.maxBlocksPerCheckpoint,
|
|
1359
|
+
source: peerId.toString(),
|
|
1360
|
+
proposer: proposer.toString(),
|
|
1361
|
+
});
|
|
1362
|
+
this.oversizedProposalCallback?.({ slot: block.slotNumber, proposer });
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1367
|
+
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1368
|
+
if (isEquivocated) {
|
|
1369
|
+
const proposer = block.getSender();
|
|
1370
|
+
this.logger.warn(`Detected duplicate block proposal (equivocation) at slot ${block.slotNumber}`, {
|
|
1371
|
+
...block.toBlockInfo(),
|
|
1372
|
+
source: peerId.toString(),
|
|
1373
|
+
proposer: proposer?.toString(),
|
|
1374
|
+
});
|
|
1375
|
+
// Invoke the duplicate callback on the first duplicate spotted only
|
|
1376
|
+
if (proposer && count === 2) {
|
|
1377
|
+
this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
|
|
1378
|
+
}
|
|
1379
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// Otherwise, we're good to go!
|
|
1383
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
|
|
1387
|
+
// should not be here as it does not deal with p2p networking.
|
|
849
1388
|
@trackSpan('Libp2pService.processValidBlockProposal', async block => ({
|
|
850
|
-
[Attributes.SLOT_NUMBER]: block.slotNumber
|
|
1389
|
+
[Attributes.SLOT_NUMBER]: block.slotNumber,
|
|
851
1390
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
852
|
-
[Attributes.P2P_ID]: await block.
|
|
1391
|
+
[Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
853
1392
|
}))
|
|
854
|
-
|
|
855
|
-
const slot = block.slotNumber
|
|
856
|
-
const previousSlot = slot - 1n;
|
|
1393
|
+
protected async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
|
|
1394
|
+
const slot = block.slotNumber;
|
|
857
1395
|
this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
858
|
-
p2pMessageIdentifier: await block.
|
|
859
|
-
slot: block.slotNumber.toNumber(),
|
|
860
|
-
archive: block.archive.toString(),
|
|
1396
|
+
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
|
|
861
1397
|
source: sender.toString(),
|
|
1398
|
+
...block.toBlockInfo(),
|
|
862
1399
|
});
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1400
|
+
|
|
1401
|
+
// Mark the txs in this proposal as protected
|
|
1402
|
+
await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
|
|
1403
|
+
|
|
1404
|
+
// Call the block received callback to validate the proposal.
|
|
1405
|
+
// Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
|
|
1406
|
+
const isValid = await this.blockReceivedCallback(block, sender);
|
|
1407
|
+
if (!isValid) {
|
|
1408
|
+
this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
|
|
1409
|
+
// Release the protections this proposal created so its txs return to pending. Only entries still
|
|
1410
|
+
// keyed to this slot are cleared, so a tx referenced by a live proposal at another slot stays protected.
|
|
1411
|
+
await this.mempools.txPool.unprotectTxs(block.txHashes, slot);
|
|
866
1412
|
}
|
|
1413
|
+
}
|
|
867
1414
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1415
|
+
/**
|
|
1416
|
+
* Handle a gossiped checkpoint proposal.
|
|
1417
|
+
* Validates and processes the checkpoint proposal, then triggers the callback for attestation.
|
|
1418
|
+
*/
|
|
1419
|
+
protected async handleGossipedCheckpointProposal(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
1420
|
+
const {
|
|
1421
|
+
result,
|
|
1422
|
+
obj: checkpoint,
|
|
1423
|
+
metadata: { isEquivocated, processBlock, isOversized } = {},
|
|
1424
|
+
} = await this.validateReceivedMessage<
|
|
1425
|
+
CheckpointProposal,
|
|
1426
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1427
|
+
>(
|
|
1428
|
+
() => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
|
|
1429
|
+
msgId,
|
|
1430
|
+
source,
|
|
1431
|
+
TopicType.checkpoint_proposal,
|
|
1432
|
+
);
|
|
872
1433
|
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
this.
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1434
|
+
// An oversized checkpoint is re-broadcast as slashing evidence but never attested or processed.
|
|
1435
|
+
// Process checkpoint proposal if valid and neither equivocated nor oversized.
|
|
1436
|
+
const processCheckpointFn = () =>
|
|
1437
|
+
result === TopicValidatorResult.Accept && checkpoint && !isEquivocated && !isOversized
|
|
1438
|
+
? this.processValidCheckpointProposal(checkpoint.toCore(), source)
|
|
1439
|
+
: Promise.resolve();
|
|
1440
|
+
|
|
1441
|
+
// If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
|
|
1442
|
+
// TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
|
|
1443
|
+
const processBlockFn = () =>
|
|
1444
|
+
processBlock && checkpoint && checkpoint.getBlockProposal()
|
|
1445
|
+
? this.processValidBlockProposal(checkpoint.getBlockProposal()!, source)
|
|
1446
|
+
: Promise.resolve();
|
|
1447
|
+
|
|
1448
|
+
// A node that skips checkpoint validation attests without re-executing the embedded last block, so run
|
|
1449
|
+
// the checkpoint callback first: this creates and broadcasts the attestation before the block is
|
|
1450
|
+
// processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
|
|
1451
|
+
// waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
|
|
1452
|
+
// window, after which peers reject it as stale.
|
|
1453
|
+
if (this.config.skipCheckpointProposalValidation) {
|
|
1454
|
+
await processCheckpointFn();
|
|
1455
|
+
await processBlockFn();
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// Process the block first, since it's required for the checkpoint proposal validation.
|
|
1460
|
+
await processBlockFn();
|
|
1461
|
+
await processCheckpointFn();
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* Validates a checkpoint proposal. Penalizes peer if validation fails. Adds the checkpoint and
|
|
1466
|
+
* its last block (if present) to the mempool if valid. Triggers equivocation detection on both.
|
|
1467
|
+
*/
|
|
1468
|
+
@trackSpan('Libp2pService.validateAndStoreCheckpointProposal', (_peerId, checkpoint) => ({
|
|
1469
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1470
|
+
}))
|
|
1471
|
+
protected async validateAndStoreCheckpointProposal(
|
|
1472
|
+
peerId: PeerId,
|
|
1473
|
+
checkpoint: CheckpointProposal,
|
|
1474
|
+
): Promise<
|
|
1475
|
+
ReceivedMessageValidationResult<
|
|
1476
|
+
CheckpointProposal,
|
|
1477
|
+
{ isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
|
|
1478
|
+
>
|
|
1479
|
+
> {
|
|
1480
|
+
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1481
|
+
|
|
1482
|
+
if (validationResult.result === 'reject') {
|
|
1483
|
+
this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
|
|
1484
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (validationResult.result === 'ignore') {
|
|
1488
|
+
return { result: TopicValidatorResult.Ignore, obj: checkpoint };
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// Extract and try to add the block proposal first if present
|
|
1492
|
+
const blockProposal = checkpoint.getBlockProposal();
|
|
1493
|
+
let processBlock = false;
|
|
1494
|
+
let isOversized = false;
|
|
1495
|
+
if (blockProposal) {
|
|
1496
|
+
this.logger.debug(`Validating block proposal from propagated checkpoint`, {
|
|
1497
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1498
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1499
|
+
});
|
|
1500
|
+
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1501
|
+
const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
|
|
1502
|
+
isOversized = blockIsOversized ?? false;
|
|
1503
|
+
|
|
1504
|
+
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1505
|
+
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1506
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1507
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1508
|
+
isEquivocated,
|
|
1509
|
+
result: blockProposalResult.result,
|
|
881
1510
|
});
|
|
882
|
-
|
|
1511
|
+
return {
|
|
1512
|
+
result: TopicValidatorResult.Reject,
|
|
1513
|
+
severity:
|
|
1514
|
+
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1515
|
+
};
|
|
1516
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
|
|
1517
|
+
// An oversized terminal block is re-broadcast as slashing evidence but never processed.
|
|
1518
|
+
processBlock = true;
|
|
883
1519
|
}
|
|
884
1520
|
}
|
|
1521
|
+
|
|
1522
|
+
// Try to add the checkpoint proposal core: this handles existence check, cap check, and adding in one call
|
|
1523
|
+
const checkpointCore = checkpoint.toCore();
|
|
1524
|
+
const tryAddResult = await this.mempools.attestationPool.tryAddCheckpointProposal(checkpointCore);
|
|
1525
|
+
const { added, alreadyExists, count } = tryAddResult;
|
|
1526
|
+
const isEquivocated = count !== undefined && count > 1;
|
|
1527
|
+
|
|
1528
|
+
// Duplicate proposal received, do not re-broadcast
|
|
1529
|
+
if (alreadyExists) {
|
|
1530
|
+
this.logger.debug(`Ignoring duplicate checkpoint proposal received`, {
|
|
1531
|
+
...checkpoint.toCheckpointInfo(),
|
|
1532
|
+
source: peerId.toString(),
|
|
1533
|
+
});
|
|
1534
|
+
return {
|
|
1535
|
+
result: TopicValidatorResult.Ignore,
|
|
1536
|
+
obj: checkpoint,
|
|
1537
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
|
|
1542
|
+
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1543
|
+
if (!added) {
|
|
1544
|
+
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
1545
|
+
...checkpoint.toCheckpointInfo(),
|
|
1546
|
+
count,
|
|
1547
|
+
source: peerId.toString(),
|
|
1548
|
+
});
|
|
1549
|
+
return {
|
|
1550
|
+
result: TopicValidatorResult.Reject,
|
|
1551
|
+
obj: checkpoint,
|
|
1552
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1553
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1558
|
+
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1559
|
+
if (isEquivocated) {
|
|
1560
|
+
const proposer = checkpoint.getSender();
|
|
1561
|
+
this.logger.warn(`Detected duplicate checkpoint proposal (equivocation) at slot ${checkpoint.slotNumber}`, {
|
|
1562
|
+
...checkpoint.toCheckpointInfo(),
|
|
1563
|
+
source: peerId.toString(),
|
|
1564
|
+
proposer: proposer?.toString(),
|
|
1565
|
+
});
|
|
1566
|
+
// Invoke the duplicate callback on the first duplicate spotted only
|
|
1567
|
+
if (proposer && count === 2) {
|
|
1568
|
+
this.duplicateProposalCallback?.({ slot: checkpoint.slotNumber, proposer, type: 'checkpoint' });
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
result: TopicValidatorResult.Accept,
|
|
1572
|
+
obj: checkpoint,
|
|
1573
|
+
metadata: { isEquivocated, processBlock, isOversized },
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// Otherwise, we're good to go!
|
|
1578
|
+
return {
|
|
1579
|
+
result: TopicValidatorResult.Accept,
|
|
1580
|
+
obj: checkpoint,
|
|
1581
|
+
metadata: { processBlock, isEquivocated, isOversized },
|
|
1582
|
+
};
|
|
885
1583
|
}
|
|
886
1584
|
|
|
887
1585
|
/**
|
|
888
|
-
*
|
|
889
|
-
*
|
|
1586
|
+
* Process a validated checkpoint proposal.
|
|
1587
|
+
* Note: The proposal was already added to the pool by tryAddCheckpointProposal in handleGossipedCheckpointProposal.
|
|
890
1588
|
*/
|
|
891
|
-
@trackSpan('Libp2pService.
|
|
892
|
-
[Attributes.SLOT_NUMBER]:
|
|
893
|
-
[Attributes.BLOCK_ARCHIVE]:
|
|
894
|
-
[Attributes.P2P_ID]: await
|
|
1589
|
+
@trackSpan('Libp2pService.processValidCheckpointProposal', async checkpoint => ({
|
|
1590
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber,
|
|
1591
|
+
[Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
|
|
1592
|
+
[Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
895
1593
|
}))
|
|
896
|
-
|
|
897
|
-
|
|
1594
|
+
protected async processValidCheckpointProposal(checkpoint: CheckpointProposalCore, sender: PeerId) {
|
|
1595
|
+
const slot = checkpoint.slotNumber;
|
|
1596
|
+
this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
1597
|
+
p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
|
|
1598
|
+
slot: checkpoint.slotNumber,
|
|
1599
|
+
archive: checkpoint.archive.toString(),
|
|
1600
|
+
source: sender.toString(),
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
|
|
1604
|
+
|
|
1605
|
+
// Call the checkpoint received callback with the core version (without lastBlock)
|
|
1606
|
+
// to validate and potentially generate attestations
|
|
1607
|
+
const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
|
|
1608
|
+
if (attestations && attestations.length > 0) {
|
|
1609
|
+
// If the callback returned attestations, add them to the pool and propagate them
|
|
1610
|
+
await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
|
|
1611
|
+
for (const attestation of attestations) {
|
|
1612
|
+
await this.propagate(attestation);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
898
1615
|
}
|
|
899
1616
|
|
|
900
1617
|
/**
|
|
@@ -902,212 +1619,186 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
902
1619
|
* @param message - The message to propagate.
|
|
903
1620
|
*/
|
|
904
1621
|
public async propagate<T extends Gossipable>(message: T) {
|
|
905
|
-
const p2pMessageIdentifier = await message.
|
|
1622
|
+
const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
|
|
906
1623
|
this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
|
|
907
|
-
void this.
|
|
908
|
-
.
|
|
909
|
-
|
|
910
|
-
})
|
|
911
|
-
.catch(error => {
|
|
912
|
-
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
913
|
-
});
|
|
1624
|
+
void this.sendToPeers(message).catch(error => {
|
|
1625
|
+
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
1626
|
+
});
|
|
914
1627
|
}
|
|
915
1628
|
|
|
916
1629
|
/**
|
|
917
|
-
* Validate the requested block transactions.
|
|
1630
|
+
* Validate the requested block transactions request-response consistency.
|
|
1631
|
+
* It does NOT validate the transactions themselves.
|
|
918
1632
|
* @param request - The block transactions request.
|
|
919
1633
|
* @param response - The block transactions response.
|
|
920
1634
|
* @param peerId - The ID of the peer that made the request.
|
|
921
|
-
* @returns True if the
|
|
1635
|
+
* @returns True if the request-response is consistent, false otherwise.
|
|
922
1636
|
*/
|
|
923
|
-
@trackSpan('Libp2pService.
|
|
924
|
-
[Attributes.
|
|
1637
|
+
@trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
|
|
1638
|
+
[Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
|
|
925
1639
|
}))
|
|
926
|
-
|
|
927
|
-
|
|
1640
|
+
protected async validateRequestedBlockTxsConsistency(
|
|
1641
|
+
request: BlockTxsRequest,
|
|
928
1642
|
response: BlockTxsResponse,
|
|
929
1643
|
peerId: PeerId,
|
|
930
1644
|
): Promise<boolean> {
|
|
931
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
932
|
-
|
|
933
1645
|
try {
|
|
934
|
-
//
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
} else {
|
|
941
|
-
this.logger.error(`Error during validation of requested block txs`, e);
|
|
1646
|
+
// Check for duplicates txs (or hashes) in the response.
|
|
1647
|
+
const returnedHashes = response.txs.map(tx => tx.getTxHash().toString());
|
|
1648
|
+
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1649
|
+
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1650
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1651
|
+
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
942
1652
|
}
|
|
943
1653
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
961
|
-
*/
|
|
962
|
-
@trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
|
|
963
|
-
[Attributes.TX_HASH]: requestedTxHash.toString(),
|
|
964
|
-
}))
|
|
965
|
-
private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
|
|
966
|
-
const requested = new Set(requestedTxHash.map(h => h.toString()));
|
|
967
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
968
|
-
|
|
969
|
-
//TODO: (mralj) - this is somewhat naive implementation, if single tx is invlid we consider the whole response invalid.
|
|
970
|
-
// I think we should still extract the valid txs and return them, so that we can still use the response.
|
|
971
|
-
try {
|
|
972
|
-
await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
|
|
973
|
-
return true;
|
|
974
|
-
} catch (e: any) {
|
|
975
|
-
if (e instanceof ValidationError) {
|
|
976
|
-
this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
|
|
977
|
-
} else {
|
|
978
|
-
this.logger.error(`Error during validation of requested txs`, e);
|
|
1654
|
+
// We get the block tx hashes from the proposal or the archiver.
|
|
1655
|
+
const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
|
|
1656
|
+
const blockTxHashes =
|
|
1657
|
+
proposal?.txHashes ??
|
|
1658
|
+
(await this.archiver.getBlock({ archive: request.archiveRoot }))?.body.txEffects.map(e => e.txHash);
|
|
1659
|
+
|
|
1660
|
+
// If we don't have the block, we can't verify membership/order of the returned txs.
|
|
1661
|
+
if (!blockTxHashes) {
|
|
1662
|
+
// This shouldn't happen, since we asked for the block (proposal).
|
|
1663
|
+
// Neither a local proposal nor an archived block: we cannot verify membership/order of the
|
|
1664
|
+
// returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
|
|
1665
|
+
this.logger.warn(
|
|
1666
|
+
`Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`,
|
|
1667
|
+
);
|
|
1668
|
+
// NOTE: We mark the response as invalid, given the limitations of a true/false return value.
|
|
1669
|
+
return false;
|
|
979
1670
|
}
|
|
980
1671
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
}
|
|
994
|
-
new TxProofValidator(this.proofVerifier),
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
|
|
999
|
-
if (!(await tx.validateTxHash())) {
|
|
1000
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1001
|
-
throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1005
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1006
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
const { result } = await txValidator.validateTx(tx);
|
|
1010
|
-
if (result === 'invalid') {
|
|
1011
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1012
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
@trackSpan('Libp2pService.validatePropagatedTx', tx => ({
|
|
1017
|
-
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
1018
|
-
}))
|
|
1019
|
-
private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
|
|
1020
|
-
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1672
|
+
// Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
|
|
1673
|
+
const uniqueRequestedHashes = new Set([
|
|
1674
|
+
// tx hashes requested by index.
|
|
1675
|
+
...request.txIndices.getTrueIndices().map(i => blockTxHashes[i].toString()),
|
|
1676
|
+
// explicitly requested tx hashes.
|
|
1677
|
+
...request.txHashes.map(h => h.toString()),
|
|
1678
|
+
]);
|
|
1679
|
+
if (!returnedHashes.every(h => uniqueRequestedHashes.has(h))) {
|
|
1680
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1681
|
+
throw new ValidationError(
|
|
1682
|
+
'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1021
1685
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1686
|
+
// Tx indices information is optional, but if present, we need to verify a few things.
|
|
1687
|
+
if (!response.peerHasBlock()) {
|
|
1688
|
+
this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
|
|
1689
|
+
return true;
|
|
1690
|
+
}
|
|
1025
1691
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1692
|
+
// If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
|
|
1693
|
+
if (response.txIndices.getLength() !== blockTxHashes.length) {
|
|
1694
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1695
|
+
throw new ValidationError(
|
|
1696
|
+
`Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1028
1699
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1700
|
+
// For every index we requested that the peer claims to have (per its bitvector), the
|
|
1701
|
+
// corresponding tx must be present in the response. A peer advertising availability but
|
|
1702
|
+
// withholding the tx is inconsistent.
|
|
1703
|
+
const returnedHashSet = new Set(returnedHashes);
|
|
1704
|
+
const expectedIndices = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
|
|
1705
|
+
const missingExpected = expectedIndices.some(i => !returnedHashSet.has(blockTxHashes[i].toString()));
|
|
1706
|
+
if (missingExpected) {
|
|
1707
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1708
|
+
throw new ValidationError('Peer advertised requested txs via indices but did not return them');
|
|
1031
1709
|
}
|
|
1032
|
-
const { name } = outcome.failure;
|
|
1033
|
-
let { severity } = outcome.failure;
|
|
1034
1710
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1711
|
+
return true;
|
|
1712
|
+
} catch (e: any) {
|
|
1713
|
+
if (e instanceof ValidationError) {
|
|
1714
|
+
this.logger.warn(`Failed validation for requested block txs from peer ${peerId.toString()}`);
|
|
1715
|
+
} else {
|
|
1716
|
+
this.logger.error(`Error during validation of requested block txs`, e);
|
|
1039
1717
|
}
|
|
1040
1718
|
|
|
1041
|
-
this.peerManager.penalizePeer(peerId, severity);
|
|
1042
1719
|
return false;
|
|
1043
1720
|
}
|
|
1044
|
-
return true;
|
|
1045
1721
|
}
|
|
1046
1722
|
|
|
1047
|
-
private
|
|
1048
|
-
|
|
1049
|
-
return this.feesCache.gasFees;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
const header = await this.archiver.getBlockHeader(blockNumber);
|
|
1053
|
-
const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
|
|
1054
|
-
this.feesCache = { blockNumber, gasFees };
|
|
1055
|
-
return gasFees;
|
|
1723
|
+
private getGasFees(): Promise<GasFees> {
|
|
1724
|
+
return this.blockMinFeesProvider.getCurrentMinFees();
|
|
1056
1725
|
}
|
|
1057
1726
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1727
|
+
/**
|
|
1728
|
+
* Get the BatchTxRequesterLibP2PService dependencies for creating BatchTxRequester instances
|
|
1729
|
+
*/
|
|
1730
|
+
public getBatchTxRequesterService(): BatchTxRequesterLibP2PService {
|
|
1731
|
+
return {
|
|
1732
|
+
reqResp: this.reqresp,
|
|
1733
|
+
connectionSampler: this.reqresp.getConnectionSampler(),
|
|
1734
|
+
txValidatorConfig: {
|
|
1735
|
+
l1ChainId: this.config.l1ChainId,
|
|
1736
|
+
rollupVersion: this.config.rollupVersion,
|
|
1737
|
+
proofVerifier: this.proofVerifier,
|
|
1738
|
+
txValidationCache: this.txValidationCache,
|
|
1739
|
+
},
|
|
1740
|
+
peerScoring: this.peerManager,
|
|
1741
|
+
validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1060
1744
|
|
|
1061
|
-
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1745
|
+
public async validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
|
|
1746
|
+
const validator = createTxValidatorForBlockProposalReceivedTxs(
|
|
1747
|
+
this.proofVerifier,
|
|
1748
|
+
{ l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
|
|
1749
|
+
this.logger.getBindings(),
|
|
1750
|
+
this.txValidationCache,
|
|
1751
|
+
);
|
|
1064
1752
|
|
|
1065
|
-
await Promise.all(
|
|
1753
|
+
const results = await Promise.all(
|
|
1066
1754
|
txs.map(async tx => {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
if (!outcome.allPassed) {
|
|
1070
|
-
throw new Error('Invalid tx detected', { cause: { outcome } });
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1755
|
+
const result = await validator.validateTx(tx);
|
|
1756
|
+
return result.result !== 'invalid';
|
|
1073
1757
|
}),
|
|
1074
1758
|
);
|
|
1759
|
+
if (results.some(value => value === false)) {
|
|
1760
|
+
throw new Error('Invalid tx detected');
|
|
1761
|
+
}
|
|
1075
1762
|
}
|
|
1076
1763
|
|
|
1077
|
-
/**
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
* Each validator is a pair of a validator and a severity.
|
|
1081
|
-
* If a validator fails, the peer is penalized with the severity of the validator.
|
|
1082
|
-
*
|
|
1083
|
-
* @param currentBlockNumber - The current synced block number.
|
|
1084
|
-
* @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
|
|
1085
|
-
* @returns The message validators.
|
|
1086
|
-
*/
|
|
1087
|
-
private async createMessageValidators(
|
|
1088
|
-
currentBlockNumber: number,
|
|
1764
|
+
/** Creates the first stage (fast) validators for gossiped transactions. */
|
|
1765
|
+
protected async createFirstStageMessageValidators(
|
|
1766
|
+
currentBlockNumber: BlockNumber,
|
|
1089
1767
|
nextSlotTimestamp: UInt64,
|
|
1090
|
-
): Promise<Record<string,
|
|
1091
|
-
const gasFees = await this.getGasFees(
|
|
1092
|
-
const allowedInSetup =
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1768
|
+
): Promise<Record<string, TransactionValidator>> {
|
|
1769
|
+
const gasFees = await this.getGasFees();
|
|
1770
|
+
const allowedInSetup = [
|
|
1771
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
1772
|
+
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1773
|
+
];
|
|
1774
|
+
const blockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1775
|
+
const l1Constants = await this.archiver.getL1Constants();
|
|
1776
|
+
const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
|
|
1777
|
+
|
|
1778
|
+
return createFirstStageTxValidationsForGossipedTransactions(
|
|
1097
1779
|
nextSlotTimestamp,
|
|
1098
|
-
|
|
1780
|
+
blockNumber,
|
|
1099
1781
|
this.worldStateSynchronizer,
|
|
1100
1782
|
gasFees,
|
|
1101
1783
|
this.config.l1ChainId,
|
|
1102
1784
|
this.config.rollupVersion,
|
|
1103
1785
|
protocolContractsHash,
|
|
1104
1786
|
this.archiver,
|
|
1105
|
-
this.proofVerifier,
|
|
1106
1787
|
!this.config.disableTransactions,
|
|
1107
1788
|
allowedInSetup,
|
|
1789
|
+
this.logger.getBindings(),
|
|
1790
|
+
{
|
|
1791
|
+
maxTxL2Gas: networkTxGasLimits.l2Gas,
|
|
1792
|
+
maxTxDAGas: networkTxGasLimits.daGas,
|
|
1793
|
+
},
|
|
1108
1794
|
);
|
|
1109
1795
|
}
|
|
1110
1796
|
|
|
1797
|
+
/** Creates the second stage (expensive proof verification) validators for gossiped transactions. */
|
|
1798
|
+
protected createSecondStageMessageValidators(): Record<string, TransactionValidator> {
|
|
1799
|
+
return createSecondStageTxValidationsForGossipedTransactions(this.proofVerifier, this.logger.getBindings());
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1111
1802
|
/**
|
|
1112
1803
|
* Run validations on a tx.
|
|
1113
1804
|
* @param tx - The tx to validate.
|
|
@@ -1116,7 +1807,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1116
1807
|
*/
|
|
1117
1808
|
private async runValidations(
|
|
1118
1809
|
tx: Tx,
|
|
1119
|
-
messageValidators: Record<string,
|
|
1810
|
+
messageValidators: Record<string, TransactionValidator>,
|
|
1120
1811
|
): Promise<ValidationOutcome> {
|
|
1121
1812
|
const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
|
|
1122
1813
|
const { result } = await validator.validateTx(tx);
|
|
@@ -1125,8 +1816,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1125
1816
|
|
|
1126
1817
|
// A promise that resolves when all validations have been run
|
|
1127
1818
|
const allValidations = await Promise.all(validationPromises);
|
|
1128
|
-
const
|
|
1129
|
-
if (
|
|
1819
|
+
const failures = allValidations.filter(x => !x.isValid);
|
|
1820
|
+
if (failures.length > 0) {
|
|
1821
|
+
// Pick the most severe failure (lowest tolerance = harshest penalty)
|
|
1822
|
+
const failed = maxBy(failures, f => PeerErrorSeverityByHarshness.indexOf(f.severity))!;
|
|
1130
1823
|
return {
|
|
1131
1824
|
allPassed: false,
|
|
1132
1825
|
failure: {
|
|
@@ -1153,20 +1846,23 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1153
1846
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
1154
1847
|
* @returns Severity
|
|
1155
1848
|
*/
|
|
1156
|
-
private async handleDoubleSpendFailure(tx: Tx, blockNumber:
|
|
1849
|
+
private async handleDoubleSpendFailure(tx: Tx, blockNumber: BlockNumber): Promise<PeerErrorSeverity> {
|
|
1157
1850
|
if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
|
|
1158
1851
|
return PeerErrorSeverity.HighToleranceError;
|
|
1159
1852
|
}
|
|
1160
1853
|
|
|
1161
|
-
const snapshotValidator = new DoubleSpendTxValidator(
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1854
|
+
const snapshotValidator = new DoubleSpendTxValidator(
|
|
1855
|
+
{
|
|
1856
|
+
nullifiersExist: async (nullifiers: Buffer[]) => {
|
|
1857
|
+
const merkleTree = this.worldStateSynchronizer.getSnapshot(
|
|
1858
|
+
BlockNumber(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow),
|
|
1859
|
+
);
|
|
1860
|
+
const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
1861
|
+
return indices.map(index => index !== undefined);
|
|
1862
|
+
},
|
|
1168
1863
|
},
|
|
1169
|
-
|
|
1864
|
+
this.logger.getBindings(),
|
|
1865
|
+
);
|
|
1170
1866
|
|
|
1171
1867
|
const validSnapshot = await snapshotValidator.validateTx(tx);
|
|
1172
1868
|
if (validSnapshot.result !== 'valid') {
|
|
@@ -1176,47 +1872,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1176
1872
|
return PeerErrorSeverity.HighToleranceError;
|
|
1177
1873
|
}
|
|
1178
1874
|
|
|
1179
|
-
/**
|
|
1180
|
-
* Validate an attestation.
|
|
1181
|
-
*
|
|
1182
|
-
* @param attestation - The attestation to validate.
|
|
1183
|
-
* @returns True if the attestation is valid, false otherwise.
|
|
1184
|
-
*/
|
|
1185
|
-
@trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
|
|
1186
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
|
|
1187
|
-
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
1188
|
-
[Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
|
|
1189
|
-
}))
|
|
1190
|
-
public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
|
|
1191
|
-
const severity = await this.attestationValidator.validate(attestation);
|
|
1192
|
-
if (severity) {
|
|
1193
|
-
this.peerManager.penalizePeer(peerId, severity);
|
|
1194
|
-
return false;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
return true;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
/**
|
|
1201
|
-
* Validate a block proposal.
|
|
1202
|
-
*
|
|
1203
|
-
* @param block - The block proposal to validate.
|
|
1204
|
-
* @returns True if the block proposal is valid, false otherwise.
|
|
1205
|
-
*/
|
|
1206
|
-
@trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
|
|
1207
|
-
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
1208
|
-
}))
|
|
1209
|
-
public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
|
|
1210
|
-
const severity = await this.blockProposalValidator.validate(block);
|
|
1211
|
-
if (severity) {
|
|
1212
|
-
this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1213
|
-
this.peerManager.penalizePeer(peerId, severity);
|
|
1214
|
-
return false;
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
return true;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
1875
|
public getPeerScore(peerId: PeerId): number {
|
|
1221
1876
|
return this.node.services.pubsub.score.score(peerId.toString());
|
|
1222
1877
|
}
|
|
@@ -1228,7 +1883,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1228
1883
|
private async sendToPeers<T extends Gossipable>(message: T) {
|
|
1229
1884
|
const parent = message.constructor as typeof Gossipable;
|
|
1230
1885
|
|
|
1231
|
-
const identifier = await message.
|
|
1886
|
+
const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
|
|
1232
1887
|
this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
|
|
1233
1888
|
|
|
1234
1889
|
const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
|