@aztec/p2p 0.0.1-commit.c2595eba → 0.0.1-commit.c2eed6949
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -3
- package/dest/client/factory.d.ts +11 -11
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +54 -15
- package/dest/client/interface.d.ts +46 -33
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +41 -51
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +168 -224
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +7 -8
- package/dest/config.d.ts +60 -17
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +105 -39
- package/dest/errors/tx-pool.error.d.ts +8 -0
- package/dest/errors/tx-pool.error.d.ts.map +1 -0
- package/dest/errors/tx-pool.error.js +9 -0
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +104 -88
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +445 -3
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +353 -87
- package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
- package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/index.js +1 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts +2 -2
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +2 -2
- package/dest/mem_pools/index.d.ts +3 -2
- package/dest/mem_pools/index.d.ts.map +1 -1
- package/dest/mem_pools/index.js +1 -1
- package/dest/mem_pools/instrumentation.d.ts +4 -2
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +16 -14
- package/dest/mem_pools/interface.d.ts +5 -5
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +2 -1
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +3 -3
- package/dest/mem_pools/tx_pool/priority.d.ts +2 -2
- package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/priority.js +4 -4
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +3 -1
- package/dest/mem_pools/tx_pool_v2/archive/index.d.ts +2 -0
- package/dest/mem_pools/tx_pool_v2/archive/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/index.js +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +43 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +103 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +47 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +128 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +94 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +19 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +97 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +10 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/index.js +11 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +180 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +25 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +65 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +93 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +16 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +78 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +20 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +75 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +19 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts +6 -0
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/index.js +5 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +215 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/interfaces.js +10 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +134 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +216 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +108 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +337 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +62 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +167 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +78 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +923 -0
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +5 -4
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.d.ts +1 -1
- package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.js +4 -3
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +10 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +6 -4
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +16 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -8
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +53 -41
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
- 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 +24 -20
- 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/block_header_validator.d.ts +16 -3
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
- 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 +1 -1
- package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/data_validator.js +35 -2
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
- package/dest/msg_validators/tx_validator/factory.d.ts +133 -6
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +247 -60
- 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 +67 -3
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +104 -37
- package/dest/msg_validators/tx_validator/index.d.ts +3 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +2 -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 +20 -4
- 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/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +10 -6
- package/dest/services/discv5/discV5_service.d.ts +1 -1
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +4 -2
- package/dest/services/dummy_service.d.ts +13 -5
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +10 -4
- package/dest/services/encoding.d.ts +7 -3
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +18 -11
- package/dest/services/gossipsub/index.d.ts +3 -0
- package/dest/services/gossipsub/index.d.ts.map +1 -0
- package/dest/services/gossipsub/index.js +2 -0
- package/dest/services/gossipsub/scoring.d.ts +21 -3
- package/dest/services/gossipsub/scoring.d.ts.map +1 -1
- package/dest/services/gossipsub/scoring.js +24 -7
- package/dest/services/gossipsub/topic_score_params.d.ts +173 -0
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
- package/dest/services/gossipsub/topic_score_params.js +346 -0
- package/dest/services/index.d.ts +2 -1
- package/dest/services/index.d.ts.map +1 -1
- package/dest/services/index.js +1 -0
- package/dest/services/libp2p/libp2p_service.d.ts +99 -49
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +559 -382
- 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 +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +6 -3
- package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +25 -2
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +14 -10
- 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 +89 -112
- package/dest/services/reqresp/batch-tx-requester/interface.d.ts +4 -7
- 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.js +2 -2
- package/dest/services/reqresp/interface.d.ts +10 -1
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +15 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +17 -12
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +25 -14
- 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 +40 -24
- package/dest/services/reqresp/protocols/tx.d.ts +7 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +20 -0
- package/dest/services/reqresp/reqresp.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +30 -14
- package/dest/services/service.d.ts +45 -3
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +22 -4
- package/dest/services/tx_collection/config.d.ts.map +1 -1
- package/dest/services/tx_collection/config.js +49 -3
- package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -8
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +88 -88
- package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
- package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_collection.js +167 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts +37 -0
- package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/file_store_tx_source.js +90 -0
- package/dest/services/tx_collection/index.d.ts +3 -2
- package/dest/services/tx_collection/index.d.ts.map +1 -1
- package/dest/services/tx_collection/index.js +1 -0
- package/dest/services/tx_collection/instrumentation.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
- package/dest/services/tx_collection/instrumentation.js +2 -1
- package/dest/services/tx_collection/proposal_tx_collector.d.ts +15 -15
- package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
- package/dest/services/tx_collection/proposal_tx_collector.js +6 -6
- 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/slow_tx_collection.d.ts +7 -3
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +60 -26
- package/dest/services/tx_collection/tx_collection.d.ts +23 -13
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +75 -3
- package/dest/services/tx_collection/tx_collection_sink.d.ts +18 -8
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +26 -29
- package/dest/services/tx_collection/tx_source.d.ts +8 -3
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +19 -2
- package/dest/services/tx_file_store/config.d.ts +16 -0
- package/dest/services/tx_file_store/config.d.ts.map +1 -0
- package/dest/services/tx_file_store/config.js +22 -0
- package/dest/services/tx_file_store/index.d.ts +4 -0
- package/dest/services/tx_file_store/index.d.ts.map +1 -0
- package/dest/services/tx_file_store/index.js +3 -0
- package/dest/services/tx_file_store/instrumentation.d.ts +15 -0
- package/dest/services/tx_file_store/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_file_store/instrumentation.js +29 -0
- package/dest/services/tx_file_store/tx_file_store.d.ts +48 -0
- package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -0
- package/dest/services/tx_file_store/tx_file_store.js +152 -0
- package/dest/services/tx_provider.d.ts +4 -4
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +9 -8
- package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +1 -2
- package/dest/test-helpers/mock-pubsub.d.ts +35 -4
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +114 -5
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +4 -3
- package/dest/test-helpers/testbench-utils.d.ts +43 -38
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +149 -61
- package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -2
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +17 -16
- package/dest/testbench/worker_client_manager.d.ts +3 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +6 -2
- package/dest/util.d.ts +10 -5
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +2 -9
- package/package.json +14 -14
- package/src/client/factory.ts +103 -28
- package/src/client/interface.ts +56 -34
- package/src/client/p2p_client.ts +202 -269
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +19 -12
- package/src/config.ts +165 -44
- package/src/errors/tx-pool.error.ts +12 -0
- package/src/index.ts +1 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +497 -91
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
- package/src/mem_pools/attestation_pool/index.ts +9 -2
- package/src/mem_pools/attestation_pool/mocks.ts +2 -1
- package/src/mem_pools/index.ts +4 -1
- package/src/mem_pools/instrumentation.ts +17 -13
- package/src/mem_pools/interface.ts +4 -4
- package/src/mem_pools/tx_pool/README.md +1 -1
- package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +2 -1
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +3 -3
- package/src/mem_pools/tx_pool/priority.ts +4 -4
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +3 -1
- package/src/mem_pools/tx_pool_v2/README.md +283 -0
- package/src/mem_pools/tx_pool_v2/archive/index.ts +1 -0
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +120 -0
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
- package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +160 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +122 -0
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +125 -0
- package/src/mem_pools/tx_pool_v2/eviction/index.ts +27 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +219 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +74 -0
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +101 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +91 -0
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +99 -0
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +32 -0
- package/src/mem_pools/tx_pool_v2/index.ts +12 -0
- package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
- package/src/mem_pools/tx_pool_v2/interfaces.ts +247 -0
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +337 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +430 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +238 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +1105 -0
- package/src/msg_validators/attestation_validator/README.md +49 -0
- package/src/msg_validators/attestation_validator/attestation_validator.ts +5 -4
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
- package/src/msg_validators/clock_tolerance.ts +4 -3
- package/src/msg_validators/proposal_validator/README.md +123 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +14 -4
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +20 -7
- package/src/msg_validators/proposal_validator/proposal_validator.ts +69 -45
- package/src/msg_validators/tx_validator/README.md +119 -0
- package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +5 -5
- 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/block_header_validator.ts +15 -3
- package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
- package/src/msg_validators/tx_validator/data_validator.ts +42 -1
- package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
- package/src/msg_validators/tx_validator/factory.ts +394 -78
- package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
- package/src/msg_validators/tx_validator/gas_validator.ts +123 -27
- package/src/msg_validators/tx_validator/index.ts +2 -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 +23 -18
- package/src/services/data_store.ts +10 -7
- package/src/services/discv5/discV5_service.ts +4 -2
- package/src/services/dummy_service.ts +18 -6
- package/src/services/encoding.ts +18 -10
- package/src/services/gossipsub/README.md +641 -0
- package/src/services/gossipsub/index.ts +2 -0
- package/src/services/gossipsub/scoring.ts +29 -5
- package/src/services/gossipsub/topic_score_params.ts +487 -0
- package/src/services/index.ts +1 -0
- package/src/services/libp2p/libp2p_service.ts +584 -415
- package/src/services/peer-manager/metrics.ts +7 -0
- package/src/services/peer-manager/peer_manager.ts +7 -3
- package/src/services/peer-manager/peer_scoring.ts +25 -0
- package/src/services/reqresp/README.md +229 -0
- package/src/services/reqresp/batch-tx-requester/README.md +60 -21
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +89 -122
- package/src/services/reqresp/batch-tx-requester/interface.ts +3 -6
- 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 +2 -2
- package/src/services/reqresp/interface.ts +26 -1
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +24 -15
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -24
- package/src/services/reqresp/protocols/tx.ts +22 -0
- package/src/services/reqresp/reqresp.ts +35 -15
- package/src/services/service.ts +58 -2
- package/src/services/tx_collection/config.ts +74 -6
- package/src/services/tx_collection/fast_tx_collection.ts +94 -97
- package/src/services/tx_collection/file_store_tx_collection.ts +202 -0
- package/src/services/tx_collection/file_store_tx_source.ts +117 -0
- package/src/services/tx_collection/index.ts +2 -1
- package/src/services/tx_collection/instrumentation.ts +7 -1
- package/src/services/tx_collection/proposal_tx_collector.ts +21 -27
- package/src/services/tx_collection/request_tracker.ts +127 -0
- package/src/services/tx_collection/slow_tx_collection.ts +66 -33
- package/src/services/tx_collection/tx_collection.ts +114 -19
- package/src/services/tx_collection/tx_collection_sink.ts +30 -34
- package/src/services/tx_collection/tx_source.ts +22 -3
- package/src/services/tx_file_store/config.ts +37 -0
- package/src/services/tx_file_store/index.ts +3 -0
- package/src/services/tx_file_store/instrumentation.ts +36 -0
- package/src/services/tx_file_store/tx_file_store.ts +175 -0
- package/src/services/tx_provider.ts +10 -9
- package/src/test-helpers/make-test-p2p-clients.ts +4 -6
- package/src/test-helpers/mock-pubsub.ts +155 -9
- package/src/test-helpers/reqresp-nodes.ts +5 -7
- package/src/test-helpers/testbench-utils.ts +156 -74
- package/src/testbench/p2p_client_testbench_worker.ts +24 -22
- package/src/testbench/worker_client_manager.ts +13 -5
- package/src/util.ts +16 -14
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
- 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/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import {
|
|
2
|
+
import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { maxBy } from '@aztec/foundation/collection';
|
|
4
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
5
|
import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
6
6
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
@@ -17,13 +17,12 @@ import {
|
|
|
17
17
|
CheckpointProposal,
|
|
18
18
|
type CheckpointProposalCore,
|
|
19
19
|
type Gossipable,
|
|
20
|
-
P2PClientType,
|
|
21
20
|
P2PMessage,
|
|
22
|
-
type ValidationResult as P2PValidationResult,
|
|
23
21
|
PeerErrorSeverity,
|
|
22
|
+
PeerErrorSeverityByHarshness,
|
|
24
23
|
TopicType,
|
|
25
24
|
createTopicString,
|
|
26
|
-
|
|
25
|
+
getTopicsForConfig,
|
|
27
26
|
metricsTopicStrToLabels,
|
|
28
27
|
} from '@aztec/stdlib/p2p';
|
|
29
28
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
@@ -45,49 +44,55 @@ import {
|
|
|
45
44
|
type GossipsubMessage,
|
|
46
45
|
gossipsub,
|
|
47
46
|
} from '@chainsafe/libp2p-gossipsub';
|
|
48
|
-
import { createPeerScoreParams
|
|
47
|
+
import { createPeerScoreParams } from '@chainsafe/libp2p-gossipsub/score';
|
|
49
48
|
import { SignaturePolicy } from '@chainsafe/libp2p-gossipsub/types';
|
|
50
49
|
import { noise } from '@chainsafe/libp2p-noise';
|
|
51
50
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
52
51
|
import { bootstrap } from '@libp2p/bootstrap';
|
|
53
52
|
import { identify } from '@libp2p/identify';
|
|
54
53
|
import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
55
|
-
import type { ConnectionManager } from '@libp2p/interface-internal';
|
|
54
|
+
import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
|
|
56
55
|
import { mplex } from '@libp2p/mplex';
|
|
57
56
|
import { tcp } from '@libp2p/tcp';
|
|
57
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
|
58
58
|
import { ENR } from '@nethermindeth/enr';
|
|
59
59
|
import { createLibp2p } from 'libp2p';
|
|
60
60
|
|
|
61
61
|
import type { P2PConfig } from '../../config.js';
|
|
62
|
-
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
63
62
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
64
63
|
import {
|
|
65
64
|
BlockProposalValidator,
|
|
66
65
|
CheckpointAttestationValidator,
|
|
67
66
|
CheckpointProposalValidator,
|
|
67
|
+
DoubleSpendTxValidator,
|
|
68
68
|
FishermanAttestationValidator,
|
|
69
|
+
getDefaultAllowedSetupFunctions,
|
|
69
70
|
} from '../../msg_validators/index.js';
|
|
70
71
|
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
71
|
-
import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
|
|
72
72
|
import {
|
|
73
|
-
type
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
type TransactionValidator,
|
|
74
|
+
createFirstStageTxValidationsForGossipedTransactions,
|
|
75
|
+
createSecondStageTxValidationsForGossipedTransactions,
|
|
76
|
+
createTxValidatorForBlockProposalReceivedTxs,
|
|
77
|
+
createTxValidatorForReqResponseReceivedTxs,
|
|
76
78
|
} from '../../msg_validators/tx_validator/factory.js';
|
|
77
|
-
import { DoubleSpendTxValidator } from '../../msg_validators/tx_validator/index.js';
|
|
78
79
|
import { GossipSubEvent } from '../../types/index.js';
|
|
79
80
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
80
81
|
import { getVersions } from '../../versioning.js';
|
|
81
82
|
import { AztecDatastore } from '../data_store.js';
|
|
82
83
|
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
83
84
|
import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
84
|
-
import { gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
85
|
+
import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
86
|
+
import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
|
|
85
87
|
import type { PeerManagerInterface } from '../peer-manager/interface.js';
|
|
86
88
|
import { PeerManager } from '../peer-manager/peer_manager.js';
|
|
87
89
|
import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
88
90
|
import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
|
|
89
91
|
import type { P2PReqRespConfig } from '../reqresp/config.js';
|
|
90
92
|
import {
|
|
93
|
+
AuthRequest,
|
|
94
|
+
BlockTxsRequest,
|
|
95
|
+
BlockTxsResponse,
|
|
91
96
|
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
92
97
|
type ReqRespInterface,
|
|
93
98
|
type ReqRespResponse,
|
|
@@ -95,25 +100,21 @@ import {
|
|
|
95
100
|
type ReqRespSubProtocolHandler,
|
|
96
101
|
type ReqRespSubProtocolHandlers,
|
|
97
102
|
type ReqRespSubProtocolValidators,
|
|
103
|
+
StatusMessage,
|
|
98
104
|
type SubProtocolMap,
|
|
99
105
|
ValidationError,
|
|
100
|
-
} from '../reqresp/interface.js';
|
|
101
|
-
import { reqRespBlockTxsHandler } from '../reqresp/protocols/block_txs/block_txs_handler.js';
|
|
102
|
-
import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
|
|
103
|
-
import {
|
|
104
|
-
AuthRequest,
|
|
105
|
-
BlockTxsRequest,
|
|
106
|
-
BlockTxsResponse,
|
|
107
|
-
StatusMessage,
|
|
108
106
|
pingHandler,
|
|
107
|
+
reqGoodbyeHandler,
|
|
109
108
|
reqRespBlockHandler,
|
|
109
|
+
reqRespBlockTxsHandler,
|
|
110
110
|
reqRespStatusHandler,
|
|
111
111
|
reqRespTxHandler,
|
|
112
|
-
} from '../reqresp/
|
|
112
|
+
} from '../reqresp/index.js';
|
|
113
113
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
114
114
|
import type {
|
|
115
115
|
P2PBlockReceivedCallback,
|
|
116
116
|
P2PCheckpointReceivedCallback,
|
|
117
|
+
P2PDuplicateAttestationCallback,
|
|
117
118
|
P2PService,
|
|
118
119
|
PeerDiscoveryService,
|
|
119
120
|
} from '../service.js';
|
|
@@ -128,14 +129,14 @@ interface ValidationResult {
|
|
|
128
129
|
type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
|
|
129
130
|
|
|
130
131
|
// REFACTOR: Unify with the type above
|
|
131
|
-
type ReceivedMessageValidationResult<T> =
|
|
132
|
-
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject
|
|
133
|
-
| { obj?:
|
|
132
|
+
type ReceivedMessageValidationResult<T, M = undefined> =
|
|
133
|
+
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
|
|
134
|
+
| { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
|
|
134
135
|
|
|
135
136
|
/**
|
|
136
137
|
* Lib P2P implementation of the P2PService interface.
|
|
137
138
|
*/
|
|
138
|
-
export class LibP2PService
|
|
139
|
+
export class LibP2PService extends WithTracer implements P2PService {
|
|
139
140
|
private discoveryRunningPromise?: RunningPromise;
|
|
140
141
|
private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
|
|
141
142
|
|
|
@@ -149,6 +150,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
149
150
|
|
|
150
151
|
private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
|
|
151
152
|
|
|
153
|
+
/** Callback invoked when a duplicate proposal is detected (triggers slashing). */
|
|
154
|
+
private duplicateProposalCallback?: (info: {
|
|
155
|
+
slot: SlotNumber;
|
|
156
|
+
proposer: EthAddress;
|
|
157
|
+
type: 'checkpoint' | 'block';
|
|
158
|
+
}) => void;
|
|
159
|
+
|
|
160
|
+
/** Callback invoked when a duplicate attestation is detected (triggers slashing). */
|
|
161
|
+
private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
|
|
162
|
+
|
|
152
163
|
/**
|
|
153
164
|
* Callback for when a block is received from a peer.
|
|
154
165
|
* @param block - The block received from the peer.
|
|
@@ -164,6 +175,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
164
175
|
private checkpointReceivedCallback: P2PCheckpointReceivedCallback;
|
|
165
176
|
|
|
166
177
|
private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
|
|
178
|
+
private ipChangedHandler?: (ip: string) => void;
|
|
179
|
+
|
|
180
|
+
/** Discovered public IP address (set when queryForIp is enabled and no static IP was configured). */
|
|
181
|
+
private discoveredP2pIp?: string;
|
|
167
182
|
|
|
168
183
|
private instrumentation: P2PInstrumentation;
|
|
169
184
|
|
|
@@ -172,14 +187,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
172
187
|
protected logger: Logger;
|
|
173
188
|
|
|
174
189
|
constructor(
|
|
175
|
-
private clientType: T,
|
|
176
190
|
private config: P2PConfig,
|
|
177
191
|
protected node: PubSubLibp2p,
|
|
178
192
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
179
193
|
private reqresp: ReqRespInterface,
|
|
180
|
-
|
|
194
|
+
protected peerManager: PeerManagerInterface,
|
|
181
195
|
protected mempools: MemPools,
|
|
182
|
-
|
|
196
|
+
protected archiver: L2BlockSource & ContractDataSource,
|
|
183
197
|
private epochCache: EpochCacheInterface,
|
|
184
198
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
185
199
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
@@ -214,10 +228,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
214
228
|
this.protocolVersion,
|
|
215
229
|
);
|
|
216
230
|
|
|
217
|
-
|
|
218
|
-
this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, {
|
|
231
|
+
const proposalValidatorOpts = {
|
|
219
232
|
txsPermitted: !config.disableTransactions,
|
|
220
|
-
|
|
233
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
|
|
234
|
+
};
|
|
235
|
+
this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
|
|
236
|
+
this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
|
|
221
237
|
this.checkpointAttestationValidator = config.fishermanMode
|
|
222
238
|
? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry)
|
|
223
239
|
: new CheckpointAttestationValidator(epochCache);
|
|
@@ -225,11 +241,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
225
241
|
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
226
242
|
|
|
227
243
|
this.blockReceivedCallback = async (block: BlockProposal): Promise<boolean> => {
|
|
228
|
-
this.logger.
|
|
229
|
-
`Handler not yet registered
|
|
244
|
+
this.logger.warn(
|
|
245
|
+
`Handler for block received not yet registered on P2P service. Received block ${block.blockNumber} for slot ${block.slotNumber} from peer.`,
|
|
230
246
|
{ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
|
|
231
247
|
);
|
|
232
|
-
return
|
|
248
|
+
return true;
|
|
233
249
|
};
|
|
234
250
|
|
|
235
251
|
this.checkpointReceivedCallback = (
|
|
@@ -252,8 +268,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
252
268
|
* @param txPool - The transaction pool to be accessed by the service.
|
|
253
269
|
* @returns The new service.
|
|
254
270
|
*/
|
|
255
|
-
public static async new
|
|
256
|
-
clientType: T,
|
|
271
|
+
public static async new(
|
|
257
272
|
config: P2PConfig,
|
|
258
273
|
peerId: PeerId,
|
|
259
274
|
deps: {
|
|
@@ -305,11 +320,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
305
320
|
const versions = getVersions(config);
|
|
306
321
|
const protocolVersion = compressComponentVersions(versions);
|
|
307
322
|
|
|
308
|
-
const txTopic = createTopicString(TopicType.tx, protocolVersion);
|
|
309
|
-
const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion);
|
|
310
|
-
const checkpointProposalTopic = createTopicString(TopicType.checkpoint_proposal, protocolVersion);
|
|
311
|
-
const checkpointAttestationTopic = createTopicString(TopicType.checkpoint_attestation, protocolVersion);
|
|
312
|
-
|
|
313
323
|
const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
314
324
|
const directPeers = (
|
|
315
325
|
await Promise.all(
|
|
@@ -329,6 +339,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
329
339
|
|
|
330
340
|
const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
|
|
331
341
|
|
|
342
|
+
// Create dynamic topic score params based on network configuration
|
|
343
|
+
const l1Constants = epochCache.getL1Constants();
|
|
344
|
+
const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
|
|
345
|
+
slotDurationMs: l1Constants.slotDuration * 1000,
|
|
346
|
+
heartbeatIntervalMs: config.gossipsubInterval,
|
|
347
|
+
targetCommitteeSize: l1Constants.targetCommitteeSize,
|
|
348
|
+
blockDurationMs: config.blockDurationMs,
|
|
349
|
+
expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
|
|
350
|
+
});
|
|
351
|
+
|
|
332
352
|
const node = await createLibp2p({
|
|
333
353
|
start: false,
|
|
334
354
|
peerId,
|
|
@@ -424,32 +444,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
424
444
|
scoreParams: createPeerScoreParams({
|
|
425
445
|
// IPColocation factor can be disabled for local testing - default to -5
|
|
426
446
|
IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
|
|
427
|
-
topics:
|
|
428
|
-
[txTopic]: createTopicScoreParams({
|
|
429
|
-
topicWeight: 1,
|
|
430
|
-
invalidMessageDeliveriesWeight: -20,
|
|
431
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
432
|
-
}),
|
|
433
|
-
[blockProposalTopic]: createTopicScoreParams({
|
|
434
|
-
topicWeight: 1,
|
|
435
|
-
invalidMessageDeliveriesWeight: -20,
|
|
436
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
437
|
-
}),
|
|
438
|
-
[checkpointProposalTopic]: createTopicScoreParams({
|
|
439
|
-
topicWeight: 1,
|
|
440
|
-
invalidMessageDeliveriesWeight: -20,
|
|
441
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
442
|
-
}),
|
|
443
|
-
[checkpointAttestationTopic]: createTopicScoreParams({
|
|
444
|
-
topicWeight: 1,
|
|
445
|
-
invalidMessageDeliveriesWeight: -20,
|
|
446
|
-
invalidMessageDeliveriesDecay: 0.5,
|
|
447
|
-
}),
|
|
448
|
-
},
|
|
447
|
+
topics: topicScoreParams,
|
|
449
448
|
}),
|
|
450
449
|
}) as (components: GossipSubComponents) => GossipSub,
|
|
451
|
-
components: (components: { connectionManager: ConnectionManager }) => ({
|
|
450
|
+
components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
|
|
452
451
|
connectionManager: components.connectionManager,
|
|
452
|
+
addressManager: components.addressManager,
|
|
453
453
|
}),
|
|
454
454
|
},
|
|
455
455
|
logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
|
|
@@ -471,13 +471,16 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
471
471
|
epochCache,
|
|
472
472
|
);
|
|
473
473
|
|
|
474
|
-
//
|
|
475
|
-
|
|
474
|
+
// Configure application-specific scoring for gossipsub.
|
|
475
|
+
// The weight scales app score to align with gossipsub thresholds:
|
|
476
|
+
// - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
|
|
477
|
+
// - Ban (-100) × 10 = -1000 = publishThreshold (cannot publish)
|
|
478
|
+
// Note: positive topic scores can offset penalties, so alignment is best-effort.
|
|
479
|
+
node.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
|
|
476
480
|
node.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
|
|
477
481
|
peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
|
|
478
482
|
|
|
479
483
|
return new LibP2PService(
|
|
480
|
-
clientType,
|
|
481
484
|
config,
|
|
482
485
|
node,
|
|
483
486
|
peerDiscoveryService,
|
|
@@ -505,10 +508,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
505
508
|
|
|
506
509
|
// Get listen & announce addresses for logging
|
|
507
510
|
const { p2pIp, p2pPort } = this.config;
|
|
508
|
-
if (!p2pIp) {
|
|
511
|
+
if (!p2pIp && !this.config.queryForIp) {
|
|
509
512
|
throw new Error('Announce address not provided.');
|
|
510
513
|
}
|
|
511
|
-
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
514
|
+
const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
|
|
512
515
|
|
|
513
516
|
// Create request response protocol handlers
|
|
514
517
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
@@ -524,7 +527,11 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
524
527
|
};
|
|
525
528
|
|
|
526
529
|
if (!this.config.disableTransactions) {
|
|
527
|
-
const blockTxsHandler = reqRespBlockTxsHandler(
|
|
530
|
+
const blockTxsHandler = reqRespBlockTxsHandler(
|
|
531
|
+
this.mempools.attestationPool,
|
|
532
|
+
this.archiver,
|
|
533
|
+
this.mempools.txPool,
|
|
534
|
+
);
|
|
528
535
|
requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
|
|
529
536
|
}
|
|
530
537
|
|
|
@@ -547,7 +554,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
547
554
|
await this.node.start();
|
|
548
555
|
|
|
549
556
|
// Subscribe to standard GossipSub topics by default
|
|
550
|
-
for (const topic of
|
|
557
|
+
for (const topic of getTopicsForConfig(this.config.disableTransactions)) {
|
|
551
558
|
this.subscribeToTopic(this.topicStrings[topic]);
|
|
552
559
|
}
|
|
553
560
|
|
|
@@ -558,6 +565,31 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
558
565
|
if (!this.config.p2pDiscoveryDisabled) {
|
|
559
566
|
await this.peerDiscoveryService.start();
|
|
560
567
|
}
|
|
568
|
+
|
|
569
|
+
// When queryForIp is enabled and no static IP was configured, bridge discv5 IP discovery to libp2p.
|
|
570
|
+
// Discv5 discovers our public IP via peer WHOAREYOU exchanges (enrUpdate=true) and emits 'ip:changed'.
|
|
571
|
+
// We confirm the discovered address in the libp2p AddressManager so it appears in getMultiaddrs()
|
|
572
|
+
// and is pushed to all connected peers via the identify protocol.
|
|
573
|
+
if (this.config.queryForIp && !p2pIp) {
|
|
574
|
+
this.ipChangedHandler = (ip: string) => {
|
|
575
|
+
const addressManager = this.node.services.components.addressManager;
|
|
576
|
+
const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
|
|
577
|
+
|
|
578
|
+
// Remove old discovered IP if one exists
|
|
579
|
+
if (this.discoveredP2pIp) {
|
|
580
|
+
const oldAddr = multiaddr(convertToMultiaddr(this.discoveredP2pIp, this.config.p2pPort, 'tcp'));
|
|
581
|
+
addressManager.removeObservedAddr(oldAddr);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
addressManager.addObservedAddr(newAddr);
|
|
585
|
+
addressManager.confirmObservedAddr(newAddr);
|
|
586
|
+
// Store discovered IP
|
|
587
|
+
this.discoveredP2pIp = ip;
|
|
588
|
+
this.logger.info('Public IP discovered via discv5', { ip });
|
|
589
|
+
};
|
|
590
|
+
this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
|
|
591
|
+
}
|
|
592
|
+
|
|
561
593
|
this.discoveryRunningPromise = new RunningPromise(
|
|
562
594
|
async () => {
|
|
563
595
|
await this.peerManager.heartbeat();
|
|
@@ -570,7 +602,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
570
602
|
this.logger.info(`Started P2P service`, {
|
|
571
603
|
listen: this.config.listenAddress,
|
|
572
604
|
port: this.config.p2pPort,
|
|
573
|
-
announce: announceTcpMultiaddr,
|
|
605
|
+
announce: announceTcpMultiaddr ?? 'pending (queryForIp=true)',
|
|
574
606
|
peerId: this.node.peerId.toString(),
|
|
575
607
|
});
|
|
576
608
|
}
|
|
@@ -583,6 +615,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
583
615
|
// Remove gossip sub listener
|
|
584
616
|
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
585
617
|
|
|
618
|
+
// Remove ip:changed listener if registered
|
|
619
|
+
if (this.ipChangedHandler) {
|
|
620
|
+
this.peerDiscoveryService.off('ip:changed', this.ipChangedHandler);
|
|
621
|
+
this.ipChangedHandler = undefined;
|
|
622
|
+
}
|
|
623
|
+
|
|
586
624
|
// Stop peer manager
|
|
587
625
|
this.logger.debug('Stopping peer manager...');
|
|
588
626
|
await this.peerManager.stop();
|
|
@@ -613,6 +651,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
613
651
|
return this.peerManager.getPeers(includePending);
|
|
614
652
|
}
|
|
615
653
|
|
|
654
|
+
public getGossipMeshPeerCount(topicType: TopicType): number {
|
|
655
|
+
return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
|
|
656
|
+
}
|
|
657
|
+
|
|
616
658
|
private handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
|
|
617
659
|
this.logger.trace(`Received PUBSUB message.`);
|
|
618
660
|
|
|
@@ -665,6 +707,25 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
665
707
|
this.checkpointReceivedCallback = callback;
|
|
666
708
|
}
|
|
667
709
|
|
|
710
|
+
/**
|
|
711
|
+
* Registers a callback to be invoked when a duplicate proposal is detected.
|
|
712
|
+
* This callback is triggered on the first duplicate (when count goes from 1 to 2).
|
|
713
|
+
*/
|
|
714
|
+
public registerDuplicateProposalCallback(
|
|
715
|
+
callback: (info: { slot: SlotNumber; proposer: EthAddress; type: 'checkpoint' | 'block' }) => void,
|
|
716
|
+
): void {
|
|
717
|
+
this.duplicateProposalCallback = callback;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Registers a callback to be invoked when a duplicate attestation is detected.
|
|
722
|
+
* A validator signing attestations for different proposals at the same slot.
|
|
723
|
+
* This callback is triggered on the first duplicate (when count goes from 1 to 2).
|
|
724
|
+
*/
|
|
725
|
+
public registerDuplicateAttestationCallback(callback: P2PDuplicateAttestationCallback): void {
|
|
726
|
+
this.duplicateAttestationCallback = callback;
|
|
727
|
+
}
|
|
728
|
+
|
|
668
729
|
/**
|
|
669
730
|
* Subscribes to a topic.
|
|
670
731
|
* @param topic - The topic to subscribe to.
|
|
@@ -729,6 +790,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
729
790
|
if (!validator || !validator.addMessage(msgId)) {
|
|
730
791
|
this.instrumentation.incMessagePrevalidationStatus(false, topicType);
|
|
731
792
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
|
|
793
|
+
if (topicType === TopicType.tx) {
|
|
794
|
+
this.logger.verbose(`Ignoring already-seen tx gossip message`, { msgId, source: source.toString() });
|
|
795
|
+
}
|
|
732
796
|
return { result: false, topicType };
|
|
733
797
|
}
|
|
734
798
|
|
|
@@ -793,9 +857,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
793
857
|
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
794
858
|
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
795
859
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
|
|
796
|
-
|
|
797
|
-
await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
798
|
-
}
|
|
860
|
+
await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
799
861
|
} else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
800
862
|
await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
|
|
801
863
|
} else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
|
|
@@ -851,51 +913,134 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
851
913
|
return;
|
|
852
914
|
}
|
|
853
915
|
|
|
854
|
-
protected async validateReceivedMessage<T>(
|
|
855
|
-
validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
|
|
916
|
+
protected async validateReceivedMessage<T, M = undefined>(
|
|
917
|
+
validationFunc: () => Promise<ReceivedMessageValidationResult<T, M>>,
|
|
856
918
|
msgId: string,
|
|
857
919
|
source: PeerId,
|
|
858
920
|
topicType: TopicType,
|
|
859
|
-
): Promise<ReceivedMessageValidationResult<T>> {
|
|
860
|
-
|
|
921
|
+
): Promise<ReceivedMessageValidationResult<T, M>> {
|
|
922
|
+
// Default to reject result with a penalty if validation function throws an error
|
|
923
|
+
let resultAndObj: ReceivedMessageValidationResult<T, M> = {
|
|
924
|
+
result: TopicValidatorResult.Reject,
|
|
925
|
+
severity: PeerErrorSeverity.MidToleranceError,
|
|
926
|
+
};
|
|
861
927
|
const timer = new Timer();
|
|
862
928
|
try {
|
|
863
929
|
resultAndObj = await validationFunc();
|
|
864
930
|
} catch (err) {
|
|
865
|
-
this.
|
|
866
|
-
this.logger.error(`Error deserializing and validating gossipsub message`, err, {
|
|
867
|
-
msgId,
|
|
868
|
-
source: source.toString(),
|
|
869
|
-
topicType,
|
|
870
|
-
});
|
|
931
|
+
this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
|
|
871
932
|
}
|
|
872
933
|
|
|
873
934
|
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
935
|
+
this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
|
|
874
936
|
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
937
|
+
} else if (resultAndObj.result === TopicValidatorResult.Reject) {
|
|
938
|
+
this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
|
|
939
|
+
msgId,
|
|
940
|
+
source: source.toString(),
|
|
941
|
+
topicType,
|
|
942
|
+
severity: resultAndObj.severity,
|
|
943
|
+
});
|
|
944
|
+
this.peerManager.penalizePeer(source, resultAndObj.severity);
|
|
945
|
+
} else {
|
|
946
|
+
this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
|
|
875
947
|
}
|
|
876
948
|
|
|
877
949
|
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
878
950
|
return resultAndObj;
|
|
879
951
|
}
|
|
880
952
|
|
|
953
|
+
private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
|
|
954
|
+
try {
|
|
955
|
+
return deserializeFunc();
|
|
956
|
+
} catch (err) {
|
|
957
|
+
this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
|
|
958
|
+
err,
|
|
959
|
+
msgId,
|
|
960
|
+
source: source.toString(),
|
|
961
|
+
});
|
|
962
|
+
return undefined;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
881
966
|
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
882
967
|
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
883
|
-
const tx = Tx.fromBuffer(payloadData);
|
|
884
|
-
|
|
885
|
-
|
|
968
|
+
const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
|
|
969
|
+
if (!tx) {
|
|
970
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
974
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
975
|
+
|
|
976
|
+
// Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
|
|
977
|
+
const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
978
|
+
const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
|
|
979
|
+
if (!firstStageOutcome.allPassed) {
|
|
980
|
+
const { name } = firstStageOutcome.failure;
|
|
981
|
+
let { severity } = firstStageOutcome.failure;
|
|
982
|
+
|
|
983
|
+
// Double spend validator has a special case handler. We perform more detailed examination
|
|
984
|
+
// as to how recently the nullifier was entered into the tree and if the transaction should
|
|
985
|
+
// have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
|
|
986
|
+
if (name === 'doubleSpendValidator') {
|
|
987
|
+
const txBlockNumber = BlockNumber(currentBlockNumber + 1);
|
|
988
|
+
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 1 validation failed`, {
|
|
992
|
+
validator: name,
|
|
993
|
+
severity,
|
|
994
|
+
source: source.toString(),
|
|
995
|
+
});
|
|
996
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
|
|
1000
|
+
const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
|
|
1001
|
+
if (canAdd === 'ignored') {
|
|
1002
|
+
this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
|
|
1003
|
+
source: source.toString(),
|
|
1004
|
+
});
|
|
1005
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// Stage 2: expensive proof verification
|
|
1009
|
+
const secondStageValidators = this.createSecondStageMessageValidators();
|
|
1010
|
+
const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
|
|
1011
|
+
if (!secondStageOutcome.allPassed) {
|
|
1012
|
+
const { severity, name } = secondStageOutcome.failure;
|
|
1013
|
+
this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
|
|
1014
|
+
validator: name,
|
|
1015
|
+
severity,
|
|
1016
|
+
source: source.toString(),
|
|
1017
|
+
});
|
|
1018
|
+
return { result: TopicValidatorResult.Reject, severity };
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// Pool add: persist the tx
|
|
1022
|
+
const txHash = tx.getTxHash();
|
|
1023
|
+
const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
|
|
1024
|
+
|
|
1025
|
+
const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
|
|
1026
|
+
const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
|
|
886
1027
|
|
|
887
|
-
this.logger.
|
|
888
|
-
|
|
889
|
-
|
|
1028
|
+
this.logger.verbose(`Validate propagated tx ${txHash.toString()}`, {
|
|
1029
|
+
wasAccepted,
|
|
1030
|
+
wasIgnored,
|
|
890
1031
|
[Attributes.P2P_ID]: source.toString(),
|
|
891
1032
|
});
|
|
892
1033
|
|
|
893
|
-
if (
|
|
894
|
-
return { result: TopicValidatorResult.
|
|
895
|
-
} else if (
|
|
1034
|
+
if (wasAccepted) {
|
|
1035
|
+
return { result: TopicValidatorResult.Accept, obj: tx };
|
|
1036
|
+
} else if (wasIgnored) {
|
|
896
1037
|
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
897
1038
|
} else {
|
|
898
|
-
|
|
1039
|
+
this.logger.warn(`Gossiped tx ${txHash.toString()} unexpectedly rejected by pool`, {
|
|
1040
|
+
source: source.toString(),
|
|
1041
|
+
txHash: txHash.toString(),
|
|
1042
|
+
});
|
|
1043
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
899
1044
|
}
|
|
900
1045
|
};
|
|
901
1046
|
|
|
@@ -904,6 +1049,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
904
1049
|
return;
|
|
905
1050
|
}
|
|
906
1051
|
|
|
1052
|
+
// Tx was accepted into pool and will be propagated - just log and record metrics
|
|
907
1053
|
const txHash = tx.getTxHash();
|
|
908
1054
|
const txHashString = txHash.toString();
|
|
909
1055
|
this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
|
|
@@ -911,13 +1057,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
911
1057
|
txHash: txHashString,
|
|
912
1058
|
});
|
|
913
1059
|
|
|
914
|
-
if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
|
|
915
|
-
this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
1060
|
this.instrumentation.incrementTxReceived(1);
|
|
920
|
-
await this.mempools.txPool.addTxs([tx]);
|
|
921
1061
|
}
|
|
922
1062
|
|
|
923
1063
|
/**
|
|
@@ -929,47 +1069,17 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
929
1069
|
msgId: string,
|
|
930
1070
|
source: PeerId,
|
|
931
1071
|
): Promise<void> {
|
|
932
|
-
const validationFunc: () => Promise<ReceivedMessageValidationResult<CheckpointAttestation>> = async () => {
|
|
933
|
-
const attestation = CheckpointAttestation.fromBuffer(payloadData);
|
|
934
|
-
const pool = this.mempools.attestationPool;
|
|
935
|
-
const validationResult = await this.validateCheckpointAttestation(source, attestation);
|
|
936
|
-
const isValid = validationResult.result === 'accept';
|
|
937
|
-
const exists = isValid && (await pool.hasCheckpointAttestation(attestation));
|
|
938
|
-
|
|
939
|
-
let canAdd = true;
|
|
940
|
-
if (isValid && !exists) {
|
|
941
|
-
const slot = attestation.payload.header.slotNumber;
|
|
942
|
-
const { committee } = await this.epochCache.getCommittee(slot);
|
|
943
|
-
const committeeSize = committee?.length ?? 0;
|
|
944
|
-
canAdd = await pool.canAddCheckpointAttestation(attestation, committeeSize);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
this.logger.trace(`Validate propagated checkpoint attestation`, {
|
|
948
|
-
isValid,
|
|
949
|
-
exists,
|
|
950
|
-
canAdd,
|
|
951
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
952
|
-
[Attributes.P2P_ID]: source.toString(),
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
if (validationResult.result === 'reject') {
|
|
956
|
-
return { result: TopicValidatorResult.Reject };
|
|
957
|
-
} else if (validationResult.result === 'ignore' || exists) {
|
|
958
|
-
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
959
|
-
} else if (!canAdd) {
|
|
960
|
-
this.logger.warn(`Dropping checkpoint attestation due to per-(slot, proposalId) attestation cap`, {
|
|
961
|
-
slot: attestation.payload.header.slotNumber.toString(),
|
|
962
|
-
archive: attestation.archive.toString(),
|
|
963
|
-
source: source.toString(),
|
|
964
|
-
});
|
|
965
|
-
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
966
|
-
} else {
|
|
967
|
-
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
968
|
-
}
|
|
969
|
-
};
|
|
970
|
-
|
|
971
1072
|
const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
|
|
972
|
-
|
|
1073
|
+
() => {
|
|
1074
|
+
const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
|
|
1075
|
+
if (!attestation) {
|
|
1076
|
+
return Promise.resolve({
|
|
1077
|
+
result: TopicValidatorResult.Reject,
|
|
1078
|
+
severity: PeerErrorSeverity.LowToleranceError,
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
return this.validateAndStoreCheckpointAttestation(source, attestation);
|
|
1082
|
+
},
|
|
973
1083
|
msgId,
|
|
974
1084
|
source,
|
|
975
1085
|
TopicType.checkpoint_attestation,
|
|
@@ -979,8 +1089,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
979
1089
|
return;
|
|
980
1090
|
}
|
|
981
1091
|
|
|
982
|
-
this.logger.
|
|
983
|
-
`Received checkpoint attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
|
|
1092
|
+
this.logger.verbose(
|
|
1093
|
+
`Received valid checkpoint attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
|
|
984
1094
|
{
|
|
985
1095
|
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
986
1096
|
slot: attestation.slotNumber,
|
|
@@ -988,60 +1098,168 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
988
1098
|
source: source.toString(),
|
|
989
1099
|
},
|
|
990
1100
|
);
|
|
991
|
-
|
|
992
|
-
await this.mempools.attestationPool.addCheckpointAttestations([attestation]);
|
|
993
1101
|
}
|
|
994
1102
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1103
|
+
/** Validates a checkpoint attestation and adds it to the pool. Penalizes the peer if validation fails. */
|
|
1104
|
+
@trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation) => ({
|
|
1105
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
1106
|
+
}))
|
|
1107
|
+
protected async validateAndStoreCheckpointAttestation(
|
|
1108
|
+
peerId: PeerId,
|
|
1109
|
+
attestation: CheckpointAttestation,
|
|
1110
|
+
): Promise<ReceivedMessageValidationResult<CheckpointAttestation>> {
|
|
1111
|
+
const validationResult = await this.checkpointAttestationValidator.validate(attestation);
|
|
1112
|
+
|
|
1113
|
+
if (validationResult.result === 'reject') {
|
|
1114
|
+
this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1115
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if (validationResult.result === 'ignore') {
|
|
1119
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Try to add the attestation: this handles existence check, cap check, and adding in one call
|
|
1123
|
+
// count is the number of attestations by this signer for this slot (for duplicate detection)
|
|
1124
|
+
const slot = attestation.payload.header.slotNumber;
|
|
1125
|
+
const { added, alreadyExists, count } =
|
|
1126
|
+
await this.mempools.attestationPool.tryAddCheckpointAttestation(attestation);
|
|
1127
|
+
|
|
1128
|
+
this.logger.trace(`Validate propagated checkpoint attestation`, {
|
|
1129
|
+
added,
|
|
1130
|
+
alreadyExists,
|
|
1131
|
+
count,
|
|
1132
|
+
[Attributes.SLOT_NUMBER]: slot.toString(),
|
|
1133
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
// Exact same attestation received, no need to re-broadcast
|
|
1137
|
+
if (alreadyExists) {
|
|
1138
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// Could not add (cap reached for signer), penalize and do not re-broadcast
|
|
1142
|
+
if (!added) {
|
|
1143
|
+
this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
|
|
1144
|
+
slot: slot.toString(),
|
|
1145
|
+
archive: attestation.archive.toString(),
|
|
1146
|
+
source: peerId.toString(),
|
|
1147
|
+
attester: attestation.getSender()?.toString(),
|
|
1148
|
+
count,
|
|
1011
1149
|
});
|
|
1150
|
+
return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
|
|
1151
|
+
}
|
|
1012
1152
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
this.
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1153
|
+
// Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
|
|
1154
|
+
// count is the number of attestations by this signer for this slot
|
|
1155
|
+
if (count === 2) {
|
|
1156
|
+
const attester = attestation.getSender();
|
|
1157
|
+
if (attester) {
|
|
1158
|
+
this.logger.warn(`Detected duplicate attestation (equivocation) at slot ${slot}`, {
|
|
1159
|
+
slot: slot.toString(),
|
|
1160
|
+
archive: attestation.archive.toString(),
|
|
1161
|
+
source: peerId.toString(),
|
|
1162
|
+
attester: attester.toString(),
|
|
1023
1163
|
});
|
|
1024
|
-
|
|
1025
|
-
} else {
|
|
1026
|
-
return { result: TopicValidatorResult.Accept, obj: block };
|
|
1164
|
+
this.duplicateAttestationCallback?.({ slot, attester });
|
|
1027
1165
|
}
|
|
1028
|
-
}
|
|
1166
|
+
}
|
|
1029
1167
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1168
|
+
// Attestation was added successfully - accept it so other nodes can also detect the equivocation
|
|
1169
|
+
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
protected async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
1173
|
+
const {
|
|
1174
|
+
result,
|
|
1175
|
+
obj: block,
|
|
1176
|
+
metadata: { isEquivocated } = {},
|
|
1177
|
+
} = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
|
|
1178
|
+
() => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
|
|
1032
1179
|
msgId,
|
|
1033
1180
|
source,
|
|
1034
1181
|
TopicType.block_proposal,
|
|
1035
1182
|
);
|
|
1036
1183
|
|
|
1037
|
-
|
|
1184
|
+
// If not accepted or equivocated, return
|
|
1185
|
+
if (result !== TopicValidatorResult.Accept || !block || isEquivocated) {
|
|
1038
1186
|
return;
|
|
1039
1187
|
}
|
|
1040
1188
|
|
|
1041
1189
|
await this.processValidBlockProposal(block, source);
|
|
1042
1190
|
}
|
|
1043
1191
|
|
|
1044
|
-
|
|
1192
|
+
/** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
|
|
1193
|
+
@trackSpan('Libp2pService.validateAndStoreBlockProposal', (_peerId, block) => ({
|
|
1194
|
+
[Attributes.BLOCK_NUMBER]: block.blockNumber.toString(),
|
|
1195
|
+
[Attributes.SLOT_NUMBER]: block.slotNumber.toString(),
|
|
1196
|
+
}))
|
|
1197
|
+
protected async validateAndStoreBlockProposal(
|
|
1198
|
+
peerId: PeerId,
|
|
1199
|
+
block: BlockProposal,
|
|
1200
|
+
): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
|
|
1201
|
+
const validationResult = await this.blockProposalValidator.validate(block);
|
|
1202
|
+
|
|
1203
|
+
if (validationResult.result === 'reject') {
|
|
1204
|
+
this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1205
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (validationResult.result === 'ignore') {
|
|
1209
|
+
return { result: TopicValidatorResult.Ignore, obj: block };
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// Try to add the proposal: this handles existence check, cap check, and adding in one call
|
|
1213
|
+
const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
|
|
1214
|
+
const isEquivocated = count !== undefined && count > 1;
|
|
1215
|
+
|
|
1216
|
+
// Duplicate proposal received, no need to re-broadcast
|
|
1217
|
+
if (alreadyExists) {
|
|
1218
|
+
this.logger.debug(`Ignoring duplicate block proposal received`, {
|
|
1219
|
+
...block.toBlockInfo(),
|
|
1220
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1221
|
+
proposer: block.getSender()?.toString(),
|
|
1222
|
+
source: peerId.toString(),
|
|
1223
|
+
});
|
|
1224
|
+
return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// Too many blocks received for this slot and index, penalize peer and do not re-broadcast
|
|
1228
|
+
if (!added) {
|
|
1229
|
+
this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
|
|
1230
|
+
...block.toBlockInfo(),
|
|
1231
|
+
indexWithinCheckpoint: block.indexWithinCheckpoint,
|
|
1232
|
+
count,
|
|
1233
|
+
proposer: block.getSender()?.toString(),
|
|
1234
|
+
source: peerId.toString(),
|
|
1235
|
+
});
|
|
1236
|
+
return {
|
|
1237
|
+
result: TopicValidatorResult.Reject,
|
|
1238
|
+
metadata: { isEquivocated },
|
|
1239
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1244
|
+
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1245
|
+
if (isEquivocated) {
|
|
1246
|
+
const proposer = block.getSender();
|
|
1247
|
+
this.logger.warn(`Detected duplicate block proposal (equivocation) at slot ${block.slotNumber}`, {
|
|
1248
|
+
...block.toBlockInfo(),
|
|
1249
|
+
source: peerId.toString(),
|
|
1250
|
+
proposer: proposer?.toString(),
|
|
1251
|
+
});
|
|
1252
|
+
// Invoke the duplicate callback on the first duplicate spotted only
|
|
1253
|
+
if (proposer && count === 2) {
|
|
1254
|
+
this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
|
|
1255
|
+
}
|
|
1256
|
+
return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// Otherwise, we're good to go!
|
|
1260
|
+
return { result: TopicValidatorResult.Accept, obj: block };
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1045
1263
|
// REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
|
|
1046
1264
|
// should not be here as it does not deal with p2p networking.
|
|
1047
1265
|
@trackSpan('Libp2pService.processValidBlockProposal', async block => ({
|
|
@@ -1049,7 +1267,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1049
1267
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
1050
1268
|
[Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1051
1269
|
}))
|
|
1052
|
-
|
|
1270
|
+
protected async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
|
|
1053
1271
|
const slot = block.slotNumber;
|
|
1054
1272
|
this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
1055
1273
|
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
|
|
@@ -1057,30 +1275,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1057
1275
|
...block.toBlockInfo(),
|
|
1058
1276
|
});
|
|
1059
1277
|
|
|
1060
|
-
//
|
|
1061
|
-
|
|
1062
|
-
await this.mempools.attestationPool.addBlockProposal(block);
|
|
1063
|
-
} catch (err: unknown) {
|
|
1064
|
-
// Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
|
|
1065
|
-
if (err instanceof ProposalSlotCapExceededError) {
|
|
1066
|
-
this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
|
|
1067
|
-
slot: String(slot),
|
|
1068
|
-
archive: block.archive.toString(),
|
|
1069
|
-
error: (err as Error).message,
|
|
1070
|
-
});
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
throw err;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
// Mark the txs in this proposal as non-evictable
|
|
1077
|
-
await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
|
|
1278
|
+
// Mark the txs in this proposal as protected
|
|
1279
|
+
await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
|
|
1078
1280
|
|
|
1079
1281
|
// Call the block received callback to validate the proposal.
|
|
1080
1282
|
// Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
|
|
1081
1283
|
const isValid = await this.blockReceivedCallback(block, sender);
|
|
1082
1284
|
if (!isValid) {
|
|
1083
|
-
this.logger.
|
|
1285
|
+
this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
|
|
1084
1286
|
}
|
|
1085
1287
|
}
|
|
1086
1288
|
|
|
@@ -1088,67 +1290,149 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1088
1290
|
* Handle a gossiped checkpoint proposal.
|
|
1089
1291
|
* Validates and processes the checkpoint proposal, then triggers the callback for attestation.
|
|
1090
1292
|
*/
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
const exists = isValid && (await pool.hasCheckpointProposal(checkpoint));
|
|
1100
|
-
const canAdd = isValid && (await pool.canAddCheckpointProposal(checkpoint));
|
|
1101
|
-
|
|
1102
|
-
this.logger.trace(`Validate propagated checkpoint proposal`, {
|
|
1103
|
-
isValid,
|
|
1104
|
-
exists,
|
|
1105
|
-
canAdd,
|
|
1106
|
-
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1107
|
-
[Attributes.P2P_ID]: source.toString(),
|
|
1108
|
-
});
|
|
1109
|
-
|
|
1110
|
-
if (validationResult.result === 'reject') {
|
|
1111
|
-
return { result: TopicValidatorResult.Reject };
|
|
1112
|
-
} else if (validationResult.result === 'ignore' || exists) {
|
|
1113
|
-
return { result: TopicValidatorResult.Ignore, obj: checkpoint };
|
|
1114
|
-
} else if (!canAdd) {
|
|
1115
|
-
this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
|
|
1116
|
-
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
1117
|
-
slot: checkpoint.slotNumber.toString(),
|
|
1118
|
-
archive: checkpoint.archive.toString(),
|
|
1119
|
-
source: source.toString(),
|
|
1120
|
-
});
|
|
1121
|
-
return { result: TopicValidatorResult.Reject };
|
|
1122
|
-
} else {
|
|
1123
|
-
return { result: TopicValidatorResult.Accept, obj: checkpoint };
|
|
1124
|
-
}
|
|
1125
|
-
};
|
|
1126
|
-
|
|
1127
|
-
const { result, obj: checkpoint } = await this.validateReceivedMessage<CheckpointProposal>(
|
|
1128
|
-
validationFunc,
|
|
1293
|
+
protected async handleGossipedCheckpointProposal(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
1294
|
+
const {
|
|
1295
|
+
result,
|
|
1296
|
+
obj: checkpoint,
|
|
1297
|
+
metadata: { isEquivocated, processBlock } = {},
|
|
1298
|
+
} = await this.validateReceivedMessage<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>(
|
|
1299
|
+
() => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
|
|
1129
1300
|
msgId,
|
|
1130
1301
|
source,
|
|
1131
1302
|
TopicType.checkpoint_proposal,
|
|
1132
1303
|
);
|
|
1133
1304
|
|
|
1134
|
-
|
|
1305
|
+
// If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
|
|
1306
|
+
// TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
|
|
1307
|
+
if (processBlock && checkpoint?.getBlockProposal()) {
|
|
1308
|
+
await this.processValidBlockProposal(checkpoint.getBlockProposal()!, source);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
if (result !== TopicValidatorResult.Accept || !checkpoint || isEquivocated) {
|
|
1135
1312
|
return;
|
|
1136
1313
|
}
|
|
1137
1314
|
|
|
1138
|
-
await this.processValidCheckpointProposal(checkpoint, source);
|
|
1315
|
+
await this.processValidCheckpointProposal(checkpoint.toCore(), source);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Validates a checkpoint proposal. Penalizes peer if validation fails. Adds the checkpoint and
|
|
1320
|
+
* its last block (if present) to the mempool if valid. Triggers equivocation detection on both.
|
|
1321
|
+
*/
|
|
1322
|
+
@trackSpan('Libp2pService.validateAndStoreCheckpointProposal', (_peerId, checkpoint) => ({
|
|
1323
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1324
|
+
}))
|
|
1325
|
+
protected async validateAndStoreCheckpointProposal(
|
|
1326
|
+
peerId: PeerId,
|
|
1327
|
+
checkpoint: CheckpointProposal,
|
|
1328
|
+
): Promise<ReceivedMessageValidationResult<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>> {
|
|
1329
|
+
const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1330
|
+
|
|
1331
|
+
if (validationResult.result === 'reject') {
|
|
1332
|
+
this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
|
|
1333
|
+
return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (validationResult.result === 'ignore') {
|
|
1337
|
+
return { result: TopicValidatorResult.Ignore, obj: checkpoint };
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// Extract and try to add the block proposal first if present
|
|
1341
|
+
const blockProposal = checkpoint.getBlockProposal();
|
|
1342
|
+
let processBlock = false;
|
|
1343
|
+
if (blockProposal) {
|
|
1344
|
+
this.logger.debug(`Validating block proposal from propagated checkpoint`, {
|
|
1345
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1346
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1347
|
+
});
|
|
1348
|
+
const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
|
|
1349
|
+
const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
|
|
1350
|
+
if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
|
|
1351
|
+
this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
|
|
1352
|
+
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1353
|
+
[Attributes.P2P_ID]: peerId.toString(),
|
|
1354
|
+
isEquivocated,
|
|
1355
|
+
result: blockProposalResult.result,
|
|
1356
|
+
});
|
|
1357
|
+
return {
|
|
1358
|
+
result: TopicValidatorResult.Reject,
|
|
1359
|
+
severity:
|
|
1360
|
+
'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
|
|
1361
|
+
};
|
|
1362
|
+
} else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
|
|
1363
|
+
processBlock = true;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Try to add the checkpoint proposal core: this handles existence check, cap check, and adding in one call
|
|
1368
|
+
const checkpointCore = checkpoint.toCore();
|
|
1369
|
+
const tryAddResult = await this.mempools.attestationPool.tryAddCheckpointProposal(checkpointCore);
|
|
1370
|
+
const { added, alreadyExists, count } = tryAddResult;
|
|
1371
|
+
const isEquivocated = count !== undefined && count > 1;
|
|
1372
|
+
|
|
1373
|
+
// Duplicate proposal received, do not re-broadcast
|
|
1374
|
+
if (alreadyExists) {
|
|
1375
|
+
this.logger.debug(`Ignoring duplicate checkpoint proposal received`, {
|
|
1376
|
+
...checkpoint.toCheckpointInfo(),
|
|
1377
|
+
source: peerId.toString(),
|
|
1378
|
+
});
|
|
1379
|
+
return {
|
|
1380
|
+
result: TopicValidatorResult.Ignore,
|
|
1381
|
+
obj: checkpoint,
|
|
1382
|
+
metadata: { isEquivocated, processBlock },
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
|
|
1387
|
+
// Note: We still return the checkpoint obj so the lastBlock can be processed if valid
|
|
1388
|
+
if (!added) {
|
|
1389
|
+
this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
|
|
1390
|
+
...checkpoint.toCheckpointInfo(),
|
|
1391
|
+
count,
|
|
1392
|
+
source: peerId.toString(),
|
|
1393
|
+
});
|
|
1394
|
+
return {
|
|
1395
|
+
result: TopicValidatorResult.Reject,
|
|
1396
|
+
obj: checkpoint,
|
|
1397
|
+
metadata: { isEquivocated, processBlock },
|
|
1398
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
|
|
1403
|
+
// and do re-broadcast it so other nodes in the network know to slash the proposer
|
|
1404
|
+
if (isEquivocated) {
|
|
1405
|
+
const proposer = checkpoint.getSender();
|
|
1406
|
+
this.logger.warn(`Detected duplicate checkpoint proposal (equivocation) at slot ${checkpoint.slotNumber}`, {
|
|
1407
|
+
...checkpoint.toCheckpointInfo(),
|
|
1408
|
+
source: peerId.toString(),
|
|
1409
|
+
proposer: proposer?.toString(),
|
|
1410
|
+
});
|
|
1411
|
+
// Invoke the duplicate callback on the first duplicate spotted only
|
|
1412
|
+
if (proposer && count === 2) {
|
|
1413
|
+
this.duplicateProposalCallback?.({ slot: checkpoint.slotNumber, proposer, type: 'checkpoint' });
|
|
1414
|
+
}
|
|
1415
|
+
return {
|
|
1416
|
+
result: TopicValidatorResult.Accept,
|
|
1417
|
+
obj: checkpoint,
|
|
1418
|
+
metadata: { isEquivocated, processBlock },
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Otherwise, we're good to go!
|
|
1423
|
+
return { result: TopicValidatorResult.Accept, obj: checkpoint, metadata: { processBlock, isEquivocated } };
|
|
1139
1424
|
}
|
|
1140
1425
|
|
|
1141
1426
|
/**
|
|
1142
1427
|
* Process a validated checkpoint proposal.
|
|
1143
|
-
*
|
|
1144
|
-
* The block callback is invoked before the checkpoint callback.
|
|
1428
|
+
* Note: The proposal was already added to the pool by tryAddCheckpointProposal in handleGossipedCheckpointProposal.
|
|
1145
1429
|
*/
|
|
1146
1430
|
@trackSpan('Libp2pService.processValidCheckpointProposal', async checkpoint => ({
|
|
1147
1431
|
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber,
|
|
1148
1432
|
[Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
|
|
1149
1433
|
[Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1150
1434
|
}))
|
|
1151
|
-
|
|
1435
|
+
protected async processValidCheckpointProposal(checkpoint: CheckpointProposalCore, sender: PeerId) {
|
|
1152
1436
|
const slot = checkpoint.slotNumber;
|
|
1153
1437
|
this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
1154
1438
|
p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
|
|
@@ -1157,37 +1441,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1157
1441
|
source: sender.toString(),
|
|
1158
1442
|
});
|
|
1159
1443
|
|
|
1160
|
-
// Extract block proposal before adding to pool (pool stores them separately)
|
|
1161
|
-
const blockProposal = checkpoint.getBlockProposal();
|
|
1162
|
-
|
|
1163
|
-
// Add proposal to the pool (this extracts and stores block proposal separately)
|
|
1164
|
-
await this.mempools.attestationPool.addCheckpointProposal(checkpoint);
|
|
1165
|
-
|
|
1166
|
-
// Mark txs as non-evictable if present (from the last block)
|
|
1167
|
-
if (checkpoint.txHashes.length > 0) {
|
|
1168
|
-
await this.mempools.txPool.markTxsAsNonEvictable(checkpoint.txHashes);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
// If there was a last block proposal, invoke the block callback first for validation.
|
|
1172
|
-
// Note: The block proposal is already stored in the pool by addCheckpointProposal.
|
|
1173
|
-
if (blockProposal) {
|
|
1174
|
-
const isValid = await this.blockReceivedCallback(blockProposal, sender);
|
|
1175
|
-
if (!isValid) {
|
|
1176
|
-
this.logger.warn(`Block proposal from checkpoint failed validation`, {
|
|
1177
|
-
slot: slot.toString(),
|
|
1178
|
-
archive: checkpoint.archive.toString(),
|
|
1179
|
-
blockNumber: blockProposal.blockNumber.toString(),
|
|
1180
|
-
});
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
1444
|
// Call the checkpoint received callback with the core version (without lastBlock)
|
|
1186
1445
|
// to validate and potentially generate attestations
|
|
1187
|
-
const attestations = await this.checkpointReceivedCallback(checkpoint
|
|
1446
|
+
const attestations = await this.checkpointReceivedCallback(checkpoint, sender);
|
|
1188
1447
|
if (attestations && attestations.length > 0) {
|
|
1189
1448
|
// If the callback returned attestations, add them to the pool and propagate them
|
|
1190
|
-
await this.mempools.attestationPool.
|
|
1449
|
+
await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
|
|
1191
1450
|
for (const attestation of attestations) {
|
|
1192
1451
|
await this.propagate(attestation);
|
|
1193
1452
|
}
|
|
@@ -1214,9 +1473,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1214
1473
|
* @returns True if the requested block transactions are valid, false otherwise.
|
|
1215
1474
|
*/
|
|
1216
1475
|
@trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
|
|
1217
|
-
[Attributes.
|
|
1476
|
+
[Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
|
|
1218
1477
|
}))
|
|
1219
|
-
|
|
1478
|
+
protected async validateRequestedBlockTxs(
|
|
1220
1479
|
request: BlockTxsRequest,
|
|
1221
1480
|
response: BlockTxsResponse,
|
|
1222
1481
|
peerId: PeerId,
|
|
@@ -1224,10 +1483,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1224
1483
|
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1225
1484
|
|
|
1226
1485
|
try {
|
|
1227
|
-
if (!response.
|
|
1486
|
+
if (!response.archiveRoot.equals(request.archiveRoot)) {
|
|
1228
1487
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1229
1488
|
throw new ValidationError(
|
|
1230
|
-
`Received block txs for unexpected
|
|
1489
|
+
`Received block txs for unexpected archive root: expected ${request.archiveRoot.toString()}, got ${response.archiveRoot.toString()}`,
|
|
1231
1490
|
);
|
|
1232
1491
|
}
|
|
1233
1492
|
|
|
@@ -1257,7 +1516,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1257
1516
|
}
|
|
1258
1517
|
|
|
1259
1518
|
// Given proposal (should have locally), ensure returned txs are valid subset and match request indices
|
|
1260
|
-
const proposal = await this.mempools.attestationPool.getBlockProposal(request.
|
|
1519
|
+
const proposal = await this.mempools.attestationPool.getBlockProposal(request.archiveRoot.toString());
|
|
1261
1520
|
if (proposal) {
|
|
1262
1521
|
// Build intersected indices
|
|
1263
1522
|
const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
|
|
@@ -1277,7 +1536,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1277
1536
|
} else {
|
|
1278
1537
|
// No local proposal, cannot check the membership/order of the returned txs
|
|
1279
1538
|
this.logger.warn(
|
|
1280
|
-
`Block proposal not found for
|
|
1539
|
+
`Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
|
|
1281
1540
|
);
|
|
1282
1541
|
return false;
|
|
1283
1542
|
}
|
|
@@ -1346,7 +1605,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1346
1605
|
@trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
|
|
1347
1606
|
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
|
|
1348
1607
|
}))
|
|
1349
|
-
|
|
1608
|
+
protected async validateRequestedBlock(
|
|
1350
1609
|
requestedBlockNumber: Fr,
|
|
1351
1610
|
responseBlock: L2Block,
|
|
1352
1611
|
peerId: PeerId,
|
|
@@ -1379,7 +1638,12 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1379
1638
|
}
|
|
1380
1639
|
}
|
|
1381
1640
|
|
|
1382
|
-
|
|
1641
|
+
protected async validateRequestedTx(
|
|
1642
|
+
tx: Tx,
|
|
1643
|
+
peerId: PeerId,
|
|
1644
|
+
txValidator: TxValidator,
|
|
1645
|
+
requested?: Set<`0x${string}`>,
|
|
1646
|
+
) {
|
|
1383
1647
|
const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
|
|
1384
1648
|
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1385
1649
|
penalize(PeerErrorSeverity.MidToleranceError);
|
|
@@ -1393,44 +1657,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1393
1657
|
}
|
|
1394
1658
|
}
|
|
1395
1659
|
|
|
1396
|
-
|
|
1397
|
-
return
|
|
1660
|
+
protected createRequestedTxValidator(): TxValidator {
|
|
1661
|
+
return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
|
|
1398
1662
|
l1ChainId: this.config.l1ChainId,
|
|
1399
1663
|
rollupVersion: this.config.rollupVersion,
|
|
1400
1664
|
});
|
|
1401
1665
|
}
|
|
1402
1666
|
|
|
1403
|
-
@trackSpan('Libp2pService.validatePropagatedTx', tx => ({
|
|
1404
|
-
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
1405
|
-
}))
|
|
1406
|
-
private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
|
|
1407
|
-
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1408
|
-
|
|
1409
|
-
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
1410
|
-
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1411
|
-
const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
1412
|
-
|
|
1413
|
-
for (const validator of messageValidators) {
|
|
1414
|
-
const outcome = await this.runValidations(tx, validator);
|
|
1415
|
-
|
|
1416
|
-
if (outcome.allPassed) {
|
|
1417
|
-
continue;
|
|
1418
|
-
}
|
|
1419
|
-
const { name } = outcome.failure;
|
|
1420
|
-
let { severity } = outcome.failure;
|
|
1421
|
-
|
|
1422
|
-
// Double spend validator has a special case handler
|
|
1423
|
-
if (name === 'doubleSpendValidator') {
|
|
1424
|
-
const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
|
|
1425
|
-
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
this.peerManager.penalizePeer(peerId, severity);
|
|
1429
|
-
return false;
|
|
1430
|
-
}
|
|
1431
|
-
return true;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
1667
|
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
1435
1668
|
if (blockNumber === this.feesCache?.blockNumber) {
|
|
1436
1669
|
return this.feesCache.gasFees;
|
|
@@ -1458,60 +1691,62 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1458
1691
|
};
|
|
1459
1692
|
}
|
|
1460
1693
|
|
|
1461
|
-
public async
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1694
|
+
public async validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
|
|
1695
|
+
const validator = createTxValidatorForBlockProposalReceivedTxs(
|
|
1696
|
+
this.proofVerifier,
|
|
1697
|
+
{ l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
|
|
1698
|
+
this.logger.getBindings(),
|
|
1699
|
+
);
|
|
1467
1700
|
|
|
1468
|
-
await Promise.all(
|
|
1701
|
+
const results = await Promise.all(
|
|
1469
1702
|
txs.map(async tx => {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
if (!outcome.allPassed) {
|
|
1473
|
-
throw new Error('Invalid tx detected', { cause: { outcome } });
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1703
|
+
const result = await validator.validateTx(tx);
|
|
1704
|
+
return result.result !== 'invalid';
|
|
1476
1705
|
}),
|
|
1477
1706
|
);
|
|
1707
|
+
if (results.some(value => value === false)) {
|
|
1708
|
+
throw new Error('Invalid tx detected');
|
|
1709
|
+
}
|
|
1478
1710
|
}
|
|
1479
1711
|
|
|
1480
|
-
/**
|
|
1481
|
-
|
|
1482
|
-
*
|
|
1483
|
-
* Each validator is a pair of a validator and a severity.
|
|
1484
|
-
* If a validator fails, the peer is penalized with the severity of the validator.
|
|
1485
|
-
*
|
|
1486
|
-
* @param currentBlockNumber - The current synced block number.
|
|
1487
|
-
* @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
|
|
1488
|
-
* @returns The message validators.
|
|
1489
|
-
*/
|
|
1490
|
-
private async createMessageValidators(
|
|
1712
|
+
/** Creates the first stage (fast) validators for gossiped transactions. */
|
|
1713
|
+
protected async createFirstStageMessageValidators(
|
|
1491
1714
|
currentBlockNumber: BlockNumber,
|
|
1492
1715
|
nextSlotTimestamp: UInt64,
|
|
1493
|
-
): Promise<Record<string,
|
|
1716
|
+
): Promise<Record<string, TransactionValidator>> {
|
|
1494
1717
|
const gasFees = await this.getGasFees(currentBlockNumber);
|
|
1495
|
-
const allowedInSetup =
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1718
|
+
const allowedInSetup = [
|
|
1719
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
1720
|
+
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
1721
|
+
];
|
|
1722
|
+
const blockNumber = BlockNumber(currentBlockNumber + 1);
|
|
1723
|
+
const l1Constants = await this.archiver.getL1Constants();
|
|
1724
|
+
|
|
1725
|
+
return createFirstStageTxValidationsForGossipedTransactions(
|
|
1500
1726
|
nextSlotTimestamp,
|
|
1501
|
-
|
|
1727
|
+
blockNumber,
|
|
1502
1728
|
this.worldStateSynchronizer,
|
|
1503
1729
|
gasFees,
|
|
1504
1730
|
this.config.l1ChainId,
|
|
1505
1731
|
this.config.rollupVersion,
|
|
1506
1732
|
protocolContractsHash,
|
|
1507
1733
|
this.archiver,
|
|
1508
|
-
this.proofVerifier,
|
|
1509
1734
|
!this.config.disableTransactions,
|
|
1510
1735
|
allowedInSetup,
|
|
1511
1736
|
this.logger.getBindings(),
|
|
1737
|
+
{
|
|
1738
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
1739
|
+
maxBlockL2Gas: this.config.validateMaxL2BlockGas,
|
|
1740
|
+
maxBlockDAGas: this.config.validateMaxDABlockGas,
|
|
1741
|
+
},
|
|
1512
1742
|
);
|
|
1513
1743
|
}
|
|
1514
1744
|
|
|
1745
|
+
/** Creates the second stage (expensive proof verification) validators for gossiped transactions. */
|
|
1746
|
+
protected createSecondStageMessageValidators(): Record<string, TransactionValidator> {
|
|
1747
|
+
return createSecondStageTxValidationsForGossipedTransactions(this.proofVerifier, this.logger.getBindings());
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1515
1750
|
/**
|
|
1516
1751
|
* Run validations on a tx.
|
|
1517
1752
|
* @param tx - The tx to validate.
|
|
@@ -1520,7 +1755,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1520
1755
|
*/
|
|
1521
1756
|
private async runValidations(
|
|
1522
1757
|
tx: Tx,
|
|
1523
|
-
messageValidators: Record<string,
|
|
1758
|
+
messageValidators: Record<string, TransactionValidator>,
|
|
1524
1759
|
): Promise<ValidationOutcome> {
|
|
1525
1760
|
const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
|
|
1526
1761
|
const { result } = await validator.validateTx(tx);
|
|
@@ -1529,8 +1764,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1529
1764
|
|
|
1530
1765
|
// A promise that resolves when all validations have been run
|
|
1531
1766
|
const allValidations = await Promise.all(validationPromises);
|
|
1532
|
-
const
|
|
1533
|
-
if (
|
|
1767
|
+
const failures = allValidations.filter(x => !x.isValid);
|
|
1768
|
+
if (failures.length > 0) {
|
|
1769
|
+
// Pick the most severe failure (lowest tolerance = harshest penalty)
|
|
1770
|
+
const failed = maxBy(failures, f => PeerErrorSeverityByHarshness.indexOf(f.severity))!;
|
|
1534
1771
|
return {
|
|
1535
1772
|
allPassed: false,
|
|
1536
1773
|
failure: {
|
|
@@ -1583,74 +1820,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
|
|
|
1583
1820
|
return PeerErrorSeverity.HighToleranceError;
|
|
1584
1821
|
}
|
|
1585
1822
|
|
|
1586
|
-
/**
|
|
1587
|
-
* Validate a checkpoint attestation.
|
|
1588
|
-
*
|
|
1589
|
-
* @param attestation - The checkpoint attestation to validate.
|
|
1590
|
-
* @returns True if the checkpoint attestation is valid, false otherwise.
|
|
1591
|
-
*/
|
|
1592
|
-
@trackSpan('Libp2pService.validateCheckpointAttestation', async (_, attestation) => ({
|
|
1593
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
1594
|
-
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
1595
|
-
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
1596
|
-
}))
|
|
1597
|
-
public async validateCheckpointAttestation(
|
|
1598
|
-
peerId: PeerId,
|
|
1599
|
-
attestation: CheckpointAttestation,
|
|
1600
|
-
): Promise<P2PValidationResult> {
|
|
1601
|
-
const result = await this.checkpointAttestationValidator.validate(attestation);
|
|
1602
|
-
|
|
1603
|
-
if (result.result === 'reject') {
|
|
1604
|
-
this.logger.debug(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
|
|
1605
|
-
this.peerManager.penalizePeer(peerId, result.severity);
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
return result;
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
/**
|
|
1612
|
-
* Validate a block proposal.
|
|
1613
|
-
*
|
|
1614
|
-
* @param block - The block proposal to validate.
|
|
1615
|
-
* @returns True if the block proposal is valid, false otherwise.
|
|
1616
|
-
*/
|
|
1617
|
-
@trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
|
|
1618
|
-
[Attributes.SLOT_NUMBER]: block.slotNumber.toString(),
|
|
1619
|
-
}))
|
|
1620
|
-
public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<P2PValidationResult> {
|
|
1621
|
-
const result = await this.blockProposalValidator.validate(block);
|
|
1622
|
-
|
|
1623
|
-
if (result.result === 'reject') {
|
|
1624
|
-
this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
1625
|
-
this.peerManager.penalizePeer(peerId, result.severity);
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
return result;
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
/**
|
|
1632
|
-
* Validate a checkpoint proposal.
|
|
1633
|
-
*
|
|
1634
|
-
* @param checkpoint - The checkpoint proposal to validate.
|
|
1635
|
-
* @returns True if the checkpoint proposal is valid, false otherwise.
|
|
1636
|
-
*/
|
|
1637
|
-
@trackSpan('Libp2pService.validateCheckpointProposal', (_peerId, checkpoint) => ({
|
|
1638
|
-
[Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
|
|
1639
|
-
}))
|
|
1640
|
-
public async validateCheckpointProposal(
|
|
1641
|
-
peerId: PeerId,
|
|
1642
|
-
checkpoint: CheckpointProposal,
|
|
1643
|
-
): Promise<P2PValidationResult> {
|
|
1644
|
-
const result = await this.checkpointProposalValidator.validate(checkpoint);
|
|
1645
|
-
|
|
1646
|
-
if (result.result === 'reject') {
|
|
1647
|
-
this.logger.debug(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
|
|
1648
|
-
this.peerManager.penalizePeer(peerId, result.severity);
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
return result;
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
1823
|
public getPeerScore(peerId: PeerId): number {
|
|
1655
1824
|
return this.node.services.pubsub.score.score(peerId.toString());
|
|
1656
1825
|
}
|