@aztec/p2p 0.0.1-commit.4d79d1f2d → 0.0.1-commit.4d9804df
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 +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +22 -6
- package/dest/client/factory.d.ts +9 -8
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +50 -34
- package/dest/client/interface.d.ts +59 -27
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +40 -34
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +162 -135
- package/dest/config.d.ts +164 -100
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +149 -49
- 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 +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +103 -59
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +278 -196
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +224 -65
- package/dest/mem_pools/attestation_pool/mocks.d.ts +2 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +7 -5
- package/dest/mem_pools/index.d.ts +1 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.d.ts +12 -5
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +38 -17
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +1 -5
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +1 -7
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +32 -15
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +95 -24
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +18 -9
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +8 -4
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +12 -4
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +3 -2
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +2 -1
- 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 +54 -5
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +8 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +7 -5
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +9 -7
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +14 -6
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +4 -4
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +16 -4
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +3 -3
- package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +1 -1
- 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 +60 -14
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +6 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +59 -15
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +116 -20
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +26 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +79 -43
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +20 -7
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +34 -12
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +29 -7
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +473 -211
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -15
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +12 -5
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +11 -3
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +12 -5
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +23 -3
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +36 -9
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +130 -48
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +2 -2
- 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.js +1 -1
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
- package/dest/msg_validators/tx_validator/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 +2 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +36 -2
- package/dest/msg_validators/tx_validator/factory.d.ts +133 -7
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +261 -61
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
- package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +104 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +137 -53
- package/dest/msg_validators/tx_validator/index.d.ts +5 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +4 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
- 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 +22 -2
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +72 -24
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.js +6 -6
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/discv5/discV5_service.d.ts +13 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +119 -23
- package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
- package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
- package/dest/services/discv5/persisted_enr_store.js +145 -0
- package/dest/services/dummy_service.d.ts +18 -18
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +19 -20
- package/dest/services/encoding.d.ts +23 -6
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +41 -24
- package/dest/services/gossipsub/topic_score_params.d.ts +23 -17
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +36 -24
- 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 +14 -0
- package/dest/services/libp2p/libp2p_service.d.ts +57 -56
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +483 -373
- package/dest/services/peer-manager/metrics.d.ts +3 -1
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +6 -0
- package/dest/services/peer-manager/peer_manager.d.ts +20 -3
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +83 -26
- 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 +102 -13
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +5 -8
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +95 -127
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +11 -11
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +11 -13
- package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/missing_txs.js +31 -46
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +19 -11
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +52 -15
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
- package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
- package/dest/services/reqresp/config.d.ts +15 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/config.js +11 -0
- package/dest/services/reqresp/interface.d.ts +16 -18
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +10 -20
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/metrics.js +0 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +42 -24
- package/dest/services/reqresp/protocols/index.d.ts +1 -2
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +0 -1
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +1 -3
- package/dest/services/reqresp/rate-limiter/rate_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 +7 -29
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +75 -216
- package/dest/services/service.d.ts +29 -14
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +8 -17
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +17 -40
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +25 -32
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_collection.js +96 -87
- package/dest/services/tx_collection/file_store_tx_source.d.ts +17 -6
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/file_store_tx_source.js +53 -10
- package/dest/services/tx_collection/index.d.ts +2 -3
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +0 -1
- package/dest/services/tx_collection/instrumentation.js +1 -2
- 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 +39 -55
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +280 -108
- package/dest/services/tx_collection/tx_collection_sink.d.ts +7 -6
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +15 -24
- 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/tx_file_store.d.ts +3 -4
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
- package/dest/services/tx_file_store/tx_file_store.js +13 -20
- package/dest/services/tx_provider.d.ts +5 -3
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +7 -4
- package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
- 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 +26 -12
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +51 -45
- 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 +17 -19
- package/dest/test-helpers/test_tx_provider.d.ts +3 -1
- package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
- package/dest/test-helpers/test_tx_provider.js +3 -0
- package/dest/test-helpers/testbench-utils.d.ts +22 -16
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +56 -16
- package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +111 -48
- package/dest/testbench/worker_client_manager.d.ts +12 -6
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +57 -11
- 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 +16 -16
- package/src/bootstrap/bootstrap.ts +17 -3
- package/src/client/factory.ts +95 -51
- package/src/client/interface.ts +59 -35
- package/src/client/p2p_client.ts +186 -196
- package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
- package/src/config.ts +259 -57
- 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 +0 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +336 -242
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +244 -69
- package/src/mem_pools/attestation_pool/mocks.ts +14 -8
- package/src/mem_pools/index.ts +0 -3
- package/src/mem_pools/instrumentation.ts +27 -17
- package/src/mem_pools/tx_pool_v2/README.md +52 -28
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +1 -9
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +114 -27
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +21 -8
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +8 -4
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +18 -4
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +5 -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 +59 -4
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +5 -5
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +8 -8
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +14 -9
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +33 -6
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +4 -3
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +61 -14
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +170 -28
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +91 -43
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +54 -14
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +527 -207
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -14
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +26 -4
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +43 -7
- package/src/msg_validators/proposal_validator/proposal_validator.ts +155 -49
- package/src/msg_validators/tx_validator/README.md +129 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +6 -15
- 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 +1 -1
- 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 +44 -1
- package/src/msg_validators/tx_validator/factory.ts +413 -80
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +195 -54
- package/src/msg_validators/tx_validator/index.ts +4 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
- package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
- package/src/msg_validators/tx_validator/phases_validator.ts +82 -27
- package/src/msg_validators/tx_validator/timestamp_validator.ts +7 -7
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
- package/src/services/data_store.ts +5 -13
- package/src/services/discv5/discV5_service.ts +126 -14
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +26 -45
- package/src/services/encoding.ts +40 -24
- package/src/services/gossipsub/README.md +59 -22
- package/src/services/gossipsub/topic_score_params.ts +58 -31
- package/src/services/libp2p/instrumentation.ts +14 -0
- package/src/services/libp2p/libp2p_service.ts +554 -428
- package/src/services/peer-manager/metrics.ts +7 -0
- package/src/services/peer-manager/peer_manager.ts +91 -25
- package/src/services/peer-manager/peer_scoring.ts +102 -8
- package/src/services/reqresp/README.md +194 -0
- package/src/services/reqresp/batch-tx-requester/README.md +46 -7
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +92 -138
- package/src/services/reqresp/batch-tx-requester/interface.ts +14 -10
- package/src/services/reqresp/batch-tx-requester/missing_txs.ts +30 -71
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +68 -24
- package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
- package/src/services/reqresp/config.ts +19 -2
- package/src/services/reqresp/interface.ts +21 -47
- package/src/services/reqresp/metrics.ts +0 -1
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
- package/src/services/reqresp/protocols/index.ts +0 -1
- package/src/services/reqresp/protocols/tx.ts +1 -3
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
- package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
- package/src/services/reqresp/reqresp.ts +87 -260
- package/src/services/service.ts +42 -30
- package/src/services/tx_collection/config.ts +24 -59
- package/src/services/tx_collection/file_store_tx_collection.ts +108 -107
- package/src/services/tx_collection/file_store_tx_source.ts +69 -10
- package/src/services/tx_collection/index.ts +1 -6
- package/src/services/tx_collection/instrumentation.ts +1 -1
- package/src/services/tx_collection/request_tracker.ts +127 -0
- package/src/services/tx_collection/tx_collection.ts +342 -156
- package/src/services/tx_collection/tx_collection_sink.ts +17 -31
- package/src/services/tx_collection/tx_source.ts +29 -8
- package/src/services/tx_file_store/tx_file_store.ts +11 -21
- package/src/services/tx_provider.ts +7 -2
- package/src/test-helpers/make-test-p2p-clients.ts +4 -3
- package/src/test-helpers/mock-pubsub.ts +59 -66
- package/src/test-helpers/reqresp-nodes.ts +15 -28
- package/src/test-helpers/test_tx_provider.ts +5 -0
- package/src/test-helpers/testbench-utils.ts +74 -31
- package/src/testbench/p2p_client_testbench_worker.ts +120 -63
- package/src/testbench/worker_client_manager.ts +72 -25
- package/src/util.ts +40 -19
- package/src/versioning.ts +3 -33
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -305
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
- 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 -596
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -122
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
- package/dest/mem_pools/tx_pool/index.d.ts +0 -3
- package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/index.js +0 -2
- package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
- package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/priority.js +0 -15
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
- 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 -400
- package/dest/msg_validators/clock_tolerance.d.ts +0 -21
- package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
- package/dest/msg_validators/clock_tolerance.js +0 -37
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
- package/dest/services/reqresp/protocols/block.d.ts +0 -9
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.js +0 -32
- package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -54
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/fast_tx_collection.js +0 -321
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +0 -49
- package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -55
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
- package/dest/services/tx_collection/slow_tx_collection.js +0 -206
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -336
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
- package/src/mem_pools/tx_pool/README.md +0 -270
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
- package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
- package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -162
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
- package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
- package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
- package/src/mem_pools/tx_pool/index.ts +0 -2
- package/src/mem_pools/tx_pool/priority.ts +0 -20
- package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -319
- package/src/msg_validators/clock_tolerance.ts +0 -51
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
- package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
- package/src/services/reqresp/protocols/block.ts +0 -37
- package/src/services/tx_collection/fast_tx_collection.ts +0 -366
- package/src/services/tx_collection/proposal_tx_collector.ts +0 -112
- package/src/services/tx_collection/slow_tx_collection.ts +0 -262
|
@@ -370,14 +370,17 @@ function applyDecs2203RFactory() {
|
|
|
370
370
|
function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
371
371
|
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
372
372
|
}
|
|
373
|
-
var _dec, _dec1, _dec2, _dec3, _dec4, _dec5,
|
|
373
|
+
var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _initProto;
|
|
374
374
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
375
|
+
import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
|
|
375
376
|
import { createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
376
377
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
377
378
|
import { Timer } from '@aztec/foundation/timer';
|
|
378
379
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
379
|
-
import {
|
|
380
|
-
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';
|
|
381
384
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
382
385
|
import { Tx } from '@aztec/stdlib/tx';
|
|
383
386
|
import { compressComponentVersions } from '@aztec/stdlib/versioning';
|
|
@@ -392,25 +395,39 @@ import { identify } from '@libp2p/identify';
|
|
|
392
395
|
import { TopicValidatorResult } from '@libp2p/interface';
|
|
393
396
|
import { mplex } from '@libp2p/mplex';
|
|
394
397
|
import { tcp } from '@libp2p/tcp';
|
|
398
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
|
395
399
|
import { ENR } from '@nethermindeth/enr';
|
|
396
400
|
import { createLibp2p } from 'libp2p';
|
|
401
|
+
import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
|
|
397
402
|
import { BlockProposalValidator, CheckpointAttestationValidator, CheckpointProposalValidator, DoubleSpendTxValidator, FishermanAttestationValidator, getDefaultAllowedSetupFunctions } from '../../msg_validators/index.js';
|
|
398
403
|
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
399
|
-
import {
|
|
404
|
+
import { createFirstStageTxValidationsForGossipedTransactions, createSecondStageTxValidationsForGossipedTransactions, createTxValidatorForBlockProposalReceivedTxs } from '../../msg_validators/tx_validator/factory.js';
|
|
400
405
|
import { GossipSubEvent } from '../../types/index.js';
|
|
401
406
|
import { convertToMultiaddr } from '../../util.js';
|
|
402
407
|
import { getVersions } from '../../versioning.js';
|
|
403
408
|
import { AztecDatastore } from '../data_store.js';
|
|
404
409
|
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
405
|
-
import { SnappyTransform,
|
|
410
|
+
import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
406
411
|
import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
407
412
|
import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
|
|
408
413
|
import { PeerManager } from '../peer-manager/peer_manager.js';
|
|
409
414
|
import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
410
|
-
import {
|
|
411
|
-
import { pingHandler, reqGoodbyeHandler,
|
|
415
|
+
import { YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
|
|
416
|
+
import { ReqRespSubProtocol, ValidationError, pingHandler, reqGoodbyeHandler, reqRespBlockTxsHandler, reqRespStatusHandler, reqRespTxHandler } from '../reqresp/index.js';
|
|
412
417
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
413
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
|
+
}
|
|
414
431
|
_dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation)=>({
|
|
415
432
|
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString()
|
|
416
433
|
})), _dec1 = trackSpan('Libp2pService.validateAndStoreBlockProposal', (_peerId, block)=>({
|
|
@@ -426,23 +443,12 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
426
443
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber,
|
|
427
444
|
[Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
|
|
428
445
|
[Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then((i)=>i.toString())
|
|
429
|
-
})), _dec5 = trackSpan('Libp2pService.
|
|
446
|
+
})), _dec5 = trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', (request)=>({
|
|
430
447
|
[Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString()
|
|
431
|
-
})), _dec6 = trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx)=>({
|
|
432
|
-
[Attributes.TX_HASH]: requestedTxHash.toString()
|
|
433
|
-
})), _dec7 = trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock)=>({
|
|
434
|
-
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString()
|
|
435
|
-
})), _dec8 = trackSpan('Libp2pService.validatePropagatedTx', (tx)=>({
|
|
436
|
-
[Attributes.TX_HASH]: tx.getTxHash().toString()
|
|
437
|
-
})), _dec9 = trackSpan('Libp2pService.validateCheckpointAttestation', async (_, attestation)=>({
|
|
438
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
439
|
-
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
440
|
-
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then((i)=>i.toString())
|
|
441
448
|
}));
|
|
442
449
|
/**
|
|
443
450
|
* Lib P2P implementation of the P2PService interface.
|
|
444
451
|
*/ export class LibP2PService extends WithTracer {
|
|
445
|
-
clientType;
|
|
446
452
|
config;
|
|
447
453
|
node;
|
|
448
454
|
peerDiscoveryService;
|
|
@@ -453,6 +459,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
453
459
|
epochCache;
|
|
454
460
|
proofVerifier;
|
|
455
461
|
worldStateSynchronizer;
|
|
462
|
+
blockMinFeesProvider;
|
|
463
|
+
txValidationCache;
|
|
456
464
|
static{
|
|
457
465
|
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
458
466
|
[
|
|
@@ -483,27 +491,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
483
491
|
[
|
|
484
492
|
_dec5,
|
|
485
493
|
2,
|
|
486
|
-
"
|
|
487
|
-
],
|
|
488
|
-
[
|
|
489
|
-
_dec6,
|
|
490
|
-
2,
|
|
491
|
-
"validateRequestedTxs"
|
|
492
|
-
],
|
|
493
|
-
[
|
|
494
|
-
_dec7,
|
|
495
|
-
2,
|
|
496
|
-
"validateRequestedBlock"
|
|
497
|
-
],
|
|
498
|
-
[
|
|
499
|
-
_dec8,
|
|
500
|
-
2,
|
|
501
|
-
"validatePropagatedTx"
|
|
502
|
-
],
|
|
503
|
-
[
|
|
504
|
-
_dec9,
|
|
505
|
-
2,
|
|
506
|
-
"validateCheckpointAttestation"
|
|
494
|
+
"validateRequestedBlockTxsConsistency"
|
|
507
495
|
]
|
|
508
496
|
], []));
|
|
509
497
|
}
|
|
@@ -515,9 +503,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
515
503
|
checkpointAttestationValidator;
|
|
516
504
|
protocolVersion;
|
|
517
505
|
topicStrings;
|
|
518
|
-
feesCache;
|
|
519
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;
|
|
520
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;
|
|
521
510
|
/**
|
|
522
511
|
* Callback for when a block is received from a peer.
|
|
523
512
|
* @param block - The block received from the peer.
|
|
@@ -527,13 +516,20 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
527
516
|
* Callback for when a checkpoint proposal is received from a peer.
|
|
528
517
|
* @param checkpoint - The checkpoint proposal received from the peer.
|
|
529
518
|
* @returns The attestations for the checkpoint, if any.
|
|
530
|
-
*/
|
|
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;
|
|
531
525
|
gossipSubEventHandler;
|
|
526
|
+
ipChangedHandler;
|
|
527
|
+
discoveredP2pIp;
|
|
532
528
|
instrumentation;
|
|
533
529
|
telemetry;
|
|
534
530
|
logger;
|
|
535
|
-
constructor(
|
|
536
|
-
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 = {};
|
|
537
533
|
this.telemetry = telemetry;
|
|
538
534
|
// Create child logger with fisherman prefix if in fisherman mode
|
|
539
535
|
this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
|
|
@@ -549,40 +545,57 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
549
545
|
this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
|
|
550
546
|
this.topicStrings[TopicType.checkpoint_proposal] = createTopicString(TopicType.checkpoint_proposal, this.protocolVersion);
|
|
551
547
|
this.topicStrings[TopicType.checkpoint_attestation] = createTopicString(TopicType.checkpoint_attestation, this.protocolVersion);
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
txsPermitted: !config.disableTransactions
|
|
557
|
-
|
|
558
|
-
|
|
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);
|
|
559
569
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
560
570
|
this.blockReceivedCallback = async (block)=>{
|
|
561
|
-
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.`, {
|
|
562
572
|
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier()
|
|
563
573
|
});
|
|
564
|
-
return
|
|
574
|
+
return true;
|
|
575
|
+
};
|
|
576
|
+
this.allNodesCheckpointReceivedCallback = (_checkpoint)=>{
|
|
577
|
+
throw new CheckpointProposalReceivedCallbackNotRegisteredError();
|
|
565
578
|
};
|
|
566
|
-
this.
|
|
567
|
-
this.logger.debug(`Handler not yet registered: Checkpoint received callback not set. Received checkpoint for slot ${checkpoint.slotNumber} from peer.`);
|
|
579
|
+
this.validatorCheckpointReceivedCallback = (_checkpoint)=>{
|
|
568
580
|
return Promise.resolve(undefined);
|
|
569
581
|
};
|
|
570
582
|
}
|
|
571
583
|
updateConfig(config) {
|
|
572
584
|
this.reqresp.updateConfig(config);
|
|
585
|
+
this.config = merge(this.config, config);
|
|
573
586
|
}
|
|
574
587
|
/**
|
|
575
588
|
* Creates an instance of the LibP2P service.
|
|
576
589
|
* @param config - The configuration to use when creating the service.
|
|
577
590
|
* @param txPool - The transaction pool to be accessed by the service.
|
|
578
591
|
* @returns The new service.
|
|
579
|
-
*/ static async new(
|
|
580
|
-
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;
|
|
581
594
|
const { p2pPort, maxPeerCount, listenAddress } = config;
|
|
582
595
|
const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
|
|
583
596
|
const datastore = new AztecDatastore(peerStore);
|
|
584
597
|
const otelMetricsAdapter = new OtelMetricsAdapter(telemetry, logger.getBindings());
|
|
585
|
-
const peerDiscoveryService = new DiscV5Service(peerId, config, packageVersion, telemetry, createLogger(`${logger.module}:discv5_service`, logger.getBindings()));
|
|
598
|
+
const peerDiscoveryService = new DiscV5Service(peerId, config, packageVersion, telemetry, createLogger(`${logger.module}:discv5_service`, logger.getBindings()), peerStore);
|
|
586
599
|
// Seed libp2p's bootstrap discovery with private and trusted peers
|
|
587
600
|
const bootstrapNodes = [
|
|
588
601
|
...config.privatePeers,
|
|
@@ -597,30 +610,45 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
597
610
|
const versions = getVersions(config);
|
|
598
611
|
const protocolVersion = compressComponentVersions(versions);
|
|
599
612
|
const preferredPeersEnrs = config.preferredPeers.map((enr)=>ENR.decodeTxt(enr));
|
|
600
|
-
const directPeers = (await Promise.all(preferredPeersEnrs.map(async (enr)=>{
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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;
|
|
605
632
|
}
|
|
606
|
-
|
|
607
|
-
id: peerId,
|
|
608
|
-
addrs: [
|
|
609
|
-
address
|
|
610
|
-
]
|
|
611
|
-
};
|
|
612
|
-
}))).filter((peer)=>peer !== undefined);
|
|
633
|
+
})));
|
|
613
634
|
const announceTcpMultiaddr = config.p2pIp ? [
|
|
614
635
|
convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')
|
|
615
636
|
] : [];
|
|
616
|
-
// Create dynamic topic score params based on network configuration
|
|
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.
|
|
617
640
|
const l1Constants = epochCache.getL1Constants();
|
|
618
641
|
const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
|
|
619
642
|
slotDurationMs: l1Constants.slotDuration * 1000,
|
|
620
643
|
heartbeatIntervalMs: config.gossipsubInterval,
|
|
621
644
|
targetCommitteeSize: l1Constants.targetCommitteeSize,
|
|
622
|
-
|
|
645
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
|
|
646
|
+
expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot
|
|
623
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));
|
|
624
652
|
const node = await createLibp2p({
|
|
625
653
|
start: false,
|
|
626
654
|
peerId,
|
|
@@ -654,8 +682,11 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
654
682
|
],
|
|
655
683
|
datastore,
|
|
656
684
|
peerDiscovery,
|
|
685
|
+
// Pin the yamux frame size: MAX_REQRESP_REQUEST_SIZE_BYTES relies on a reqresp request fitting in one frame.
|
|
657
686
|
streamMuxers: [
|
|
658
|
-
yamux(
|
|
687
|
+
yamux({
|
|
688
|
+
maxMessageSize: YAMUX_MAX_MESSAGE_SIZE_BYTES
|
|
689
|
+
}),
|
|
659
690
|
mplex()
|
|
660
691
|
],
|
|
661
692
|
connectionEncryption: [
|
|
@@ -673,7 +704,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
673
704
|
},
|
|
674
705
|
connectionGater: {
|
|
675
706
|
denyInboundConnection: (maConn)=>{
|
|
676
|
-
const allowed = peerManager.
|
|
707
|
+
const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
|
|
677
708
|
if (allowed) {
|
|
678
709
|
return false;
|
|
679
710
|
}
|
|
@@ -683,7 +714,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
683
714
|
denyInboundEncryptedConnection: (peerId, _maConn)=>{
|
|
684
715
|
//NOTE: it is not necessary to check address here because this was already done by
|
|
685
716
|
// denyInboundConnection
|
|
686
|
-
const allowed = peerManager.
|
|
717
|
+
const allowed = peerManager.isPeerAllowedToConnect(peerId);
|
|
687
718
|
if (allowed) {
|
|
688
719
|
return false;
|
|
689
720
|
}
|
|
@@ -710,9 +741,13 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
710
741
|
mcacheLength: config.gossipsubMcacheLength,
|
|
711
742
|
mcacheGossip: config.gossipsubMcacheGossip,
|
|
712
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.
|
|
713
749
|
msgIdFn: getMsgIdFn,
|
|
714
750
|
msgIdToStrFn: msgIdToStrFn,
|
|
715
|
-
fastMsgIdFn: fastMsgIdFn,
|
|
716
751
|
dataTransform: new SnappyTransform(),
|
|
717
752
|
metricsRegister: otelMetricsAdapter,
|
|
718
753
|
metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
|
|
@@ -725,7 +760,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
725
760
|
})
|
|
726
761
|
}),
|
|
727
762
|
components: (components)=>({
|
|
728
|
-
connectionManager: components.connectionManager
|
|
763
|
+
connectionManager: components.connectionManager,
|
|
764
|
+
addressManager: components.addressManager
|
|
729
765
|
})
|
|
730
766
|
},
|
|
731
767
|
logger: createLibp2pComponentLogger(logger.module, logger.getBindings())
|
|
@@ -733,6 +769,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
733
769
|
const peerScoring = new PeerScoring(config, telemetry);
|
|
734
770
|
const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
|
|
735
771
|
const peerManager = new PeerManager(node, peerDiscoveryService, config, telemetry, createLogger(`${logger.module}:peer_manager`), peerScoring, reqresp, worldStateSynchronizer, protocolVersion, epochCache);
|
|
772
|
+
// Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
|
|
773
|
+
reqresp.setShouldRejectPeer((peerId)=>peerManager.shouldDisableP2PGossip(peerId));
|
|
736
774
|
// Configure application-specific scoring for gossipsub.
|
|
737
775
|
// The weight scales app score to align with gossipsub thresholds:
|
|
738
776
|
// - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
|
|
@@ -740,7 +778,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
740
778
|
// Note: positive topic scores can offset penalties, so alignment is best-effort.
|
|
741
779
|
node.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
|
|
742
780
|
node.services.pubsub.score.params.appSpecificScore = (peerId)=>peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
|
|
743
|
-
return new LibP2PService(
|
|
781
|
+
return new LibP2PService(config, node, peerDiscoveryService, reqresp, peerManager, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, blockMinFeesProvider, telemetry, logger, txValidationCache);
|
|
744
782
|
}
|
|
745
783
|
/**
|
|
746
784
|
* Starts the LibP2P service.
|
|
@@ -750,22 +788,19 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
750
788
|
if (this.node.status === 'started') {
|
|
751
789
|
throw new Error('P2P service already started');
|
|
752
790
|
}
|
|
753
|
-
// Get listen & announce addresses for logging
|
|
754
791
|
const { p2pIp, p2pPort } = this.config;
|
|
755
|
-
if (!p2pIp) {
|
|
756
|
-
throw new Error('Announce address not provided.');
|
|
792
|
+
if (!p2pIp && !this.config.queryForIp) {
|
|
793
|
+
throw new Error('Announce address not provided and queryForIp is not enabled.');
|
|
757
794
|
}
|
|
758
|
-
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
795
|
+
const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
|
|
759
796
|
// Create request response protocol handlers
|
|
760
797
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
761
798
|
const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
|
|
762
|
-
const blockHandler = reqRespBlockHandler(this.archiver);
|
|
763
799
|
const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
|
|
764
800
|
const requestResponseHandlers = {
|
|
765
801
|
[ReqRespSubProtocol.PING]: pingHandler,
|
|
766
802
|
[ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
|
|
767
|
-
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this)
|
|
768
|
-
[ReqRespSubProtocol.BLOCK]: blockHandler.bind(this)
|
|
803
|
+
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this)
|
|
769
804
|
};
|
|
770
805
|
if (!this.config.disableTransactions) {
|
|
771
806
|
const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.archiver, this.mempools.txPool);
|
|
@@ -774,18 +809,11 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
774
809
|
if (!this.config.disableTransactions) {
|
|
775
810
|
requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
|
|
776
811
|
}
|
|
777
|
-
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
778
|
-
const reqrespSubProtocolValidators = {
|
|
779
|
-
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
780
|
-
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
781
|
-
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
782
|
-
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this)
|
|
783
|
-
};
|
|
784
812
|
await this.peerManager.initializePeers();
|
|
785
|
-
await this.reqresp.start(requestResponseHandlers
|
|
813
|
+
await this.reqresp.start(requestResponseHandlers);
|
|
786
814
|
await this.node.start();
|
|
787
815
|
// Subscribe to standard GossipSub topics by default
|
|
788
|
-
for (const topic of
|
|
816
|
+
for (const topic of getTopicsForConfig(this.config.disableTransactions)){
|
|
789
817
|
this.subscribeToTopic(this.topicStrings[topic]);
|
|
790
818
|
}
|
|
791
819
|
// add GossipSub listener
|
|
@@ -794,6 +822,34 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
794
822
|
if (!this.config.p2pDiscoveryDisabled) {
|
|
795
823
|
await this.peerDiscoveryService.start();
|
|
796
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
|
+
}
|
|
797
853
|
this.discoveryRunningPromise = new RunningPromise(async ()=>{
|
|
798
854
|
await this.peerManager.heartbeat();
|
|
799
855
|
}, this.logger, this.config.peerCheckIntervalMS);
|
|
@@ -811,6 +867,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
811
867
|
*/ async stop() {
|
|
812
868
|
// Remove gossip sub listener
|
|
813
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
|
+
}
|
|
814
874
|
// Stop peer manager
|
|
815
875
|
this.logger.debug('Stopping peer manager...');
|
|
816
876
|
await this.peerManager.stop();
|
|
@@ -824,8 +884,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
824
884
|
await this.stopLibP2P();
|
|
825
885
|
this.logger.info('LibP2P service stopped');
|
|
826
886
|
}
|
|
827
|
-
addReqRespSubProtocol(subProtocol, handler
|
|
828
|
-
return this.reqresp.addSubProtocol(subProtocol, handler
|
|
887
|
+
addReqRespSubProtocol(subProtocol, handler) {
|
|
888
|
+
return this.reqresp.addSubProtocol(subProtocol, handler);
|
|
829
889
|
}
|
|
830
890
|
registerThisValidatorAddresses(address) {
|
|
831
891
|
this.peerManager.registerThisValidatorAddresses(address);
|
|
@@ -833,6 +893,9 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
833
893
|
getPeers(includePending) {
|
|
834
894
|
return this.peerManager.getPeers(includePending);
|
|
835
895
|
}
|
|
896
|
+
getGossipMeshPeerCount(topicType) {
|
|
897
|
+
return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
|
|
898
|
+
}
|
|
836
899
|
handleGossipSubEvent(e) {
|
|
837
900
|
this.logger.trace(`Received PUBSUB message.`);
|
|
838
901
|
const safeJob = async ()=>{
|
|
@@ -844,14 +907,6 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
844
907
|
};
|
|
845
908
|
setImmediate(()=>void safeJob());
|
|
846
909
|
}
|
|
847
|
-
/**
|
|
848
|
-
* Send a batch of requests to peers, and return the responses
|
|
849
|
-
* @param protocol - The request response protocol to use
|
|
850
|
-
* @param requests - The requests to send to the peers
|
|
851
|
-
* @returns The responses to the requests
|
|
852
|
-
*/ sendBatchRequest(protocol, requests, pinnedPeerId) {
|
|
853
|
-
return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
|
|
854
|
-
}
|
|
855
910
|
sendRequestToPeer(peerId, subProtocol, payload, dialTimeout) {
|
|
856
911
|
return this.reqresp.sendRequestToPeer(peerId, subProtocol, payload, dialTimeout);
|
|
857
912
|
}
|
|
@@ -864,8 +919,11 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
864
919
|
registerBlockReceivedCallback(callback) {
|
|
865
920
|
this.blockReceivedCallback = callback;
|
|
866
921
|
}
|
|
867
|
-
|
|
868
|
-
this.
|
|
922
|
+
registerValidatorCheckpointReceivedCallback(callback) {
|
|
923
|
+
this.validatorCheckpointReceivedCallback = callback;
|
|
924
|
+
}
|
|
925
|
+
registerAllNodesCheckpointReceivedCallback(callback) {
|
|
926
|
+
this.allNodesCheckpointReceivedCallback = callback;
|
|
869
927
|
}
|
|
870
928
|
/**
|
|
871
929
|
* Registers a callback to be invoked when a duplicate proposal is detected.
|
|
@@ -874,12 +932,20 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
874
932
|
this.duplicateProposalCallback = callback;
|
|
875
933
|
}
|
|
876
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
|
+
/**
|
|
877
940
|
* Registers a callback to be invoked when a duplicate attestation is detected.
|
|
878
941
|
* A validator signing attestations for different proposals at the same slot.
|
|
879
942
|
* This callback is triggered on the first duplicate (when count goes from 1 to 2).
|
|
880
943
|
*/ registerDuplicateAttestationCallback(callback) {
|
|
881
944
|
this.duplicateAttestationCallback = callback;
|
|
882
945
|
}
|
|
946
|
+
registerCheckpointAttestationCallback(callback) {
|
|
947
|
+
this.checkpointAttestationCallback = callback;
|
|
948
|
+
}
|
|
883
949
|
/**
|
|
884
950
|
* Subscribes to a topic.
|
|
885
951
|
* @param topic - The topic to subscribe to.
|
|
@@ -931,6 +997,12 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
931
997
|
if (!validator || !validator.addMessage(msgId)) {
|
|
932
998
|
this.instrumentation.incMessagePrevalidationStatus(false, topicType);
|
|
933
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
|
+
}
|
|
934
1006
|
return {
|
|
935
1007
|
result: false,
|
|
936
1008
|
topicType
|
|
@@ -988,12 +1060,17 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
988
1060
|
}
|
|
989
1061
|
// Process the message, optionally within a linked span for trace propagation
|
|
990
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;
|
|
1069
|
+
}
|
|
991
1070
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
992
1071
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
993
1072
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
994
|
-
|
|
995
|
-
await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
996
|
-
}
|
|
1073
|
+
await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
997
1074
|
} else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
998
1075
|
await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
|
|
999
1076
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
|
|
@@ -1038,39 +1115,131 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1038
1115
|
return;
|
|
1039
1116
|
}
|
|
1040
1117
|
async validateReceivedMessage(validationFunc, msgId, source, topicType) {
|
|
1118
|
+
// Default to reject result with a penalty if validation function throws an error
|
|
1041
1119
|
let resultAndObj = {
|
|
1042
|
-
result: TopicValidatorResult.Reject
|
|
1120
|
+
result: TopicValidatorResult.Reject,
|
|
1121
|
+
severity: PeerErrorSeverity.MidToleranceError
|
|
1043
1122
|
};
|
|
1044
1123
|
const timer = new Timer();
|
|
1045
1124
|
try {
|
|
1046
1125
|
resultAndObj = await validationFunc();
|
|
1047
1126
|
} catch (err) {
|
|
1048
|
-
this.
|
|
1049
|
-
this.logger.error(`Error deserializing and validating gossipsub message`, err, {
|
|
1127
|
+
this.logger.error(`Error validating gossipsub message`, err, {
|
|
1050
1128
|
msgId,
|
|
1051
1129
|
source: source.toString(),
|
|
1052
1130
|
topicType
|
|
1053
1131
|
});
|
|
1054
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
|
+
}
|
|
1055
1145
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
1146
|
+
this.logger.debug(`Message ${topicType} accepted by validator`, {
|
|
1147
|
+
msgId,
|
|
1148
|
+
source: source.toString(),
|
|
1149
|
+
topicType
|
|
1150
|
+
});
|
|
1056
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
|
+
});
|
|
1057
1166
|
}
|
|
1058
1167
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
1059
1168
|
return resultAndObj;
|
|
1060
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
|
+
}
|
|
1061
1182
|
async handleGossipedTx(payloadData, msgId, source) {
|
|
1062
1183
|
const validationFunc = async ()=>{
|
|
1063
|
-
const tx = Tx.fromBuffer(payloadData);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
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()
|
|
1068
1236
|
});
|
|
1069
1237
|
return {
|
|
1070
|
-
result: TopicValidatorResult.Reject
|
|
1238
|
+
result: TopicValidatorResult.Reject,
|
|
1239
|
+
severity
|
|
1071
1240
|
};
|
|
1072
1241
|
}
|
|
1073
|
-
//
|
|
1242
|
+
// Pool add: persist the tx
|
|
1074
1243
|
const txHash = tx.getTxHash();
|
|
1075
1244
|
const addResult = await this.mempools.txPool.addPendingTxs([
|
|
1076
1245
|
tx
|
|
@@ -1079,8 +1248,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1079
1248
|
});
|
|
1080
1249
|
const wasAccepted = addResult.accepted.some((h)=>h.equals(txHash));
|
|
1081
1250
|
const wasIgnored = addResult.ignored.some((h)=>h.equals(txHash));
|
|
1082
|
-
this.logger.
|
|
1083
|
-
isValid,
|
|
1251
|
+
this.logger.verbose(`Validate propagated tx ${txHash.toString()}`, {
|
|
1084
1252
|
wasAccepted,
|
|
1085
1253
|
wasIgnored,
|
|
1086
1254
|
[Attributes.P2P_ID]: source.toString()
|
|
@@ -1096,8 +1264,13 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1096
1264
|
obj: tx
|
|
1097
1265
|
};
|
|
1098
1266
|
} else {
|
|
1267
|
+
this.logger.warn(`Gossiped tx ${txHash.toString()} unexpectedly rejected by pool`, {
|
|
1268
|
+
source: source.toString(),
|
|
1269
|
+
txHash: txHash.toString()
|
|
1270
|
+
});
|
|
1099
1271
|
return {
|
|
1100
|
-
result: TopicValidatorResult.Reject
|
|
1272
|
+
result: TopicValidatorResult.Reject,
|
|
1273
|
+
severity: PeerErrorSeverity.HighToleranceError
|
|
1101
1274
|
};
|
|
1102
1275
|
}
|
|
1103
1276
|
};
|
|
@@ -1118,7 +1291,16 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1118
1291
|
* Process a checkpoint attestation from a peer.
|
|
1119
1292
|
* Validates the attestation and adds it to the pool.
|
|
1120
1293
|
*/ async processCheckpointAttestationFromPeer(payloadData, msgId, source) {
|
|
1121
|
-
const { result, obj: attestation } = await this.validateReceivedMessage(()=>
|
|
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
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
return this.validateAndStoreCheckpointAttestation(source, attestation);
|
|
1303
|
+
}, msgId, source, TopicType.checkpoint_attestation);
|
|
1122
1304
|
if (result !== TopicValidatorResult.Accept || !attestation) {
|
|
1123
1305
|
return;
|
|
1124
1306
|
}
|
|
@@ -1133,9 +1315,9 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1133
1315
|
const validationResult = await this.checkpointAttestationValidator.validate(attestation);
|
|
1134
1316
|
if (validationResult.result === 'reject') {
|
|
1135
1317
|
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1136
|
-
this.peerManager.penalizePeer(peerId, validationResult.severity);
|
|
1137
1318
|
return {
|
|
1138
|
-
result: TopicValidatorResult.Reject
|
|
1319
|
+
result: TopicValidatorResult.Reject,
|
|
1320
|
+
severity: validationResult.severity
|
|
1139
1321
|
};
|
|
1140
1322
|
}
|
|
1141
1323
|
if (validationResult.result === 'ignore') {
|
|
@@ -1162,9 +1344,9 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1162
1344
|
obj: attestation
|
|
1163
1345
|
};
|
|
1164
1346
|
}
|
|
1165
|
-
// Could not add (cap reached for signer),
|
|
1347
|
+
// Could not add (cap reached for signer), penalize and do not re-broadcast
|
|
1166
1348
|
if (!added) {
|
|
1167
|
-
this.logger.warn(`
|
|
1349
|
+
this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
|
|
1168
1350
|
slot: slot.toString(),
|
|
1169
1351
|
archive: attestation.archive.toString(),
|
|
1170
1352
|
source: peerId.toString(),
|
|
@@ -1172,8 +1354,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1172
1354
|
count
|
|
1173
1355
|
});
|
|
1174
1356
|
return {
|
|
1175
|
-
result: TopicValidatorResult.
|
|
1176
|
-
|
|
1357
|
+
result: TopicValidatorResult.Reject,
|
|
1358
|
+
severity: PeerErrorSeverity.HighToleranceError
|
|
1177
1359
|
};
|
|
1178
1360
|
}
|
|
1179
1361
|
// Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
|
|
@@ -1194,15 +1376,17 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1194
1376
|
}
|
|
1195
1377
|
}
|
|
1196
1378
|
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1379
|
+
this.checkpointAttestationCallback?.(attestation);
|
|
1197
1380
|
return {
|
|
1198
1381
|
result: TopicValidatorResult.Accept,
|
|
1199
1382
|
obj: attestation
|
|
1200
1383
|
};
|
|
1201
1384
|
}
|
|
1202
1385
|
async processBlockFromPeer(payloadData, msgId, source) {
|
|
1203
|
-
const { result, obj: block, metadata: { isEquivocated } = {} } = await this.validateReceivedMessage(()=>this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)), msgId, source, TopicType.block_proposal);
|
|
1204
|
-
// If not accepted or
|
|
1205
|
-
|
|
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) {
|
|
1206
1390
|
return;
|
|
1207
1391
|
}
|
|
1208
1392
|
await this.processValidBlockProposal(block, source);
|
|
@@ -1211,9 +1395,9 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1211
1395
|
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1212
1396
|
if (validationResult.result === 'reject') {
|
|
1213
1397
|
this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1214
|
-
this.peerManager.penalizePeer(peerId, validationResult.severity);
|
|
1215
1398
|
return {
|
|
1216
|
-
result: TopicValidatorResult.Reject
|
|
1399
|
+
result: TopicValidatorResult.Reject,
|
|
1400
|
+
severity: validationResult.severity
|
|
1217
1401
|
};
|
|
1218
1402
|
}
|
|
1219
1403
|
if (validationResult.result === 'ignore') {
|
|
@@ -1225,6 +1409,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1225
1409
|
// Try to add the proposal: this handles existence check, cap check, and adding in one call
|
|
1226
1410
|
const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
|
|
1227
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;
|
|
1228
1416
|
// Duplicate proposal received, no need to re-broadcast
|
|
1229
1417
|
if (alreadyExists) {
|
|
1230
1418
|
this.logger.debug(`Ignoring duplicate block proposal received`, {
|
|
@@ -1237,13 +1425,13 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1237
1425
|
result: TopicValidatorResult.Ignore,
|
|
1238
1426
|
obj: block,
|
|
1239
1427
|
metadata: {
|
|
1240
|
-
isEquivocated
|
|
1428
|
+
isEquivocated,
|
|
1429
|
+
isOversized
|
|
1241
1430
|
}
|
|
1242
1431
|
};
|
|
1243
1432
|
}
|
|
1244
1433
|
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
1245
1434
|
if (!added) {
|
|
1246
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
1247
1435
|
this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
|
|
1248
1436
|
...block.toBlockInfo(),
|
|
1249
1437
|
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
@@ -1254,10 +1442,30 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1254
1442
|
return {
|
|
1255
1443
|
result: TopicValidatorResult.Reject,
|
|
1256
1444
|
metadata: {
|
|
1257
|
-
isEquivocated
|
|
1258
|
-
|
|
1445
|
+
isEquivocated,
|
|
1446
|
+
isOversized
|
|
1447
|
+
},
|
|
1448
|
+
severity: PeerErrorSeverity.HighToleranceError
|
|
1259
1449
|
};
|
|
1260
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
|
+
}
|
|
1261
1469
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1262
1470
|
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1263
1471
|
if (isEquivocated) {
|
|
@@ -1279,14 +1487,19 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1279
1487
|
result: TopicValidatorResult.Accept,
|
|
1280
1488
|
obj: block,
|
|
1281
1489
|
metadata: {
|
|
1282
|
-
isEquivocated
|
|
1490
|
+
isEquivocated,
|
|
1491
|
+
isOversized
|
|
1283
1492
|
}
|
|
1284
1493
|
};
|
|
1285
1494
|
}
|
|
1286
1495
|
// Otherwise, we're good to go!
|
|
1287
1496
|
return {
|
|
1288
1497
|
result: TopicValidatorResult.Accept,
|
|
1289
|
-
obj: block
|
|
1498
|
+
obj: block,
|
|
1499
|
+
metadata: {
|
|
1500
|
+
isEquivocated: false,
|
|
1501
|
+
isOversized
|
|
1502
|
+
}
|
|
1290
1503
|
};
|
|
1291
1504
|
}
|
|
1292
1505
|
// REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
|
|
@@ -1304,23 +1517,36 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1304
1517
|
// Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
|
|
1305
1518
|
const isValid = await this.blockReceivedCallback(block, sender);
|
|
1306
1519
|
if (!isValid) {
|
|
1307
|
-
this.logger.
|
|
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);
|
|
1308
1524
|
}
|
|
1309
1525
|
}
|
|
1310
1526
|
/**
|
|
1311
1527
|
* Handle a gossiped checkpoint proposal.
|
|
1312
1528
|
* Validates and processes the checkpoint proposal, then triggers the callback for attestation.
|
|
1313
1529
|
*/ async handleGossipedCheckpointProposal(payloadData, msgId, source) {
|
|
1314
|
-
const { result, obj: checkpoint, metadata: { isEquivocated, processBlock } = {} } = await this.validateReceivedMessage(()=>this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)), msgId, source, TopicType.checkpoint_proposal);
|
|
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();
|
|
1315
1534
|
// If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
|
|
1316
1535
|
// TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
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();
|
|
1321
1545
|
return;
|
|
1322
1546
|
}
|
|
1323
|
-
|
|
1547
|
+
// Process the block first, since it's required for the checkpoint proposal validation.
|
|
1548
|
+
await processBlockFn();
|
|
1549
|
+
await processCheckpointFn();
|
|
1324
1550
|
}
|
|
1325
1551
|
/**
|
|
1326
1552
|
* Validates a checkpoint proposal. Penalizes peer if validation fails. Adds the checkpoint and
|
|
@@ -1329,9 +1555,9 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1329
1555
|
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1330
1556
|
if (validationResult.result === 'reject') {
|
|
1331
1557
|
this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
|
|
1332
|
-
this.peerManager.penalizePeer(peerId, validationResult.severity);
|
|
1333
1558
|
return {
|
|
1334
|
-
result: TopicValidatorResult.Reject
|
|
1559
|
+
result: TopicValidatorResult.Reject,
|
|
1560
|
+
severity: validationResult.severity
|
|
1335
1561
|
};
|
|
1336
1562
|
}
|
|
1337
1563
|
if (validationResult.result === 'ignore') {
|
|
@@ -1343,23 +1569,28 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1343
1569
|
// Extract and try to add the block proposal first if present
|
|
1344
1570
|
const blockProposal = checkpoint.getBlockProposal();
|
|
1345
1571
|
let processBlock = false;
|
|
1572
|
+
let isOversized = false;
|
|
1346
1573
|
if (blockProposal) {
|
|
1347
1574
|
this.logger.debug(`Validating block proposal from propagated checkpoint`, {
|
|
1348
1575
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1349
1576
|
[Attributes.P2P_ID]: peerId.toString()
|
|
1350
1577
|
});
|
|
1351
|
-
const
|
|
1352
|
-
|
|
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) {
|
|
1353
1582
|
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1354
1583
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1355
1584
|
[Attributes.P2P_ID]: peerId.toString(),
|
|
1356
1585
|
isEquivocated,
|
|
1357
|
-
result
|
|
1586
|
+
result: blockProposalResult.result
|
|
1358
1587
|
});
|
|
1359
1588
|
return {
|
|
1360
|
-
result: TopicValidatorResult.Reject
|
|
1589
|
+
result: TopicValidatorResult.Reject,
|
|
1590
|
+
severity: 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError
|
|
1361
1591
|
};
|
|
1362
|
-
} else if (result === TopicValidatorResult.Accept && obj && !isEquivocated) {
|
|
1592
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
|
|
1593
|
+
// An oversized terminal block is re-broadcast as slashing evidence but never processed.
|
|
1363
1594
|
processBlock = true;
|
|
1364
1595
|
}
|
|
1365
1596
|
}
|
|
@@ -1379,14 +1610,14 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1379
1610
|
obj: checkpoint,
|
|
1380
1611
|
metadata: {
|
|
1381
1612
|
isEquivocated,
|
|
1382
|
-
processBlock
|
|
1613
|
+
processBlock,
|
|
1614
|
+
isOversized
|
|
1383
1615
|
}
|
|
1384
1616
|
};
|
|
1385
1617
|
}
|
|
1386
|
-
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
|
|
1618
|
+
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
|
|
1387
1619
|
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1388
1620
|
if (!added) {
|
|
1389
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
1390
1621
|
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
1391
1622
|
...checkpoint.toCheckpointInfo(),
|
|
1392
1623
|
count,
|
|
@@ -1397,8 +1628,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1397
1628
|
obj: checkpoint,
|
|
1398
1629
|
metadata: {
|
|
1399
1630
|
isEquivocated,
|
|
1400
|
-
processBlock
|
|
1401
|
-
|
|
1631
|
+
processBlock,
|
|
1632
|
+
isOversized
|
|
1633
|
+
},
|
|
1634
|
+
severity: PeerErrorSeverity.HighToleranceError
|
|
1402
1635
|
};
|
|
1403
1636
|
}
|
|
1404
1637
|
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
@@ -1423,7 +1656,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1423
1656
|
obj: checkpoint,
|
|
1424
1657
|
metadata: {
|
|
1425
1658
|
isEquivocated,
|
|
1426
|
-
processBlock
|
|
1659
|
+
processBlock,
|
|
1660
|
+
isOversized
|
|
1427
1661
|
}
|
|
1428
1662
|
};
|
|
1429
1663
|
}
|
|
@@ -1433,7 +1667,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1433
1667
|
obj: checkpoint,
|
|
1434
1668
|
metadata: {
|
|
1435
1669
|
processBlock,
|
|
1436
|
-
isEquivocated
|
|
1670
|
+
isEquivocated,
|
|
1671
|
+
isOversized
|
|
1437
1672
|
}
|
|
1438
1673
|
};
|
|
1439
1674
|
}
|
|
@@ -1448,9 +1683,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1448
1683
|
archive: checkpoint.archive.toString(),
|
|
1449
1684
|
source: sender.toString()
|
|
1450
1685
|
});
|
|
1686
|
+
await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
|
|
1451
1687
|
// Call the checkpoint received callback with the core version (without lastBlock)
|
|
1452
1688
|
// to validate and potentially generate attestations
|
|
1453
|
-
const attestations = await this.
|
|
1689
|
+
const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
|
|
1454
1690
|
if (attestations && attestations.length > 0) {
|
|
1455
1691
|
// If the callback returned attestations, add them to the pool and propagate them
|
|
1456
1692
|
await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
|
|
@@ -1474,192 +1710,78 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1474
1710
|
});
|
|
1475
1711
|
}
|
|
1476
1712
|
/**
|
|
1477
|
-
* Validate the requested block transactions
|
|
1713
|
+
* Validate the requested block transactions request-response consistency.
|
|
1714
|
+
* It does NOT validate the transactions themselves.
|
|
1478
1715
|
* @param request - The block transactions request.
|
|
1479
1716
|
* @param response - The block transactions response.
|
|
1480
1717
|
* @param peerId - The ID of the peer that made the request.
|
|
1481
|
-
* @returns True if the
|
|
1482
|
-
*/ async
|
|
1483
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1718
|
+
* @returns True if the request-response is consistent, false otherwise.
|
|
1719
|
+
*/ async validateRequestedBlockTxsConsistency(request, response, peerId) {
|
|
1484
1720
|
try {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
throw new ValidationError(`Received block txs for unexpected archive root: expected ${request.archiveRoot.toString()}, got ${response.archiveRoot.toString()}`);
|
|
1488
|
-
}
|
|
1489
|
-
if (response.txIndices.getLength() !== request.txIndices.getLength()) {
|
|
1490
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1491
|
-
throw new ValidationError(`Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`);
|
|
1492
|
-
}
|
|
1493
|
-
// Check no duplicates and not exceeding returnable count
|
|
1494
|
-
const requestedIndices = new Set(request.txIndices.getTrueIndices());
|
|
1495
|
-
const availableIndices = new Set(response.txIndices.getTrueIndices());
|
|
1496
|
-
const maxReturnable = [
|
|
1497
|
-
...requestedIndices
|
|
1498
|
-
].filter((i)=>availableIndices.has(i)).length;
|
|
1499
|
-
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());
|
|
1500
1723
|
const uniqueReturned = new Set(returnedHashes.map((h)=>h.toString()));
|
|
1501
1724
|
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1502
1725
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1503
1726
|
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1504
1727
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
if (
|
|
1512
|
-
//
|
|
1513
|
-
|
|
1514
|
-
//
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
i
|
|
1518
|
-
]));
|
|
1519
|
-
const allowedIndexSet = new Set(intersectIdx);
|
|
1520
|
-
const indices = returnedHashes.map((h)=>hashToIndexInProposal.get(h));
|
|
1521
|
-
const allAllowed = indices.every((idx)=>idx !== undefined && allowedIndexSet.has(idx));
|
|
1522
|
-
const strictlyIncreasing = indices.every((idx, i)=>i === 0 ? idx !== undefined : idx > indices[i - 1]);
|
|
1523
|
-
if (!allAllowed || !strictlyIncreasing) {
|
|
1524
|
-
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1525
|
-
throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
|
|
1526
|
-
}
|
|
1527
|
-
} else {
|
|
1528
|
-
// No local proposal, cannot check the membership/order of the returned txs
|
|
1529
|
-
this.logger.warn(`Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`);
|
|
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.
|
|
1530
1740
|
return false;
|
|
1531
1741
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
return false;
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
/**
|
|
1544
|
-
* Validate a collection of txs that has been requested from a peer.
|
|
1545
|
-
*
|
|
1546
|
-
* The core component of this validator is that each tx hash MUST match the requested tx hash,
|
|
1547
|
-
* In order to perform this check, the tx proof must be verified.
|
|
1548
|
-
*
|
|
1549
|
-
* Note: This function is called from within `ReqResp.sendRequest` as part of the
|
|
1550
|
-
* ReqRespSubProtocol.TX subprotocol validation.
|
|
1551
|
-
*
|
|
1552
|
-
* @param requestedTxHash - The collection of the txs that was requested.
|
|
1553
|
-
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
1554
|
-
* @param peerId - The peer ID of the peer that sent the tx.
|
|
1555
|
-
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
1556
|
-
*/ async validateRequestedTxs(requestedTxHash, responseTx, peerId) {
|
|
1557
|
-
const requested = new Set(requestedTxHash.map((h)=>h.toString()));
|
|
1558
|
-
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1559
|
-
//TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
|
|
1560
|
-
// I think we should still extract the valid txs and return them, so that we can still use the response.
|
|
1561
|
-
try {
|
|
1562
|
-
await Promise.all(responseTx.map((tx)=>this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
|
|
1563
|
-
return true;
|
|
1564
|
-
} catch (e) {
|
|
1565
|
-
if (e instanceof ValidationError) {
|
|
1566
|
-
this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
|
|
1567
|
-
} else {
|
|
1568
|
-
this.logger.error(`Error during validation of requested txs`, e);
|
|
1569
|
-
}
|
|
1570
|
-
return false;
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
/**
|
|
1574
|
-
* Validates a BLOCK response.
|
|
1575
|
-
*
|
|
1576
|
-
* If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
|
|
1577
|
-
* Penalizes on block number mismatch or hash mismatch.
|
|
1578
|
-
*
|
|
1579
|
-
* @param requestedBlockNumber - The requested block number.
|
|
1580
|
-
* @param responseBlock - The block returned by the peer.
|
|
1581
|
-
* @param peerId - The peer that returned the block.
|
|
1582
|
-
* @returns True if the response is valid, false otherwise.
|
|
1583
|
-
*/ async validateRequestedBlock(requestedBlockNumber, responseBlock, peerId) {
|
|
1584
|
-
try {
|
|
1585
|
-
const reqNum = Number(requestedBlockNumber.toString());
|
|
1586
|
-
if (responseBlock.number !== reqNum) {
|
|
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))) {
|
|
1587
1750
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1588
|
-
|
|
1751
|
+
throw new ValidationError('Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)');
|
|
1589
1752
|
}
|
|
1590
|
-
|
|
1591
|
-
if (!
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
// performing quorum-based checks when using P2P syncing prior to L1 sync.
|
|
1595
|
-
this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
|
|
1596
|
-
return false;
|
|
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;
|
|
1597
1757
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
responseBlock.hash()
|
|
1601
|
-
]);
|
|
1602
|
-
if (!localHash.equals(respHash)) {
|
|
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) {
|
|
1603
1760
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1604
|
-
|
|
1761
|
+
throw new ValidationError(`Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`);
|
|
1762
|
+
}
|
|
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');
|
|
1605
1772
|
}
|
|
1606
1773
|
return true;
|
|
1607
1774
|
} catch (e) {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
async validateRequestedTx(tx, peerId, txValidator, requested) {
|
|
1613
|
-
const penalize = (severity)=>this.peerManager.penalizePeer(peerId, severity);
|
|
1614
|
-
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1615
|
-
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1616
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1617
|
-
}
|
|
1618
|
-
const { result } = await txValidator.validateTx(tx);
|
|
1619
|
-
if (result === 'invalid') {
|
|
1620
|
-
penalize(PeerErrorSeverity.LowToleranceError);
|
|
1621
|
-
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
createRequestedTxValidator() {
|
|
1625
|
-
return createTxReqRespValidator(this.proofVerifier, {
|
|
1626
|
-
l1ChainId: this.config.l1ChainId,
|
|
1627
|
-
rollupVersion: this.config.rollupVersion
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
async validatePropagatedTx(tx, peerId) {
|
|
1631
|
-
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1632
|
-
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
1633
|
-
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1634
|
-
const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
1635
|
-
for (const validator of messageValidators){
|
|
1636
|
-
const outcome = await this.runValidations(tx, validator);
|
|
1637
|
-
if (outcome.allPassed) {
|
|
1638
|
-
continue;
|
|
1639
|
-
}
|
|
1640
|
-
const { name } = outcome.failure;
|
|
1641
|
-
let { severity } = outcome.failure;
|
|
1642
|
-
// Double spend validator has a special case handler
|
|
1643
|
-
if (name === 'doubleSpendValidator') {
|
|
1644
|
-
const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
|
|
1645
|
-
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
1775
|
+
if (e instanceof ValidationError) {
|
|
1776
|
+
this.logger.warn(`Failed validation for requested block txs from peer ${peerId.toString()}`);
|
|
1777
|
+
} else {
|
|
1778
|
+
this.logger.error(`Error during validation of requested block txs`, e);
|
|
1646
1779
|
}
|
|
1647
|
-
this.peerManager.penalizePeer(peerId, severity);
|
|
1648
1780
|
return false;
|
|
1649
1781
|
}
|
|
1650
|
-
return true;
|
|
1651
1782
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
return this.feesCache.gasFees;
|
|
1655
|
-
}
|
|
1656
|
-
const header = await this.archiver.getBlockHeader(blockNumber);
|
|
1657
|
-
const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
|
|
1658
|
-
this.feesCache = {
|
|
1659
|
-
blockNumber,
|
|
1660
|
-
gasFees
|
|
1661
|
-
};
|
|
1662
|
-
return gasFees;
|
|
1783
|
+
getGasFees() {
|
|
1784
|
+
return this.blockMinFeesProvider.getCurrentMinFees();
|
|
1663
1785
|
}
|
|
1664
1786
|
/**
|
|
1665
1787
|
* Get the BatchTxRequesterLibP2PService dependencies for creating BatchTxRequester instances
|
|
@@ -1670,43 +1792,42 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1670
1792
|
txValidatorConfig: {
|
|
1671
1793
|
l1ChainId: this.config.l1ChainId,
|
|
1672
1794
|
rollupVersion: this.config.rollupVersion,
|
|
1673
|
-
proofVerifier: this.proofVerifier
|
|
1795
|
+
proofVerifier: this.proofVerifier,
|
|
1796
|
+
txValidationCache: this.txValidationCache
|
|
1674
1797
|
},
|
|
1675
|
-
peerScoring: this.peerManager
|
|
1798
|
+
peerScoring: this.peerManager,
|
|
1799
|
+
validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this)
|
|
1676
1800
|
};
|
|
1677
1801
|
}
|
|
1678
|
-
async
|
|
1679
|
-
const
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
await Promise.all(txs.map(async (tx)=>{
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
if (!outcome.allPassed) {
|
|
1687
|
-
throw new Error('Invalid tx detected', {
|
|
1688
|
-
cause: {
|
|
1689
|
-
outcome
|
|
1690
|
-
}
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
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';
|
|
1694
1810
|
}));
|
|
1811
|
+
if (results.some((value)=>value === false)) {
|
|
1812
|
+
throw new Error('Invalid tx detected');
|
|
1813
|
+
}
|
|
1695
1814
|
}
|
|
1696
|
-
/**
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
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());
|
|
1710
1831
|
}
|
|
1711
1832
|
/**
|
|
1712
1833
|
* Run validations on a tx.
|
|
@@ -1724,8 +1845,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1724
1845
|
});
|
|
1725
1846
|
// A promise that resolves when all validations have been run
|
|
1726
1847
|
const allValidations = await Promise.all(validationPromises);
|
|
1727
|
-
const
|
|
1728
|
-
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));
|
|
1729
1852
|
return {
|
|
1730
1853
|
allPassed: false,
|
|
1731
1854
|
failure: {
|
|
@@ -1772,19 +1895,6 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
|
|
|
1772
1895
|
}
|
|
1773
1896
|
return PeerErrorSeverity.HighToleranceError;
|
|
1774
1897
|
}
|
|
1775
|
-
/**
|
|
1776
|
-
* Validate a checkpoint attestation.
|
|
1777
|
-
*
|
|
1778
|
-
* @param attestation - The checkpoint attestation to validate.
|
|
1779
|
-
* @returns True if the checkpoint attestation is valid, false otherwise.
|
|
1780
|
-
*/ async validateCheckpointAttestation(peerId, attestation) {
|
|
1781
|
-
const result = await this.checkpointAttestationValidator.validate(attestation);
|
|
1782
|
-
if (result.result === 'reject') {
|
|
1783
|
-
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1784
|
-
this.peerManager.penalizePeer(peerId, result.severity);
|
|
1785
|
-
}
|
|
1786
|
-
return result;
|
|
1787
|
-
}
|
|
1788
1898
|
getPeerScore(peerId) {
|
|
1789
1899
|
return this.node.services.pubsub.score.score(peerId.toString());
|
|
1790
1900
|
}
|