@aztec-labs/p2p 6.0.0-nightly.20260829
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 +134 -0
- package/dest/bootstrap/bootstrap.d.ts +39 -0
- package/dest/bootstrap/bootstrap.d.ts.map +1 -0
- package/dest/bootstrap/bootstrap.js +152 -0
- package/dest/client/factory.d.ts +32 -0
- package/dest/client/factory.d.ts.map +1 -0
- package/dest/client/factory.js +125 -0
- package/dest/client/index.d.ts +4 -0
- package/dest/client/index.d.ts.map +1 -0
- package/dest/client/index.js +3 -0
- package/dest/client/interface.d.ts +223 -0
- package/dest/client/interface.d.ts.map +1 -0
- package/dest/client/interface.js +9 -0
- package/dest/client/p2p_client.d.ts +202 -0
- package/dest/client/p2p_client.d.ts.map +1 -0
- package/dest/client/p2p_client.js +1051 -0
- package/dest/config.d.ts +295 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +458 -0
- package/dest/enr/generate-enr.d.ts +17 -0
- package/dest/enr/generate-enr.d.ts.map +1 -0
- package/dest/enr/generate-enr.js +52 -0
- package/dest/enr/index.d.ts +2 -0
- package/dest/enr/index.d.ts.map +1 -0
- package/dest/enr/index.js +1 -0
- 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 +21 -0
- package/dest/errors/reqresp.error.d.ts.map +1 -0
- package/dest/errors/reqresp.error.js +23 -0
- 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 +11 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +9 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +194 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.js +521 -0
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +3 -0
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +745 -0
- package/dest/mem_pools/attestation_pool/index.d.ts +2 -0
- package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -0
- package/dest/mem_pools/attestation_pool/index.js +1 -0
- package/dest/mem_pools/attestation_pool/mocks.d.ts +242 -0
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -0
- package/dest/mem_pools/attestation_pool/mocks.js +33 -0
- package/dest/mem_pools/index.d.ts +4 -0
- package/dest/mem_pools/index.d.ts.map +1 -0
- package/dest/mem_pools/index.js +1 -0
- package/dest/mem_pools/instrumentation.d.ts +41 -0
- package/dest/mem_pools/instrumentation.d.ts.map +1 -0
- package/dest/mem_pools/instrumentation.js +120 -0
- package/dest/mem_pools/interface.d.ts +10 -0
- package/dest/mem_pools/interface.d.ts.map +1 -0
- package/dest/mem_pools/interface.js +3 -0
- 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 +48 -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 +112 -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 +99 -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 +64 -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 +64 -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 +92 -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 +79 -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 +25 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.js +67 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +243 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.js +11 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +137 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +223 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +122 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +368 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +75 -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 +218 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +122 -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 +1039 -0
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +17 -0
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -0
- package/dest/msg_validators/attestation_validator/attestation_validator.js +111 -0
- 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 +3 -0
- package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -0
- package/dest/msg_validators/attestation_validator/index.js +2 -0
- package/dest/msg_validators/index.d.ts +4 -0
- package/dest/msg_validators/index.d.ts.map +1 -0
- package/dest/msg_validators/index.js +3 -0
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +10 -0
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -0
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.js +36 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +16 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +14 -0
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +16 -0
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +26 -0
- package/dest/msg_validators/proposal_validator/index.d.ts +4 -0
- package/dest/msg_validators/proposal_validator/index.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/index.js +3 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +40 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -0
- package/dest/msg_validators/proposal_validator/proposal_validator.js +186 -0
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +7 -0
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +25 -0
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +4 -0
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +31 -0
- 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 +14 -0
- package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/archive_cache.js +22 -0
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +25 -0
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/block_header_validator.js +27 -0
- 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 +9 -0
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/data_validator.js +114 -0
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +21 -0
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/double_spend_validator.js +36 -0
- package/dest/msg_validators/tx_validator/factory.d.ts +148 -0
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/factory.js +302 -0
- 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_limits_validator.d.ts +73 -0
- package/dest/msg_validators/tx_validator/gas_limits_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/gas_limits_validator.js +107 -0
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +65 -0
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/gas_validator.js +107 -0
- package/dest/msg_validators/tx_validator/index.d.ts +20 -0
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/index.js +19 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +15 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/metadata_validator.js +63 -0
- 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 +35 -0
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/phases_validator.js +141 -0
- 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 +17 -0
- package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/test_utils.js +22 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +30 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.js +32 -0
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +9 -0
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_permitted_validator.js +24 -0
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +11 -0
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +28 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
- package/dest/services/data_store.d.ts +27 -0
- package/dest/services/data_store.d.ts.map +1 -0
- package/dest/services/data_store.js +192 -0
- package/dest/services/discv5/discV5_service.d.ts +54 -0
- package/dest/services/discv5/discV5_service.d.ts.map +1 -0
- package/dest/services/discv5/discV5_service.js +337 -0
- package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
- package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
- package/dest/services/discv5/persisted_enr_store.js +145 -0
- package/dest/services/dummy_service.d.ts +150 -0
- package/dest/services/dummy_service.d.ts.map +1 -0
- package/dest/services/dummy_service.js +228 -0
- package/dest/services/encoding.d.ts +67 -0
- package/dest/services/encoding.d.ts.map +1 -0
- package/dest/services/encoding.js +152 -0
- 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 +25 -0
- package/dest/services/gossipsub/scoring.d.ts.map +1 -0
- package/dest/services/gossipsub/scoring.js +27 -0
- package/dest/services/gossipsub/topic_score_params.d.ts +167 -0
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
- package/dest/services/gossipsub/topic_score_params.js +336 -0
- package/dest/services/index.d.ts +8 -0
- package/dest/services/index.d.ts.map +1 -0
- package/dest/services/index.js +7 -0
- package/dest/services/libp2p/instrumentation.d.ts +25 -0
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -0
- package/dest/services/libp2p/instrumentation.js +143 -0
- package/dest/services/libp2p/libp2p_service.d.ts +250 -0
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -0
- package/dest/services/libp2p/libp2p_service.js +1983 -0
- package/dest/services/peer-manager/interface.d.ts +23 -0
- package/dest/services/peer-manager/interface.d.ts.map +1 -0
- package/dest/services/peer-manager/interface.js +1 -0
- package/dest/services/peer-manager/metrics.d.ts +23 -0
- package/dest/services/peer-manager/metrics.d.ts.map +1 -0
- package/dest/services/peer-manager/metrics.js +71 -0
- package/dest/services/peer-manager/peer_manager.d.ts +202 -0
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -0
- package/dest/services/peer-manager/peer_manager.js +1015 -0
- package/dest/services/peer-manager/peer_scoring.d.ts +65 -0
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -0
- package/dest/services/peer-manager/peer_scoring.js +239 -0
- 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 +30 -0
- package/dest/services/reqresp/config.d.ts.map +1 -0
- package/dest/services/reqresp/config.js +45 -0
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +76 -0
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -0
- package/dest/services/reqresp/connection-sampler/connection_sampler.js +251 -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 +7 -0
- package/dest/services/reqresp/index.d.ts.map +1 -0
- package/dest/services/reqresp/index.js +5 -0
- package/dest/services/reqresp/interface.d.ts +174 -0
- package/dest/services/reqresp/interface.d.ts.map +1 -0
- package/dest/services/reqresp/interface.js +107 -0
- package/dest/services/reqresp/metrics.d.ts +16 -0
- package/dest/services/reqresp/metrics.d.ts.map +1 -0
- package/dest/services/reqresp/metrics.js +50 -0
- package/dest/services/reqresp/protocols/auth.d.ts +43 -0
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/auth.js +71 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +35 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +88 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +13 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +60 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +75 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +139 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +4 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/index.js +3 -0
- package/dest/services/reqresp/protocols/goodbye.d.ts +49 -0
- package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/goodbye.js +87 -0
- package/dest/services/reqresp/protocols/index.d.ts +10 -0
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/index.js +8 -0
- package/dest/services/reqresp/protocols/ping.d.ts +7 -0
- package/dest/services/reqresp/protocols/ping.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/ping.js +7 -0
- package/dest/services/reqresp/protocols/status.d.ts +42 -0
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/status.js +78 -0
- package/dest/services/reqresp/protocols/tx.d.ts +29 -0
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/tx.js +66 -0
- package/dest/services/reqresp/rate-limiter/index.d.ts +2 -0
- package/dest/services/reqresp/rate-limiter/index.d.ts.map +1 -0
- package/dest/services/reqresp/rate-limiter/index.js +1 -0
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +105 -0
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -0
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +194 -0
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +3 -0
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -0
- package/dest/services/reqresp/rate-limiter/rate_limits.js +64 -0
- package/dest/services/reqresp/reqresp.d.ts +107 -0
- package/dest/services/reqresp/reqresp.d.ts.map +1 -0
- package/dest/services/reqresp/reqresp.js +887 -0
- package/dest/services/reqresp/status.d.ts +37 -0
- package/dest/services/reqresp/status.d.ts.map +1 -0
- package/dest/services/reqresp/status.js +58 -0
- package/dest/services/service.d.ts +168 -0
- package/dest/services/service.d.ts.map +1 -0
- package/dest/services/service.js +5 -0
- package/dest/services/tx_collection/config.d.ts +25 -0
- package/dest/services/tx_collection/config.d.ts.map +1 -0
- package/dest/services/tx_collection/config.js +59 -0
- 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 +4 -0
- package/dest/services/tx_collection/index.d.ts.map +1 -0
- package/dest/services/tx_collection/index.js +3 -0
- package/dest/services/tx_collection/instrumentation.d.ts +10 -0
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_collection/instrumentation.js +30 -0
- 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 +104 -0
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection.js +356 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts +40 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection_sink.js +108 -0
- package/dest/services/tx_collection/tx_source.d.ts +24 -0
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_source.js +56 -0
- 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 +53 -0
- package/dest/services/tx_provider.d.ts.map +1 -0
- package/dest/services/tx_provider.js +223 -0
- package/dest/services/tx_provider_instrumentation.d.ts +16 -0
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -0
- package/dest/services/tx_provider_instrumentation.js +34 -0
- package/dest/test-helpers/generate-peer-id-private-keys.d.ts +7 -0
- package/dest/test-helpers/generate-peer-id-private-keys.d.ts.map +1 -0
- package/dest/test-helpers/generate-peer-id-private-keys.js +13 -0
- package/dest/test-helpers/get-ports.d.ts +7 -0
- package/dest/test-helpers/get-ports.d.ts.map +1 -0
- package/dest/test-helpers/get-ports.js +8 -0
- package/dest/test-helpers/index.d.ts +9 -0
- package/dest/test-helpers/index.d.ts.map +1 -0
- package/dest/test-helpers/index.js +8 -0
- package/dest/test-helpers/make-enrs.d.ts +16 -0
- package/dest/test-helpers/make-enrs.d.ts.map +1 -0
- package/dest/test-helpers/make-enrs.js +31 -0
- package/dest/test-helpers/make-test-p2p-clients.d.ts +63 -0
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -0
- package/dest/test-helpers/make-test-p2p-clients.js +140 -0
- package/dest/test-helpers/mock-pubsub.d.ts +109 -0
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -0
- package/dest/test-helpers/mock-pubsub.js +244 -0
- package/dest/test-helpers/mock-tx-helpers.d.ts +12 -0
- package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -0
- package/dest/test-helpers/mock-tx-helpers.js +19 -0
- package/dest/test-helpers/reqresp-nodes.d.ts +68 -0
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -0
- package/dest/test-helpers/reqresp-nodes.js +240 -0
- package/dest/test-helpers/test_tx_provider.d.ts +42 -0
- package/dest/test-helpers/test_tx_provider.d.ts.map +1 -0
- package/dest/test-helpers/test_tx_provider.js +44 -0
- package/dest/test-helpers/testbench-utils.d.ts +164 -0
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -0
- package/dest/test-helpers/testbench-utils.js +401 -0
- package/dest/testbench/p2p_client_testbench_worker.d.ts +26 -0
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -0
- package/dest/testbench/p2p_client_testbench_worker.js +364 -0
- package/dest/testbench/parse_log_file.d.ts +2 -0
- package/dest/testbench/parse_log_file.d.ts.map +1 -0
- package/dest/testbench/parse_log_file.js +131 -0
- package/dest/testbench/testbench.d.ts +2 -0
- package/dest/testbench/testbench.d.ts.map +1 -0
- package/dest/testbench/testbench.js +62 -0
- package/dest/testbench/worker_client_manager.d.ts +102 -0
- package/dest/testbench/worker_client_manager.d.ts.map +1 -0
- package/dest/testbench/worker_client_manager.js +492 -0
- package/dest/types/index.d.ts +34 -0
- package/dest/types/index.d.ts.map +1 -0
- package/dest/types/index.js +30 -0
- package/dest/util.d.ts +66 -0
- package/dest/util.d.ts.map +1 -0
- package/dest/util.js +167 -0
- package/dest/versioning.d.ts +9 -0
- package/dest/versioning.d.ts.map +1 -0
- package/dest/versioning.js +17 -0
- package/package.json +131 -0
- package/src/bootstrap/bootstrap.ts +178 -0
- package/src/client/factory.ts +282 -0
- package/src/client/index.ts +3 -0
- package/src/client/interface.ts +264 -0
- package/src/client/p2p_client.ts +846 -0
- package/src/client/test/p2p_client.batch_tx_requester.bench.README.md +197 -0
- package/src/config.ts +787 -0
- package/src/enr/generate-enr.ts +71 -0
- package/src/enr/index.ts +1 -0
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/errors/p2p-service.error.ts +11 -0
- package/src/errors/reqresp.error.ts +29 -0
- package/src/errors/tx-pool.error.ts +12 -0
- package/src/index.ts +11 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +648 -0
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +896 -0
- package/src/mem_pools/attestation_pool/index.ts +9 -0
- package/src/mem_pools/attestation_pool/mocks.ts +56 -0
- package/src/mem_pools/index.ts +3 -0
- package/src/mem_pools/instrumentation.ts +160 -0
- package/src/mem_pools/interface.ts +10 -0
- 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 +130 -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 +120 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +126 -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 +67 -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 +72 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +99 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +90 -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 +96 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +274 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +349 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +465 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +300 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +1242 -0
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +116 -0
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +107 -0
- package/src/msg_validators/attestation_validator/index.ts +2 -0
- package/src/msg_validators/index.ts +3 -0
- package/src/msg_validators/msg_seen_validator/msg_seen_validator.ts +36 -0
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +37 -0
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +49 -0
- package/src/msg_validators/proposal_validator/index.ts +3 -0
- package/src/msg_validators/proposal_validator/proposal_validator.ts +198 -0
- package/src/msg_validators/tx_validator/README.md +137 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +24 -0
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +30 -0
- package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
- package/src/msg_validators/tx_validator/archive_cache.ts +28 -0
- package/src/msg_validators/tx_validator/block_header_validator.ts +38 -0
- 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 +173 -0
- package/src/msg_validators/tx_validator/double_spend_validator.ts +46 -0
- package/src/msg_validators/tx_validator/factory.ts +490 -0
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +44 -0
- package/src/msg_validators/tx_validator/gas_limits_validator.ts +148 -0
- package/src/msg_validators/tx_validator/gas_validator.ts +149 -0
- package/src/msg_validators/tx_validator/index.ts +19 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +104 -0
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +173 -0
- package/src/msg_validators/tx_validator/size_validator.ts +22 -0
- package/src/msg_validators/tx_validator/test_utils.ts +43 -0
- package/src/msg_validators/tx_validator/timestamp_validator.ts +61 -0
- package/src/msg_validators/tx_validator/tx_permitted_validator.ts +22 -0
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +26 -0
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +100 -0
- package/src/services/data_store.ts +229 -0
- package/src/services/discv5/discV5_service.ts +413 -0
- package/src/services/discv5/persisted_enr_store.ts +157 -0
- package/src/services/dummy_service.ts +320 -0
- package/src/services/encoding.ts +170 -0
- package/src/services/gossipsub/README.md +663 -0
- package/src/services/gossipsub/index.ts +2 -0
- package/src/services/gossipsub/scoring.ts +37 -0
- package/src/services/gossipsub/topic_score_params.ts +477 -0
- package/src/services/index.ts +7 -0
- package/src/services/libp2p/instrumentation.ts +147 -0
- package/src/services/libp2p/libp2p_service.ts +1972 -0
- package/src/services/peer-manager/interface.ts +28 -0
- package/src/services/peer-manager/metrics.ts +90 -0
- package/src/services/peer-manager/peer_manager.ts +1221 -0
- package/src/services/peer-manager/peer_scoring.ts +265 -0
- package/src/services/reqresp/README.md +194 -0
- package/src/services/reqresp/batch-tx-requester/README.md +344 -0
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +659 -0
- package/src/services/reqresp/batch-tx-requester/config.ts +40 -0
- package/src/services/reqresp/batch-tx-requester/interface.ts +60 -0
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +167 -0
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +248 -0
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +24 -0
- package/src/services/reqresp/config.ts +69 -0
- package/src/services/reqresp/connection-sampler/connection_sampler.ts +287 -0
- package/src/services/reqresp/constants.ts +14 -0
- package/src/services/reqresp/index.ts +6 -0
- package/src/services/reqresp/interface.ts +241 -0
- package/src/services/reqresp/metrics.ts +68 -0
- package/src/services/reqresp/protocols/auth.ts +83 -0
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +107 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +79 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +170 -0
- package/src/services/reqresp/protocols/block_txs/index.ts +3 -0
- package/src/services/reqresp/protocols/goodbye.ts +102 -0
- package/src/services/reqresp/protocols/index.ts +9 -0
- package/src/services/reqresp/protocols/ping.ts +8 -0
- package/src/services/reqresp/protocols/status.ts +123 -0
- package/src/services/reqresp/protocols/tx.ts +76 -0
- package/src/services/reqresp/rate-limiter/index.ts +1 -0
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +240 -0
- package/src/services/reqresp/rate-limiter/rate_limits.ts +65 -0
- package/src/services/reqresp/reqresp.ts +675 -0
- package/src/services/reqresp/status.ts +68 -0
- package/src/services/service.ts +219 -0
- package/src/services/tx_collection/config.ts +89 -0
- 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 +3 -0
- package/src/services/tx_collection/instrumentation.ts +35 -0
- package/src/services/tx_collection/request_tracker.ts +127 -0
- package/src/services/tx_collection/tx_collection.ts +467 -0
- package/src/services/tx_collection/tx_collection_sink.ts +124 -0
- package/src/services/tx_collection/tx_source.ts +61 -0
- 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 +42 -0
- package/src/services/tx_file_store/tx_file_store.ts +163 -0
- package/src/services/tx_provider.ts +237 -0
- package/src/services/tx_provider_instrumentation.ts +54 -0
- package/src/test-helpers/generate-peer-id-private-keys.ts +15 -0
- package/src/test-helpers/get-ports.ts +8 -0
- package/src/test-helpers/index.ts +8 -0
- package/src/test-helpers/make-enrs.ts +42 -0
- package/src/test-helpers/make-test-p2p-clients.ts +213 -0
- package/src/test-helpers/mock-pubsub.ts +325 -0
- package/src/test-helpers/mock-tx-helpers.ts +24 -0
- package/src/test-helpers/reqresp-nodes.ts +327 -0
- package/src/test-helpers/test_tx_provider.ts +68 -0
- package/src/test-helpers/testbench-utils.ts +465 -0
- package/src/testbench/README.md +20 -0
- package/src/testbench/p2p_client_testbench_worker.ts +553 -0
- package/src/testbench/parse_log_file.ts +174 -0
- package/src/testbench/testbench.ts +66 -0
- package/src/testbench/worker_client_manager.ts +621 -0
- package/src/types/index.ts +38 -0
- package/src/util.ts +230 -0
- package/src/versioning.ts +26 -0
|
@@ -0,0 +1,1221 @@
|
|
|
1
|
+
import type { EpochCacheInterface } from '@aztec-labs/epoch-cache';
|
|
2
|
+
import { compactArray } from '@aztec-labs/foundation/collection';
|
|
3
|
+
import { makeEthSignDigest, tryRecoverAddress } from '@aztec-labs/foundation/crypto/secp256k1-signer';
|
|
4
|
+
import { Fr } from '@aztec-labs/foundation/curves/bn254';
|
|
5
|
+
import type { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
6
|
+
import { createLogger } from '@aztec-labs/foundation/log';
|
|
7
|
+
import { bufferToHex } from '@aztec-labs/foundation/string';
|
|
8
|
+
import { DateProvider } from '@aztec-labs/foundation/timer';
|
|
9
|
+
import type { PeerInfo, WorldStateSynchronizer } from '@aztec-labs/stdlib/interfaces/server';
|
|
10
|
+
import type { PeerErrorSeverity } from '@aztec-labs/stdlib/p2p';
|
|
11
|
+
import type { TelemetryClient } from '@aztec-labs/telemetry-client';
|
|
12
|
+
import type { Connection, PeerId } from '@libp2p/interface';
|
|
13
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
14
|
+
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
15
|
+
import { ENR } from '@nethermindeth/enr';
|
|
16
|
+
import { inspect } from 'util';
|
|
17
|
+
|
|
18
|
+
import type { P2PConfig } from '../../config.js';
|
|
19
|
+
import { PeerEvent } from '../../types/index.js';
|
|
20
|
+
import type { FullLibp2p } from '../../util.js';
|
|
21
|
+
import { ReqRespSubProtocol } from '../reqresp/interface.js';
|
|
22
|
+
import { AuthRequest, AuthResponse } from '../reqresp/protocols/auth.js';
|
|
23
|
+
import { GoodByeReason, prettyGoodbyeReason } from '../reqresp/protocols/goodbye.js';
|
|
24
|
+
import { StatusMessage } from '../reqresp/protocols/status.js';
|
|
25
|
+
import type { ReqResp } from '../reqresp/reqresp.js';
|
|
26
|
+
import { ReqRespStatus } from '../reqresp/status.js';
|
|
27
|
+
import type { PeerDiscoveryService } from '../service.js';
|
|
28
|
+
import type { PeerManagerInterface } from './interface.js';
|
|
29
|
+
import { PeerManagerMetrics } from './metrics.js';
|
|
30
|
+
import { PeerScoreState, type PeerScoring } from './peer_scoring.js';
|
|
31
|
+
|
|
32
|
+
const MAX_DIAL_ATTEMPTS = 3;
|
|
33
|
+
const MAX_CACHED_PEERS = 100;
|
|
34
|
+
const MAX_CACHED_PEER_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
35
|
+
const DEFAULT_FAILED_PEER_BAN_TIME_MS = 5 * 60 * 1000; // 5 minutes timeout after failing MAX_DIAL_ATTEMPTS
|
|
36
|
+
const GOODBYE_DIAL_TIMEOUT_MS = 1000;
|
|
37
|
+
const FAILED_AUTH_HANDSHAKE_EXPIRY_MS = 60 * 60 * 1000; // 1 hour
|
|
38
|
+
const ZERO_PEER_WARNING_THRESHOLD_HEARTBEATS = 3; // tolerate transient dips, such as at startup
|
|
39
|
+
const ZERO_PEER_WARNING_REPEAT_INTERVAL_MS = 60 * 1000;
|
|
40
|
+
|
|
41
|
+
type CachedPeer = {
|
|
42
|
+
peerId: PeerId;
|
|
43
|
+
enr: ENR;
|
|
44
|
+
multiaddrTcp: Multiaddr;
|
|
45
|
+
dialAttempts: number;
|
|
46
|
+
addedUnixMs: number;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type TimedOutPeer = {
|
|
50
|
+
peerId: string;
|
|
51
|
+
timeoutUntilMs: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type FailedAuthHandshakeEntry = {
|
|
55
|
+
count: number;
|
|
56
|
+
lastFailureTimestamp: number;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export class PeerManager implements PeerManagerInterface {
|
|
60
|
+
private cachedPeers: Map<string, CachedPeer> = new Map();
|
|
61
|
+
private heartbeatCounter: number = 0;
|
|
62
|
+
private displayPeerCountsPeerHeartbeat: number = 0;
|
|
63
|
+
private timedOutPeers: Map<string, TimedOutPeer> = new Map();
|
|
64
|
+
private trustedPeers: Set<string> = new Set();
|
|
65
|
+
private trustedPeersInitialized: boolean = false;
|
|
66
|
+
private privatePeers: Set<string> = new Set();
|
|
67
|
+
private privatePeersInitialized: boolean = false;
|
|
68
|
+
private preferredPeers: Set<string> = new Set();
|
|
69
|
+
private authenticatedPeerIdToValidatorAddress: Map<string, EthAddress> = new Map();
|
|
70
|
+
private authenticatedValidatorAddressToPeerId: Map<string, PeerId> = new Map();
|
|
71
|
+
private peersToBeDisconnected: Set<string> = new Set();
|
|
72
|
+
private failedAuthHandshakes: Map<string, FailedAuthHandshakeEntry> = new Map();
|
|
73
|
+
private validatorAddresses: EthAddress[] = [];
|
|
74
|
+
private initializedPreferredPeers: boolean = false;
|
|
75
|
+
private consecutiveZeroPeerHeartbeats: number = 0;
|
|
76
|
+
private zeroPeerWarningHeartbeats: number = 0;
|
|
77
|
+
private zeroPeerWarningIssued: boolean = false;
|
|
78
|
+
private lastConnectedPeerAtMs: number | undefined;
|
|
79
|
+
|
|
80
|
+
private metrics: PeerManagerMetrics;
|
|
81
|
+
private handlers: {
|
|
82
|
+
handleConnectedPeerEvent: (e: CustomEvent<PeerId>) => void;
|
|
83
|
+
handleDisconnectedPeerEvent: (e: CustomEvent<PeerId>) => void;
|
|
84
|
+
handleDiscoveredPeer: (enr: ENR) => Promise<void>;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
constructor(
|
|
88
|
+
private libP2PNode: FullLibp2p,
|
|
89
|
+
private peerDiscoveryService: PeerDiscoveryService,
|
|
90
|
+
private config: P2PConfig,
|
|
91
|
+
telemetryClient: TelemetryClient,
|
|
92
|
+
private logger = createLogger('p2p:peer-manager'),
|
|
93
|
+
private peerScoring: PeerScoring,
|
|
94
|
+
private reqresp: ReqResp,
|
|
95
|
+
private readonly worldStateSynchronizer: WorldStateSynchronizer,
|
|
96
|
+
private readonly protocolVersion: string,
|
|
97
|
+
private readonly epochCache: EpochCacheInterface,
|
|
98
|
+
private readonly dateProvider: DateProvider = new DateProvider(),
|
|
99
|
+
) {
|
|
100
|
+
if (this.config.p2pDisableStatusHandshake && this.config.p2pAllowOnlyValidators) {
|
|
101
|
+
throw new Error('Status handshake disabled but is required to allow only validators to connect.');
|
|
102
|
+
}
|
|
103
|
+
this.metrics = new PeerManagerMetrics(telemetryClient, 'PeerManager');
|
|
104
|
+
|
|
105
|
+
// Handle Discovered peers
|
|
106
|
+
this.handlers = {
|
|
107
|
+
handleConnectedPeerEvent: this.handleConnectedPeerEvent.bind(this),
|
|
108
|
+
handleDisconnectedPeerEvent: this.handleDisconnectedPeerEvent.bind(this),
|
|
109
|
+
handleDiscoveredPeer: (enr: ENR) =>
|
|
110
|
+
this.handleDiscoveredPeer(enr).catch(e => this.logger.error('Error handling discovered peer', e)),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Handle new established connections
|
|
114
|
+
this.libP2PNode.addEventListener(PeerEvent.CONNECTED, this.handlers.handleConnectedPeerEvent);
|
|
115
|
+
// Handle lost connections
|
|
116
|
+
this.libP2PNode.addEventListener(PeerEvent.DISCONNECTED, this.handlers.handleDisconnectedPeerEvent);
|
|
117
|
+
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
119
|
+
this.peerDiscoveryService?.on(PeerEvent.DISCOVERED, this.handlers.handleDiscoveredPeer);
|
|
120
|
+
|
|
121
|
+
// Display peer counts every 60 seconds
|
|
122
|
+
this.displayPeerCountsPeerHeartbeat = Math.floor(60_000 / this.config.peerCheckIntervalMS);
|
|
123
|
+
this.zeroPeerWarningHeartbeats = Math.max(
|
|
124
|
+
1,
|
|
125
|
+
Math.floor(ZERO_PEER_WARNING_REPEAT_INTERVAL_MS / this.config.peerCheckIntervalMS),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Initializes the trusted peers.
|
|
130
|
+
*
|
|
131
|
+
* This function is called when the peer manager is initialized.
|
|
132
|
+
*/
|
|
133
|
+
async initializePeers() {
|
|
134
|
+
if (this.config.trustedPeers) {
|
|
135
|
+
const trustedPeersEnrs: ENR[] = this.config.trustedPeers.map(enr => ENR.decodeTxt(enr));
|
|
136
|
+
await Promise.all(trustedPeersEnrs.map(enr => enr.peerId()))
|
|
137
|
+
.then(peerIds => peerIds.forEach(peerId => this.trustedPeers.add(peerId.toString())))
|
|
138
|
+
.finally(() => {
|
|
139
|
+
this.trustedPeersInitialized = true;
|
|
140
|
+
})
|
|
141
|
+
.catch(e => this.logger.error('Error initializing trusted peers', e));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.config.privatePeers) {
|
|
145
|
+
const privatePeersEnrs: ENR[] = this.config.privatePeers.map(enr => ENR.decodeTxt(enr));
|
|
146
|
+
await Promise.all(privatePeersEnrs.map(enr => enr.peerId()))
|
|
147
|
+
.then(peerIds =>
|
|
148
|
+
peerIds.forEach(peerId => {
|
|
149
|
+
this.trustedPeers.add(peerId.toString());
|
|
150
|
+
this.privatePeers.add(peerId.toString());
|
|
151
|
+
}),
|
|
152
|
+
)
|
|
153
|
+
.finally(() => {
|
|
154
|
+
if (!this.config.trustedPeers) {
|
|
155
|
+
this.trustedPeersInitialized = true;
|
|
156
|
+
}
|
|
157
|
+
this.privatePeersInitialized = true;
|
|
158
|
+
})
|
|
159
|
+
.catch(e => this.logger.error('Error initializing private peers', e));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (this.config.preferredPeers) {
|
|
163
|
+
const preferredPeersEnrs: ENR[] = this.config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
164
|
+
await Promise.all(preferredPeersEnrs.map(enr => enr.peerId()))
|
|
165
|
+
.then(peerIds => peerIds.forEach(peerId => this.preferredPeers.add(peerId.toString())))
|
|
166
|
+
.catch(e => this.logger.error('Error initializing preferred peers', e));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get tracer() {
|
|
171
|
+
return this.metrics.tracer;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public async heartbeat() {
|
|
175
|
+
this.heartbeatCounter++;
|
|
176
|
+
this.peerScoring.decayAllScores();
|
|
177
|
+
this.peerScoring.pruneExpiredBans();
|
|
178
|
+
this.cleanupExpiredTimeouts();
|
|
179
|
+
|
|
180
|
+
await this.setupDirectPeersIfValidator();
|
|
181
|
+
await this.updateAuthenticatedPeers();
|
|
182
|
+
await this.processScheduledDisconnects();
|
|
183
|
+
|
|
184
|
+
this.discover();
|
|
185
|
+
this.checkPeerConnectivity();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Flags a node that has been left without any connected peers, since it can neither gossip nor propagate txs.
|
|
190
|
+
*
|
|
191
|
+
* Warnings only start after a few consecutive zero-peer heartbeats so that startup or a transient dip stays quiet,
|
|
192
|
+
* and then repeat roughly once a minute instead of on every heartbeat. Recovery is logged once.
|
|
193
|
+
*/
|
|
194
|
+
private checkPeerConnectivity() {
|
|
195
|
+
const connectedPeerCount = this.libP2PNode.getPeers().length;
|
|
196
|
+
const now = this.dateProvider.now();
|
|
197
|
+
|
|
198
|
+
if (connectedPeerCount > 0) {
|
|
199
|
+
if (this.zeroPeerWarningIssued) {
|
|
200
|
+
this.logger.info(`Peer connectivity restored with ${connectedPeerCount} connected peers`, {
|
|
201
|
+
connectedPeerCount,
|
|
202
|
+
zeroPeerHeartbeats: this.consecutiveZeroPeerHeartbeats,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
this.consecutiveZeroPeerHeartbeats = 0;
|
|
206
|
+
this.zeroPeerWarningIssued = false;
|
|
207
|
+
this.lastConnectedPeerAtMs = now;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.consecutiveZeroPeerHeartbeats++;
|
|
212
|
+
const heartbeatsSinceThreshold = this.consecutiveZeroPeerHeartbeats - ZERO_PEER_WARNING_THRESHOLD_HEARTBEATS;
|
|
213
|
+
if (heartbeatsSinceThreshold < 0 || heartbeatsSinceThreshold % this.zeroPeerWarningHeartbeats !== 0) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.zeroPeerWarningIssued = true;
|
|
218
|
+
this.logger.warn('Node has no connected peers; gossip and tx propagation are unavailable', {
|
|
219
|
+
zeroPeerHeartbeats: this.consecutiveZeroPeerHeartbeats,
|
|
220
|
+
peerCheckIntervalMS: this.config.peerCheckIntervalMS,
|
|
221
|
+
msSinceLastConnectedPeer: this.lastConnectedPeerAtMs === undefined ? undefined : now - this.lastConnectedPeerAtMs,
|
|
222
|
+
cachedPeers: this.cachedPeers.size,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* If this node is connecting to preferred peers, make sure it is registered validator */
|
|
228
|
+
async setupDirectPeersIfValidator() {
|
|
229
|
+
if (!this.config.preferredPeers) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Already initialized preferred peers, don't wastefully repeat the same work
|
|
234
|
+
if (this.initializedPreferredPeers) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const registeredValidators = await this.epochCache.getRegisteredValidators();
|
|
239
|
+
const validatorSet = new Set(registeredValidators.map(v => v.toString()));
|
|
240
|
+
const isThisNodePartOfValidatorSet = this.validatorAddresses.some(v => validatorSet.has(v.toString()));
|
|
241
|
+
|
|
242
|
+
if (!isThisNodePartOfValidatorSet) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const preferredPeersEnrs: ENR[] = this.config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
247
|
+
await Promise.all(preferredPeersEnrs.map(enr => enr.peerId()))
|
|
248
|
+
.then(peerIds => peerIds.forEach(peerId => this.preferredPeers.add(peerId.toString())))
|
|
249
|
+
.catch(e => this.logger.error('Error initializing preferred peers', e));
|
|
250
|
+
|
|
251
|
+
const directPeers = compactArray(
|
|
252
|
+
await Promise.all(
|
|
253
|
+
preferredPeersEnrs.map(async enr => {
|
|
254
|
+
try {
|
|
255
|
+
const peerId = await enr.peerId();
|
|
256
|
+
const address = enr.getLocationMultiaddr('tcp');
|
|
257
|
+
if (address === undefined) {
|
|
258
|
+
throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
id: peerId,
|
|
262
|
+
addrs: [address],
|
|
263
|
+
};
|
|
264
|
+
} catch (err) {
|
|
265
|
+
// A malformed configured ENR shouldn't abort preferred-peer setup — skip it and log.
|
|
266
|
+
this.logger.warn(`Skipping preferred peer with invalid ENR`, { err });
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
}),
|
|
270
|
+
),
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
await Promise.all(
|
|
274
|
+
directPeers.map(peer => {
|
|
275
|
+
this.libP2PNode.services.pubsub.direct.add(peer.id.toString());
|
|
276
|
+
|
|
277
|
+
return this.libP2PNode.peerStore.merge(peer.id, { multiaddrs: peer.addrs });
|
|
278
|
+
}),
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
this.initializedPreferredPeers = true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Cleans up expired timeouts and stale failed-auth-handshake entries.
|
|
286
|
+
*
|
|
287
|
+
* When peers fail to dial after a number of retries, they are temporarily timed out.
|
|
288
|
+
* This function removes any peers that have been in the timed out state for too long.
|
|
289
|
+
* To give them a chance to reconnect.
|
|
290
|
+
*
|
|
291
|
+
* Also evicts entries from the failed-auth-handshake map whose expiry window has passed.
|
|
292
|
+
* Without this, peers that probe once and never reconnect would leave their entries in the
|
|
293
|
+
* map forever, causing an unbounded memory leak.
|
|
294
|
+
*/
|
|
295
|
+
private cleanupExpiredTimeouts() {
|
|
296
|
+
const now = this.dateProvider.now();
|
|
297
|
+
|
|
298
|
+
for (const [peerId, timedOutPeer] of this.timedOutPeers.entries()) {
|
|
299
|
+
if (now >= timedOutPeer.timeoutUntilMs) {
|
|
300
|
+
this.timedOutPeers.delete(peerId);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (const [id, entry] of this.failedAuthHandshakes.entries()) {
|
|
305
|
+
if (now - entry.lastFailureTimestamp > FAILED_AUTH_HANDSHAKE_EXPIRY_MS) {
|
|
306
|
+
this.failedAuthHandshakes.delete(id);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Processes scheduled disconnects during heartbeat.
|
|
313
|
+
*
|
|
314
|
+
* This batch processes all peers that have been marked for disconnect.
|
|
315
|
+
* preventing immediate disconnects that could cause libp2p state corruption.
|
|
316
|
+
*/
|
|
317
|
+
private async processScheduledDisconnects() {
|
|
318
|
+
if (this.peersToBeDisconnected.size === 0) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const peersToDisconnect = Array.from(this.peersToBeDisconnected);
|
|
323
|
+
|
|
324
|
+
this.logger.debug(`Processing ${peersToDisconnect.length} scheduled disconnects`);
|
|
325
|
+
try {
|
|
326
|
+
await Promise.all(
|
|
327
|
+
peersToDisconnect.map(async peerIdStr => {
|
|
328
|
+
if (await this.disconnectPeer(peerIdFromString(peerIdStr))) {
|
|
329
|
+
this.peersToBeDisconnected.delete(peerIdStr);
|
|
330
|
+
}
|
|
331
|
+
}),
|
|
332
|
+
);
|
|
333
|
+
this.logger.verbose(`Disconnected ${peersToDisconnect.length} peers`, { peersToDisconnect });
|
|
334
|
+
} catch (error) {
|
|
335
|
+
this.logger.error('Error when disconnecting from peers', error);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Performs Status Handshake with a connected peer.
|
|
341
|
+
* @param e - The connected peer event.
|
|
342
|
+
*/
|
|
343
|
+
private handleConnectedPeerEvent(e: CustomEvent<PeerId>) {
|
|
344
|
+
const peerId = e.detail;
|
|
345
|
+
this.logger.verbose(`Connected to peer ${peerId.toString()}`);
|
|
346
|
+
this.metrics.peerConnected(peerId);
|
|
347
|
+
if (this.config.p2pDisableStatusHandshake) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// If we are not configured to only allow validators then perform a status handshake
|
|
351
|
+
if (!this.config.p2pAllowOnlyValidators) {
|
|
352
|
+
void this.exchangeStatusHandshake(peerId);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// We are configured to only allow validators, but this doesn't apply to trusted, private peers or preferred peers
|
|
357
|
+
if (this.isProtectedPeer(peerId)) {
|
|
358
|
+
void this.exchangeStatusHandshake(peerId);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Initiate auth handshake
|
|
363
|
+
void this.exchangeAuthHandshake(peerId);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Simply logs the type of disconnected peer.
|
|
368
|
+
* @param e - The disconnected peer event.
|
|
369
|
+
*/
|
|
370
|
+
private handleDisconnectedPeerEvent(e: CustomEvent<PeerId>) {
|
|
371
|
+
const peerId = e.detail;
|
|
372
|
+
const peerIdStr = peerId.toString();
|
|
373
|
+
this.metrics.peerDisconnected(peerId);
|
|
374
|
+
this.logger.verbose(`Disconnected from peer ${peerIdStr}`);
|
|
375
|
+
const validatorAddress = this.authenticatedPeerIdToValidatorAddress.get(peerIdStr);
|
|
376
|
+
if (validatorAddress !== undefined) {
|
|
377
|
+
this.logger.info(
|
|
378
|
+
`Removing authentication for validator ${validatorAddress} at peer id ${peerIdStr} due to disconnection`,
|
|
379
|
+
);
|
|
380
|
+
this.authenticatedValidatorAddressToPeerId.delete(validatorAddress.toString());
|
|
381
|
+
this.authenticatedPeerIdToValidatorAddress.delete(peerIdStr);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (this.peerScoring.getScoreState(peerIdStr) === PeerScoreState.Healthy) {
|
|
385
|
+
this.peerScoring.removePeer(peerIdStr);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
public registerThisValidatorAddresses(address: EthAddress[]): void {
|
|
390
|
+
this.validatorAddresses = [...address];
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Checks if a peer is trusted.
|
|
395
|
+
* @param peerId - The peer ID.
|
|
396
|
+
* @returns True if the peer is trusted, false otherwise.
|
|
397
|
+
* Note: This function will return false and log a warning if the trusted peers are not initialized.
|
|
398
|
+
*/
|
|
399
|
+
private isTrustedPeer(peerId: PeerId): boolean {
|
|
400
|
+
if (!this.trustedPeersInitialized) {
|
|
401
|
+
this.logger.warn('Trusted peers not initialized, returning false');
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
return this.trustedPeers.has(peerId.toString());
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Adds a peer to the trusted peers set.
|
|
409
|
+
* @param peerId - The peer ID to add to trusted peers.
|
|
410
|
+
*/
|
|
411
|
+
public addTrustedPeer(peerId: PeerId): void {
|
|
412
|
+
const peerIdStr = peerId.toString();
|
|
413
|
+
|
|
414
|
+
this.trustedPeers.add(peerIdStr);
|
|
415
|
+
this.trustedPeersInitialized = true;
|
|
416
|
+
this.logger.verbose(`Added trusted peer ${peerIdStr}`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Adds a peer to the private peers set.
|
|
421
|
+
* @param peerId - The peer ID to add to private peers.
|
|
422
|
+
*/
|
|
423
|
+
public addPrivatePeer(peerId: PeerId): void {
|
|
424
|
+
const peerIdStr = peerId.toString();
|
|
425
|
+
|
|
426
|
+
this.trustedPeers.add(peerIdStr);
|
|
427
|
+
this.privatePeers.add(peerIdStr);
|
|
428
|
+
this.trustedPeersInitialized = true;
|
|
429
|
+
this.privatePeersInitialized = true;
|
|
430
|
+
this.logger.verbose(`Added private peer ${peerIdStr}`);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Checks if a peer is private.
|
|
435
|
+
* @param peerId - The peer ID.
|
|
436
|
+
* @returns True if the peer is private, false otherwise.
|
|
437
|
+
*/
|
|
438
|
+
private isPrivatePeer(peerId: PeerId): boolean {
|
|
439
|
+
if (!this.privatePeersInitialized) {
|
|
440
|
+
this.logger.warn('Private peers not initialized, returning false');
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
return this.privatePeers.has(peerId.toString());
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Adds a peer to the preferred peers set.
|
|
448
|
+
* @param peerId - The peer ID to add to preferred peers.
|
|
449
|
+
*/
|
|
450
|
+
public addPreferredPeer(peerId: PeerId): void {
|
|
451
|
+
const peerIdStr = peerId.toString();
|
|
452
|
+
|
|
453
|
+
this.preferredPeers.add(peerIdStr);
|
|
454
|
+
this.logger.verbose(`Added preferred peer ${peerIdStr}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Checks if a peer is preferred.
|
|
459
|
+
* @param peerId - The peer ID.
|
|
460
|
+
* @returns True if the peer is preferred, false otherwise.
|
|
461
|
+
*/
|
|
462
|
+
private isPreferredPeer(peerId: PeerId): boolean {
|
|
463
|
+
return this.preferredPeers.has(peerId.toString());
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Checks if a peer is protected (either trusted or private).
|
|
468
|
+
* @param peerId - The peer ID.
|
|
469
|
+
* @returns True if the peer is protected, false otherwise.
|
|
470
|
+
*/
|
|
471
|
+
private isProtectedPeer(peerId: PeerId): boolean {
|
|
472
|
+
return this.isTrustedPeer(peerId) || this.isPrivatePeer(peerId) || this.isPreferredPeer(peerId);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Handles a goodbye received from a peer.
|
|
477
|
+
*
|
|
478
|
+
* Used as the reqresp handler when a peer sends us goodbye message.
|
|
479
|
+
* @param peerId - The peer ID.
|
|
480
|
+
* @param reason - The reason for the goodbye.
|
|
481
|
+
*/
|
|
482
|
+
public goodbyeReceived(peerId: PeerId, reason: GoodByeReason) {
|
|
483
|
+
this.logger.debug(`Goodbye received from peer ${peerId.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
484
|
+
|
|
485
|
+
this.metrics.recordGoodbyeReceived(reason);
|
|
486
|
+
|
|
487
|
+
this.markPeerForDisconnect(peerId);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
public penalizePeer(peerId: PeerId, penalty: PeerErrorSeverity) {
|
|
491
|
+
this.peerScoring.penalizePeer(peerId, penalty);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
public getPeerScore(peerId: string): number {
|
|
495
|
+
return this.peerScoring.getScore(peerId);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
public shouldDisableP2PGossip(peerId: string): boolean {
|
|
499
|
+
const isAuthenticated = this.isAuthenticatedPeer(peerIdFromString(peerId));
|
|
500
|
+
return (this.config.p2pAllowOnlyValidators ?? false) && !isAuthenticated;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
public getPeers(includePending = false): PeerInfo[] {
|
|
504
|
+
const connected = this.libP2PNode
|
|
505
|
+
.getPeers()
|
|
506
|
+
.map(peer => ({ id: peer.toString(), score: this.getPeerScore(peer.toString()), status: 'connected' as const }));
|
|
507
|
+
|
|
508
|
+
if (!includePending) {
|
|
509
|
+
return connected;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const dialQueue = this.libP2PNode
|
|
513
|
+
.getDialQueue()
|
|
514
|
+
.filter(peer => !!peer.peerId)
|
|
515
|
+
.map(peer => ({
|
|
516
|
+
id: peer.peerId!.toString(),
|
|
517
|
+
status: 'dialing' as const,
|
|
518
|
+
dialStatus: peer.status,
|
|
519
|
+
addresses: peer.multiaddrs.map(m => m.toString()),
|
|
520
|
+
}));
|
|
521
|
+
|
|
522
|
+
const cachedPeers = Array.from(this.cachedPeers.values())
|
|
523
|
+
.filter(peer => !dialQueue.some(dialPeer => dialPeer.id && peer.peerId.toString() === dialPeer.id.toString()))
|
|
524
|
+
.filter(peer => !connected.some(connPeer => connPeer.id.toString() === peer.peerId.toString()))
|
|
525
|
+
.map(peer => ({
|
|
526
|
+
status: 'cached' as const,
|
|
527
|
+
id: peer.peerId.toString(),
|
|
528
|
+
addresses: [peer.multiaddrTcp.toString()],
|
|
529
|
+
dialAttempts: peer.dialAttempts,
|
|
530
|
+
enr: peer.enr.encodeTxt(),
|
|
531
|
+
}));
|
|
532
|
+
|
|
533
|
+
return [...connected, ...dialQueue, ...cachedPeers];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
public isAuthenticatedPeer(peerId: PeerId): boolean {
|
|
537
|
+
const peerIdAsString = peerId.toString();
|
|
538
|
+
return (
|
|
539
|
+
this.privatePeers.has(peerIdAsString) ||
|
|
540
|
+
this.trustedPeers.has(peerIdAsString) ||
|
|
541
|
+
this.preferredPeers.has(peerIdAsString) ||
|
|
542
|
+
this.authenticatedPeerIdToValidatorAddress.has(peerIdAsString)
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/*
|
|
547
|
+
* Checks whether peer is allowed to connect
|
|
548
|
+
*
|
|
549
|
+
* @param id: Address of the node or it's peerId
|
|
550
|
+
*
|
|
551
|
+
* @returns: True if node is allowed to connect, otherwise false
|
|
552
|
+
* */
|
|
553
|
+
/**
|
|
554
|
+
* Whether a peer is allowed to connect, given its peer id. Rejects peers serving an active ban and
|
|
555
|
+
* peers that have exceeded the failed auth-handshake limit. Use this once the peer id is known —
|
|
556
|
+
* i.e. for the encrypted-inbound gater and when dialing.
|
|
557
|
+
*/
|
|
558
|
+
public isPeerAllowedToConnect(peerId: string | PeerId): boolean {
|
|
559
|
+
const id = peerId.toString();
|
|
560
|
+
if (this.peerScoring.getScoreState(id) === PeerScoreState.Banned) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
return this.isWithinFailedAuthLimit(id);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Whether a connection from an address is allowed. Bans are keyed by peer id, which isn't known at
|
|
568
|
+
* the raw-inbound layer, so only the failed auth-handshake limit (also tracked per address) is
|
|
569
|
+
* enforced here; the ban is applied once the peer id is known via {@link isPeerAllowedToConnect}.
|
|
570
|
+
*/
|
|
571
|
+
public isAddressAllowedToConnect(address: string): boolean {
|
|
572
|
+
return this.isWithinFailedAuthLimit(address);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** Whether the failed auth-handshake count for a peer id or address is below the configured limit. */
|
|
576
|
+
private isWithinFailedAuthLimit(key: string): boolean {
|
|
577
|
+
const entry = this.failedAuthHandshakes.get(key);
|
|
578
|
+
if (!entry) {
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// In case entry is too old, remove it and allow connection
|
|
583
|
+
if (this.dateProvider.now() - entry.lastFailureTimestamp > FAILED_AUTH_HANDSHAKE_EXPIRY_MS) {
|
|
584
|
+
this.failedAuthHandshakes.delete(key);
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return entry.count <= this.config.p2pMaxFailedAuthAttemptsAllowed;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Discovers peers.
|
|
593
|
+
*/
|
|
594
|
+
private discover() {
|
|
595
|
+
const connections = this.libP2PNode.getConnections();
|
|
596
|
+
|
|
597
|
+
const healthyConnections = this.prioritizePeers(this.pruneUnhealthyPeers(this.getNonProtectedPeers(connections)));
|
|
598
|
+
|
|
599
|
+
// Calculate how many connections we're looking to make
|
|
600
|
+
const protectedPeerCount = this.getProtectedPeerCount();
|
|
601
|
+
const peersToConnect = this.config.maxPeerCount - healthyConnections.length - protectedPeerCount;
|
|
602
|
+
|
|
603
|
+
const logLevel = this.heartbeatCounter % this.displayPeerCountsPeerHeartbeat === 0 ? 'info' : 'debug';
|
|
604
|
+
this.logger[logLevel](`Connected to ${healthyConnections.length + this.trustedPeers.size} peers`, {
|
|
605
|
+
discoveredConnections: healthyConnections.length,
|
|
606
|
+
protectedConnections: protectedPeerCount,
|
|
607
|
+
maxPeerCount: this.config.maxPeerCount,
|
|
608
|
+
cachedPeers: this.cachedPeers.size,
|
|
609
|
+
discv5KadPeers: this.peerDiscoveryService.getKadValues().length,
|
|
610
|
+
...this.peerScoring.getStats(),
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
this.metrics.recordPeerCount(connections.length);
|
|
614
|
+
this.metrics.recordHealthyPeerCount(healthyConnections.length);
|
|
615
|
+
|
|
616
|
+
// Exit if no peers to connect
|
|
617
|
+
if (peersToConnect <= 0) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const cachedPeersToDial: CachedPeer[] = [];
|
|
622
|
+
|
|
623
|
+
const pendingDials = new Set(
|
|
624
|
+
this.libP2PNode
|
|
625
|
+
.getDialQueue()
|
|
626
|
+
.map(pendingDial => pendingDial.peerId?.toString())
|
|
627
|
+
.filter(Boolean) as string[],
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
const now = this.dateProvider.now();
|
|
631
|
+
for (const [id, peerData] of this.cachedPeers.entries()) {
|
|
632
|
+
// if already dialling or connected to, remove from cache
|
|
633
|
+
if (
|
|
634
|
+
pendingDials.has(id) ||
|
|
635
|
+
healthyConnections.some(conn => conn.remotePeer.equals(peerData.peerId)) ||
|
|
636
|
+
// if peer has been in cache for the max cache age, remove from cache
|
|
637
|
+
now - peerData.addedUnixMs > MAX_CACHED_PEER_AGE_MS
|
|
638
|
+
) {
|
|
639
|
+
this.cachedPeers.delete(id);
|
|
640
|
+
} else {
|
|
641
|
+
// cachedPeersToDial.set(id, enr);
|
|
642
|
+
cachedPeersToDial.push(peerData);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// reverse to dial older entries first
|
|
647
|
+
cachedPeersToDial.reverse();
|
|
648
|
+
|
|
649
|
+
for (const peer of cachedPeersToDial) {
|
|
650
|
+
// We remove from the cache before, as dialling will add it back if it fails
|
|
651
|
+
this.cachedPeers.delete(peer.peerId.toString());
|
|
652
|
+
void this.dialPeer(peer);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// if we need more peers, start randomNodesQuery
|
|
656
|
+
if (peersToConnect > 0) {
|
|
657
|
+
this.logger.trace(`Running random nodes query to connect to ${peersToConnect} peers`);
|
|
658
|
+
void this.peerDiscoveryService.runRandomNodesQuery();
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private getNonProtectedPeers(connections: Connection[]): Connection[] {
|
|
663
|
+
return connections.filter(conn => !this.isProtectedPeer(conn.remotePeer));
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private getProtectedPeerCount(): number {
|
|
667
|
+
return this.trustedPeers.size + this.privatePeers.size + this.preferredPeers.size;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private pruneUnhealthyPeers(connections: Connection[]): Connection[] {
|
|
671
|
+
const connectedHealthyPeers: Connection[] = [];
|
|
672
|
+
|
|
673
|
+
for (const peer of connections) {
|
|
674
|
+
const score = this.peerScoring.getScoreState(peer.remotePeer.toString());
|
|
675
|
+
switch (score) {
|
|
676
|
+
case PeerScoreState.Banned:
|
|
677
|
+
this.metrics.recordLowScoreDisconnect('Banned');
|
|
678
|
+
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.BANNED);
|
|
679
|
+
break;
|
|
680
|
+
case PeerScoreState.Disconnect:
|
|
681
|
+
this.metrics.recordLowScoreDisconnect('Disconnect');
|
|
682
|
+
void this.goodbyeAndDisconnectPeer(peer.remotePeer, GoodByeReason.LOW_SCORE);
|
|
683
|
+
break;
|
|
684
|
+
case PeerScoreState.Healthy:
|
|
685
|
+
connectedHealthyPeers.push(peer);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return connectedHealthyPeers;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* If the max peer count is reached, the lowest scoring peers will be pruned to satisfy the max peer count.
|
|
694
|
+
*
|
|
695
|
+
* @param connections - The list of connections to prune low scoring peers above the max peer count from.
|
|
696
|
+
* @returns The pruned list of connections.
|
|
697
|
+
*/
|
|
698
|
+
private prioritizePeers(connections: Connection[]): Connection[] {
|
|
699
|
+
const protectedPeerCount = this.getProtectedPeerCount();
|
|
700
|
+
if (connections.length > this.config.maxPeerCount - protectedPeerCount) {
|
|
701
|
+
// Sort the regular peer scores from highest to lowest
|
|
702
|
+
const prioritizedConnections = connections.sort((connectionA, connectionB) => {
|
|
703
|
+
const connectionScoreA = this.peerScoring.getScore(connectionA.remotePeer.toString());
|
|
704
|
+
const connectionScoreB = this.peerScoring.getScore(connectionB.remotePeer.toString());
|
|
705
|
+
return connectionScoreB - connectionScoreA;
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
// Calculate how many regular peers we can keep
|
|
709
|
+
const peersToKeep = Math.max(0, this.config.maxPeerCount - protectedPeerCount);
|
|
710
|
+
|
|
711
|
+
// Disconnect from the lowest scoring regular connections that exceed our limit
|
|
712
|
+
for (const conn of prioritizedConnections.slice(peersToKeep)) {
|
|
713
|
+
void this.goodbyeAndDisconnectPeer(conn.remotePeer, GoodByeReason.MAX_PEERS);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Return trusted connections plus the highest scoring regular connections up to the max peer count
|
|
717
|
+
return prioritizedConnections.slice(0, peersToKeep);
|
|
718
|
+
} else {
|
|
719
|
+
return connections;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
private async goodbyeAndDisconnectPeer(peer: PeerId, reason: GoodByeReason) {
|
|
724
|
+
this.logger.debug(`Disconnecting peer ${peer.toString()} with reason ${prettyGoodbyeReason(reason)}`);
|
|
725
|
+
|
|
726
|
+
this.metrics.recordGoodbyeSent(reason);
|
|
727
|
+
|
|
728
|
+
try {
|
|
729
|
+
const resp = await this.reqresp.sendRequestToPeer(
|
|
730
|
+
peer,
|
|
731
|
+
ReqRespSubProtocol.GOODBYE,
|
|
732
|
+
Buffer.from([reason]),
|
|
733
|
+
GOODBYE_DIAL_TIMEOUT_MS,
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
if (resp.status === ReqRespStatus.FAILURE) {
|
|
737
|
+
this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}`);
|
|
738
|
+
} else if (resp.status === ReqRespStatus.SUCCESS) {
|
|
739
|
+
this.logger.verbose(`Sent goodbye to peer ${peer.toString()}`);
|
|
740
|
+
} else {
|
|
741
|
+
this.logger.debug(
|
|
742
|
+
`Unexpected status sending goodbye to peer ${peer.toString()}: ${ReqRespStatus[resp.status]}`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
} catch (error) {
|
|
746
|
+
this.logger.debug(`Failed to send goodbye to peer ${peer.toString()}: ${error}`);
|
|
747
|
+
} finally {
|
|
748
|
+
this.markPeerForDisconnect(peer);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/*
|
|
753
|
+
* Marks peer to be disconnected on the next heartbeat
|
|
754
|
+
* */
|
|
755
|
+
private markPeerForDisconnect(peer: PeerId) {
|
|
756
|
+
const peerIdStr = peer.toString();
|
|
757
|
+
this.logger.debug(`Scheduling peer ${peerIdStr} for disconnection`);
|
|
758
|
+
this.peersToBeDisconnected.add(peerIdStr);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Performs the actual disconnection of a peer.
|
|
763
|
+
* This is called during heartbeat processing to avoid immediate disconnections.
|
|
764
|
+
*
|
|
765
|
+
* @returns True if peer was disconnect, otherwise false
|
|
766
|
+
*/
|
|
767
|
+
private async disconnectPeer(peer: PeerId): Promise<boolean> {
|
|
768
|
+
const peerIdStr = peer.toString();
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
await this.libP2PNode.hangUp(peer);
|
|
772
|
+
|
|
773
|
+
this.logger.debug(`Successfully disconnected peer ${peerIdStr}`);
|
|
774
|
+
return true;
|
|
775
|
+
} catch (error) {
|
|
776
|
+
this.logger.warn(`Failed to disconnect peer ${peerIdStr}`, { error });
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Handles a discovered peer.
|
|
783
|
+
* @param enr - The discovered peer's ENR.
|
|
784
|
+
*/
|
|
785
|
+
private async handleDiscoveredPeer(enr: ENR) {
|
|
786
|
+
// Check that the peer has not already been banned
|
|
787
|
+
const peerId = await enr.peerId();
|
|
788
|
+
const peerIdString = peerId.toString();
|
|
789
|
+
|
|
790
|
+
// Don't attempt to connect to peers scheduled for disconnection
|
|
791
|
+
if (this.peersToBeDisconnected.has(peerIdString)) {
|
|
792
|
+
this.logger.trace(`Skipping peer scheduled for disconnection ${peerId}`);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Check if peer is temporarily timed out
|
|
797
|
+
const timedOutPeer = this.timedOutPeers.get(peerIdString);
|
|
798
|
+
if (timedOutPeer) {
|
|
799
|
+
if (this.dateProvider.now() < timedOutPeer.timeoutUntilMs) {
|
|
800
|
+
this.logger.trace(`Skipping timed out peer ${peerId}`);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
// Timeout period expired, remove from timed out peers
|
|
804
|
+
this.timedOutPeers.delete(peerIdString);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (this.peerScoring.getScoreState(peerIdString) != PeerScoreState.Healthy) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Don't dial banned peers or those that have exceeded the auth failure threshold
|
|
812
|
+
if (!this.isPeerAllowedToConnect(peerId)) {
|
|
813
|
+
this.logger.trace(`Skipping peer ${peerId} due to ban or failed auth handshake attempts`);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const [multiaddrTcp] = await Promise.all([enr.getFullMultiaddr('tcp')]);
|
|
818
|
+
|
|
819
|
+
this.logger.trace(`Handling discovered peer ${peerId} at ${multiaddrTcp?.toString() ?? 'undefined address'}`);
|
|
820
|
+
|
|
821
|
+
// stop if no tcp addr in multiaddr
|
|
822
|
+
if (!multiaddrTcp) {
|
|
823
|
+
this.logger.debug(`No TCP address in discovered node's multiaddr ${enr.encodeTxt()}`);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
// check if peer is already connected
|
|
827
|
+
const connections = this.libP2PNode.getConnections();
|
|
828
|
+
if (connections.some((conn: Connection) => conn.remotePeer.equals(peerId))) {
|
|
829
|
+
this.logger.trace(`Already connected to peer ${peerId}`);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// check if peer is already in cache
|
|
834
|
+
if (this.cachedPeers.has(peerIdString)) {
|
|
835
|
+
this.logger.trace(`Peer already in cache ${peerIdString}`);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// create cached peer object
|
|
840
|
+
const cachedPeer: CachedPeer = {
|
|
841
|
+
peerId,
|
|
842
|
+
enr,
|
|
843
|
+
multiaddrTcp,
|
|
844
|
+
dialAttempts: 0,
|
|
845
|
+
addedUnixMs: this.dateProvider.now(),
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// Determine if we should dial immediately or not
|
|
849
|
+
if (this.shouldDialPeer()) {
|
|
850
|
+
void this.dialPeer(cachedPeer);
|
|
851
|
+
} else {
|
|
852
|
+
this.logger.trace(`Caching peer ${peerIdString}`);
|
|
853
|
+
this.cachedPeers.set(peerIdString, cachedPeer);
|
|
854
|
+
// Prune set of cached peers
|
|
855
|
+
this.pruneCachedPeers();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
private async dialPeer(peer: CachedPeer) {
|
|
860
|
+
const id = peer.peerId.toString();
|
|
861
|
+
|
|
862
|
+
// Add to the address book before dialing
|
|
863
|
+
await this.libP2PNode.peerStore.merge(peer.peerId, { multiaddrs: [peer.multiaddrTcp] });
|
|
864
|
+
|
|
865
|
+
this.logger.trace(`Dialing peer ${id}`);
|
|
866
|
+
try {
|
|
867
|
+
await this.libP2PNode.dial(peer.multiaddrTcp);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
peer.dialAttempts++;
|
|
870
|
+
if (peer.dialAttempts < MAX_DIAL_ATTEMPTS) {
|
|
871
|
+
this.logger.trace(`Failed to dial peer ${id} (attempt ${peer.dialAttempts})`, { error: inspect(error) });
|
|
872
|
+
this.cachedPeers.set(id, peer);
|
|
873
|
+
} else {
|
|
874
|
+
formatLibp2pDialError(error as Error);
|
|
875
|
+
this.logger.debug(`Failed to dial peer ${id} (dropping)`, { error: inspect(error) });
|
|
876
|
+
this.cachedPeers.delete(id);
|
|
877
|
+
// Add to timed out peers
|
|
878
|
+
this.timedOutPeers.set(id, {
|
|
879
|
+
peerId: id,
|
|
880
|
+
timeoutUntilMs:
|
|
881
|
+
this.dateProvider.now() + (this.config.peerFailedBanTimeMs ?? DEFAULT_FAILED_PEER_BAN_TIME_MS),
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
private shouldDialPeer(): boolean {
|
|
888
|
+
const connections = this.libP2PNode.getConnections().length;
|
|
889
|
+
if (connections >= this.config.maxPeerCount) {
|
|
890
|
+
this.logger.trace(
|
|
891
|
+
`Not dialing peer due to max peer count of ${this.config.maxPeerCount} reached (${connections} current connections)`,
|
|
892
|
+
);
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
private pruneCachedPeers() {
|
|
899
|
+
let peersToDelete = this.cachedPeers.size - MAX_CACHED_PEERS;
|
|
900
|
+
if (peersToDelete <= 0) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Remove the oldest peers
|
|
905
|
+
for (const [key, value] of this.cachedPeers.entries()) {
|
|
906
|
+
if (this.isProtectedPeer(value.peerId)) {
|
|
907
|
+
this.logger.debug(`Not pruning trusted peer ${key}`);
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
this.cachedPeers.delete(key);
|
|
912
|
+
this.logger.trace(`Pruning peer ${key} from cache`);
|
|
913
|
+
peersToDelete--;
|
|
914
|
+
if (peersToDelete <= 0) {
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
private async createStatusMessage() {
|
|
921
|
+
const syncSummary = (await this.worldStateSynchronizer.status()).syncSummary;
|
|
922
|
+
return StatusMessage.fromWorldStateSyncStatus(this.protocolVersion, syncSummary);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Performs status Handshake with the Peer
|
|
927
|
+
* The way the protocol is designed is that each peer will call this method on newly established p2p connection.
|
|
928
|
+
* Both peers request Status message and both peers perform validation of the received Status message.
|
|
929
|
+
* If this validation fails on any end that peer will initiate disconnect.
|
|
930
|
+
* Note: It's important for both peers to request and perform Status validation,
|
|
931
|
+
* Because one of the peers can be _bad peer_ and this peer can simply skip the check.
|
|
932
|
+
* If we don't implement validation on both ends the _bad peer_ remains connected.
|
|
933
|
+
* @param: peerId The Id of the peer to request the Status from.
|
|
934
|
+
* */
|
|
935
|
+
private async exchangeStatusHandshake(peerId: PeerId) {
|
|
936
|
+
try {
|
|
937
|
+
const ourStatus = await this.createStatusMessage();
|
|
938
|
+
//Note: Technically we don't have to send out status to peer as well, but we do.
|
|
939
|
+
//It will be easier to update protocol in the future this way if need be.
|
|
940
|
+
this.logger.trace(`Initiating status handshake with peer ${peerId}`);
|
|
941
|
+
const response = await this.reqresp.sendRequestToPeer(peerId, ReqRespSubProtocol.STATUS, ourStatus.toBuffer());
|
|
942
|
+
const { status } = response;
|
|
943
|
+
if (status !== ReqRespStatus.SUCCESS) {
|
|
944
|
+
//TODO: maybe hard ban these peers in the future.
|
|
945
|
+
//We could allow this to happen up to N times, and then hard ban?
|
|
946
|
+
//Hard ban: Disallow connection via e.g. libp2p's Gater
|
|
947
|
+
this.logger.debug(`Disconnecting peer ${peerId} who failed to respond status handshake`, {
|
|
948
|
+
peerId,
|
|
949
|
+
status: ReqRespStatus[status],
|
|
950
|
+
});
|
|
951
|
+
this.markPeerForDisconnect(peerId);
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const { data } = response;
|
|
956
|
+
const logData = { peerId, status: ReqRespStatus[status], data: data ? bufferToHex(data) : undefined };
|
|
957
|
+
const peerStatusMessage = StatusMessage.fromBuffer(data);
|
|
958
|
+
if (!ourStatus.validate(peerStatusMessage)) {
|
|
959
|
+
this.logger.debug(`Disconnecting peer ${peerId} due to failed status handshake.`, logData);
|
|
960
|
+
this.markPeerForDisconnect(peerId);
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
this.logger.debug(`Successfully completed status handshake with peer ${peerId}`, logData);
|
|
964
|
+
} catch (err: any) {
|
|
965
|
+
//TODO: maybe hard ban these peers in the future
|
|
966
|
+
this.logger.debug(`Disconnecting peer ${peerId} due to error during status handshake: ${err.message ?? err}`, {
|
|
967
|
+
peerId,
|
|
968
|
+
});
|
|
969
|
+
this.markPeerForDisconnect(peerId);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Performs auth Handshake with the Peer
|
|
975
|
+
* A superset of the status handshake. Also includes a challenge that needs to be signed by the peer's validator key.
|
|
976
|
+
* @param: peerId The Id of the peer to request the Status from.
|
|
977
|
+
* */
|
|
978
|
+
private async exchangeAuthHandshake(peerId: PeerId) {
|
|
979
|
+
const peerIdString = peerId.toString();
|
|
980
|
+
|
|
981
|
+
try {
|
|
982
|
+
const ourStatus = await this.createStatusMessage();
|
|
983
|
+
const authRequest = new AuthRequest(ourStatus, Fr.random());
|
|
984
|
+
|
|
985
|
+
// Note: Technically we don't have to send our status to peer as well, but we do.
|
|
986
|
+
// It will be easier to update protocol in the future this way if need be.
|
|
987
|
+
// We also need to send the challenge at least, so that the peer can sign it.
|
|
988
|
+
this.logger.debug(`Initiating auth handshake with peer ${peerId}`);
|
|
989
|
+
const response = await this.reqresp.sendRequestToPeer(peerId, ReqRespSubProtocol.AUTH, authRequest.toBuffer());
|
|
990
|
+
const { status } = response;
|
|
991
|
+
if (status !== ReqRespStatus.SUCCESS) {
|
|
992
|
+
this.logger.verbose(`Disconnecting peer ${peerId} who failed to respond auth handshake`, {
|
|
993
|
+
peerId,
|
|
994
|
+
status: ReqRespStatus[status],
|
|
995
|
+
});
|
|
996
|
+
this.markAuthHandshakeFailed(peerId);
|
|
997
|
+
this.markPeerForDisconnect(peerId);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
const { data } = response;
|
|
1002
|
+
const logData = { peerId, status: ReqRespStatus[status], data: data ? bufferToHex(data) : undefined };
|
|
1003
|
+
|
|
1004
|
+
const peerAuthResponse = AuthResponse.fromBuffer(data);
|
|
1005
|
+
|
|
1006
|
+
const peerStatusMessage = peerAuthResponse.status;
|
|
1007
|
+
if (!ourStatus.validate(peerStatusMessage)) {
|
|
1008
|
+
this.logger.verbose(`Disconnecting peer ${peerId} due to failed status handshake as part of auth.`, logData);
|
|
1009
|
+
this.markAuthHandshakeFailed(peerId);
|
|
1010
|
+
this.markPeerForDisconnect(peerId);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const hashToRecover = authRequest.getPayloadToSign();
|
|
1015
|
+
const ethSignedHash = makeEthSignDigest(hashToRecover);
|
|
1016
|
+
const sender = tryRecoverAddress(ethSignedHash, peerAuthResponse.signature);
|
|
1017
|
+
if (!sender) {
|
|
1018
|
+
this.logger.verbose(`Disconnecting peer ${peerId} due to failed auth handshake, invalid signature.`, logData);
|
|
1019
|
+
this.markAuthHandshakeFailed(peerId);
|
|
1020
|
+
this.markPeerForDisconnect(peerId);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const registeredValidators = await this.epochCache.getRegisteredValidators();
|
|
1025
|
+
const found = registeredValidators.find(v => v.toString() === sender.toString()) !== undefined;
|
|
1026
|
+
if (!found) {
|
|
1027
|
+
this.logger.verbose(
|
|
1028
|
+
`Disconnecting peer ${peerId} due to failed auth handshake, peer is not a registered validator.`,
|
|
1029
|
+
{ ...logData, address: sender.toString() },
|
|
1030
|
+
);
|
|
1031
|
+
this.markAuthHandshakeFailed(peerId);
|
|
1032
|
+
this.markPeerForDisconnect(peerId);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// Check to see that this validator address isn't already allocated to a different peer
|
|
1037
|
+
const peerForAddress = this.authenticatedValidatorAddressToPeerId.get(sender.toString());
|
|
1038
|
+
if (peerForAddress !== undefined && peerForAddress.toString() !== peerIdString) {
|
|
1039
|
+
this.logger.verbose(
|
|
1040
|
+
`Received auth for validator ${sender.toString()} from peer ${peerIdString}, but this validator is already authenticated to peer ${peerForAddress.toString()}`,
|
|
1041
|
+
{ ...logData, address: sender.toString() },
|
|
1042
|
+
);
|
|
1043
|
+
this.markAuthHandshakeFailed(peerId);
|
|
1044
|
+
this.markPeerForDisconnect(peerId);
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
this.markAuthHandshakeSuccess(peerId);
|
|
1049
|
+
this.authenticatedPeerIdToValidatorAddress.set(peerIdString, sender);
|
|
1050
|
+
this.authenticatedValidatorAddressToPeerId.set(sender.toString(), peerId);
|
|
1051
|
+
this.logger.info(
|
|
1052
|
+
`Successfully completed auth handshake with peer ${peerId}, validator address ${sender.toString()}`,
|
|
1053
|
+
{ ...logData, address: sender.toString() },
|
|
1054
|
+
);
|
|
1055
|
+
} catch (err: any) {
|
|
1056
|
+
//TODO: maybe hard ban these peers in the future
|
|
1057
|
+
this.logger.verbose(`Disconnecting peer ${peerId} due to error during auth handshake: ${err.message}`, {
|
|
1058
|
+
peerId,
|
|
1059
|
+
err,
|
|
1060
|
+
});
|
|
1061
|
+
this.markAuthHandshakeFailed(peerId);
|
|
1062
|
+
this.markPeerForDisconnect(peerId);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/*
|
|
1067
|
+
* Marks when peer fails auth handshake
|
|
1068
|
+
* */
|
|
1069
|
+
private markAuthHandshakeFailed(peerId: PeerId) {
|
|
1070
|
+
const now = this.dateProvider.now();
|
|
1071
|
+
const peerIdStr = peerId.toString();
|
|
1072
|
+
|
|
1073
|
+
const existingEntry = this.failedAuthHandshakes.get(peerIdStr);
|
|
1074
|
+
const failureCount = (existingEntry?.count || 0) + 1;
|
|
1075
|
+
this.failedAuthHandshakes.set(peerIdStr, {
|
|
1076
|
+
count: failureCount,
|
|
1077
|
+
lastFailureTimestamp: now,
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
const connections = this.libP2PNode.getConnections(peerId);
|
|
1081
|
+
connections.forEach(conn => {
|
|
1082
|
+
const address = conn.remoteAddr.nodeAddress().address;
|
|
1083
|
+
const existingAddressEntry = this.failedAuthHandshakes.get(address);
|
|
1084
|
+
this.failedAuthHandshakes.set(address, {
|
|
1085
|
+
count: (existingAddressEntry?.count || 0) + 1,
|
|
1086
|
+
lastFailureTimestamp: now,
|
|
1087
|
+
});
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
// Ban the peer from being re-dialed for a cooldown period (exponential backoff)
|
|
1091
|
+
const banTimeMs = this.config.peerFailedBanTimeMs ?? DEFAULT_FAILED_PEER_BAN_TIME_MS;
|
|
1092
|
+
const backoffMs = banTimeMs * Math.pow(2, Math.min(failureCount - 1, 5));
|
|
1093
|
+
this.timedOutPeers.set(peerIdStr, {
|
|
1094
|
+
peerId: peerIdStr,
|
|
1095
|
+
timeoutUntilMs: now + backoffMs,
|
|
1096
|
+
});
|
|
1097
|
+
this.cachedPeers.delete(peerIdStr);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/*
|
|
1101
|
+
* Marks when peer exchanges auth handshake
|
|
1102
|
+
* Removes any failed previous attempts
|
|
1103
|
+
* */
|
|
1104
|
+
private markAuthHandshakeSuccess(peerId: PeerId) {
|
|
1105
|
+
this.failedAuthHandshakes.delete(peerId.toString());
|
|
1106
|
+
|
|
1107
|
+
const connections = this.libP2PNode.getConnections(peerId);
|
|
1108
|
+
connections.forEach(conn => {
|
|
1109
|
+
const address = conn.remoteAddr.nodeAddress().address;
|
|
1110
|
+
this.failedAuthHandshakes.delete(address);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* Stops the peer manager.
|
|
1116
|
+
* Removing all event listeners.
|
|
1117
|
+
*/
|
|
1118
|
+
public async stop() {
|
|
1119
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
1120
|
+
this.peerDiscoveryService.off(PeerEvent.DISCOVERED, this.handlers.handleDiscoveredPeer);
|
|
1121
|
+
|
|
1122
|
+
// Send goodbyes to all peers
|
|
1123
|
+
await Promise.all(
|
|
1124
|
+
this.libP2PNode.getPeers().map(peer => this.goodbyeAndDisconnectPeer(peer, GoodByeReason.SHUTDOWN)),
|
|
1125
|
+
);
|
|
1126
|
+
|
|
1127
|
+
this.libP2PNode.removeEventListener(PeerEvent.CONNECTED, this.handlers.handleConnectedPeerEvent);
|
|
1128
|
+
this.libP2PNode.removeEventListener(PeerEvent.DISCONNECTED, this.handlers.handleDisconnectedPeerEvent);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
private shouldTrustWithIdentity(peerId: PeerId): boolean {
|
|
1132
|
+
return this.isProtectedPeer(peerId);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Performs auth request verification from peer. An auth request is valid if requested by an authorized peer (a peer we trust).
|
|
1137
|
+
*
|
|
1138
|
+
* @param: _authRequest - Auth request (unused)
|
|
1139
|
+
* @param: peerId - The ID of the peer that requested the auth handshake
|
|
1140
|
+
*
|
|
1141
|
+
* @returns: StatusMessage if peer is trusted
|
|
1142
|
+
*
|
|
1143
|
+
* @throws: If peer is unauthorized
|
|
1144
|
+
* */
|
|
1145
|
+
public async handleAuthRequestFromPeer(_authRequest: AuthRequest, peerId: PeerId): Promise<StatusMessage> {
|
|
1146
|
+
if (!this.shouldTrustWithIdentity(peerId)) {
|
|
1147
|
+
this.logger.warn(`Received auth request from untrusted peer ${peerId.toString()}`);
|
|
1148
|
+
throw new Error('Unauthorised');
|
|
1149
|
+
}
|
|
1150
|
+
this.logger.debug(`Received auth request from trusted peer ${peerId.toString()}`);
|
|
1151
|
+
return await this.createStatusMessage();
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
private async updateAuthenticatedPeers(): Promise<void> {
|
|
1155
|
+
const registeredValidators = await this.epochCache.getRegisteredValidators();
|
|
1156
|
+
const validatorSet = new Set(registeredValidators.map(v => v.toString()));
|
|
1157
|
+
|
|
1158
|
+
const peersToDelete: Set<string> = new Set();
|
|
1159
|
+
const addressesToDelete: Set<string> = new Set();
|
|
1160
|
+
for (const [peer, address] of this.authenticatedPeerIdToValidatorAddress.entries()) {
|
|
1161
|
+
const addressString = address.toString();
|
|
1162
|
+
if (!validatorSet.has(addressString)) {
|
|
1163
|
+
peersToDelete.add(peer);
|
|
1164
|
+
addressesToDelete.add(addressString);
|
|
1165
|
+
this.logger.info(
|
|
1166
|
+
`Removing authentication for peer ${peer.toString()} at address ${addressString} due to no longer being a registered validator`,
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
for (const peer of peersToDelete) {
|
|
1172
|
+
this.authenticatedPeerIdToValidatorAddress.delete(peer);
|
|
1173
|
+
}
|
|
1174
|
+
for (const address of addressesToDelete) {
|
|
1175
|
+
this.authenticatedValidatorAddressToPeerId.delete(address);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* copied from github.com/ChainSafe/lodestar
|
|
1182
|
+
* libp2p errors with extremely noisy errors here, which are deeply nested taking 30-50 lines.
|
|
1183
|
+
* Some known errors:
|
|
1184
|
+
* ```
|
|
1185
|
+
* Error: The operation was aborted
|
|
1186
|
+
* Error: stream ended before 1 bytes became available
|
|
1187
|
+
* Error: Error occurred during XX handshake: Error occurred while verifying signed payload: Peer ID doesn't match libp2p public key
|
|
1188
|
+
* ```
|
|
1189
|
+
*
|
|
1190
|
+
* Also the error's message is not properly formatted, where the error message is indented and includes the full stack
|
|
1191
|
+
* ```
|
|
1192
|
+
* {
|
|
1193
|
+
* emessage: '\n' +
|
|
1194
|
+
* ' Error: stream ended before 1 bytes became available\n' +
|
|
1195
|
+
* ' at /home/lion/Code/eth2.0/lodestar/node_modules/it-reader/index.js:37:9\n' +
|
|
1196
|
+
* ' at runMicrotasks (<anonymous>)\n' +
|
|
1197
|
+
* ' at decoder (/home/lion/Code/eth2.0/lodestar/node_modules/it-length-prefixed/src/decode.js:113:22)\n' +
|
|
1198
|
+
* ' at first (/home/lion/Code/eth2.0/lodestar/node_modules/it-first/index.js:11:20)\n' +
|
|
1199
|
+
* ' at Object.exports.read (/home/lion/Code/eth2.0/lodestar/node_modules/multistream-select/src/multistream.js:31:15)\n' +
|
|
1200
|
+
* ' at module.exports (/home/lion/Code/eth2.0/lodestar/node_modules/multistream-select/src/select.js:21:19)\n' +
|
|
1201
|
+
* ' at Upgrader._encryptOutbound (/home/lion/Code/eth2.0/lodestar/node_modules/libp2p/src/upgrader.js:397:36)\n' +
|
|
1202
|
+
* ' at Upgrader.upgradeOutbound (/home/lion/Code/eth2.0/lodestar/node_modules/libp2p/src/upgrader.js:176:11)\n' +
|
|
1203
|
+
* ' at ClassIsWrapper.dial (/home/lion/Code/eth2.0/lodestar/node_modules/libp2p-tcp/src/index.js:49:18)'
|
|
1204
|
+
* }
|
|
1205
|
+
* ```
|
|
1206
|
+
*
|
|
1207
|
+
* Tracking issue https://github.com/libp2p/js-libp2p/issues/996
|
|
1208
|
+
*/
|
|
1209
|
+
function formatLibp2pDialError(e: Error): void {
|
|
1210
|
+
const errorMessage = e.message.trim();
|
|
1211
|
+
const newlineIndex = errorMessage.indexOf('\n');
|
|
1212
|
+
e.message = newlineIndex !== -1 ? errorMessage.slice(0, newlineIndex) : errorMessage;
|
|
1213
|
+
|
|
1214
|
+
if (
|
|
1215
|
+
e.message.includes('The operation was aborted') ||
|
|
1216
|
+
e.message.includes('stream ended before 1 bytes became available') ||
|
|
1217
|
+
e.message.includes('The operation was aborted')
|
|
1218
|
+
) {
|
|
1219
|
+
e.stack = undefined;
|
|
1220
|
+
}
|
|
1221
|
+
}
|