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