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