@aztec/p2p 0.0.0-test.1 → 0.0.1-commit.03f7ef2
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/dest/bootstrap/bootstrap.d.ts +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +22 -9
- package/dest/client/factory.d.ts +15 -5
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +60 -25
- package/dest/client/index.d.ts +2 -1
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +1 -0
- package/dest/client/interface.d.ts +157 -0
- package/dest/client/interface.d.ts.map +1 -0
- package/dest/client/interface.js +9 -0
- package/dest/client/p2p_client.d.ts +75 -190
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +381 -183
- package/dest/config.d.ts +151 -125
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +183 -34
- package/dest/enr/generate-enr.d.ts +11 -3
- package/dest/enr/generate-enr.d.ts.map +1 -1
- package/dest/enr/generate-enr.js +27 -5
- package/dest/enr/index.d.ts +1 -1
- package/dest/errors/attestation-pool.error.d.ts +7 -0
- package/dest/errors/attestation-pool.error.d.ts.map +1 -0
- package/dest/errors/attestation-pool.error.js +12 -0
- package/dest/errors/reqresp.error.d.ts +1 -1
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/index.d.ts +4 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +68 -8
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +216 -65
- package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +21 -6
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +127 -26
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +19 -6
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +111 -21
- package/dest/mem_pools/attestation_pool/mocks.d.ts +227 -7
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +10 -16
- package/dest/mem_pools/index.d.ts +1 -1
- package/dest/mem_pools/instrumentation.d.ts +16 -12
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +57 -35
- package/dest/mem_pools/interface.d.ts +3 -4
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +64 -14
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +472 -97
- package/dest/mem_pools/tx_pool/index.d.ts +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +36 -11
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +137 -36
- package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
- package/dest/mem_pools/tx_pool/priority.js +1 -1
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +67 -10
- package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
- 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 +273 -42
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -2
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +45 -9
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +20 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +67 -0
- package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
- package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/index.js +1 -0
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +6 -2
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +73 -12
- package/dest/msg_validators/block_proposal_validator/index.d.ts +1 -1
- package/dest/msg_validators/index.d.ts +1 -1
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +10 -0
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -0
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.js +36 -0
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +3 -0
- package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/allowed_public_setup.js +27 -0
- package/dest/msg_validators/tx_validator/archive_cache.d.ts +14 -0
- package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/archive_cache.js +22 -0
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts +2 -2
- package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/block_header_validator.js +4 -4
- 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 +56 -86
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +1 -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 +21 -27
- package/dest/msg_validators/tx_validator/factory.d.ts +16 -0
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/factory.js +74 -0
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +11 -0
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/gas_validator.js +115 -0
- package/dest/msg_validators/tx_validator/index.d.ts +8 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +7 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +9 -5
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +39 -20
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +14 -0
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/phases_validator.js +93 -0
- package/dest/msg_validators/tx_validator/test_utils.d.ts +17 -0
- package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/test_utils.js +22 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +13 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/timestamp_validator.js +32 -0
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +8 -0
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -0
- package/dest/msg_validators/tx_validator/tx_permitted_validator.js +24 -0
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_proof_validator.js +6 -5
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.d.ts +10 -9
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +63 -36
- package/dest/services/dummy_service.d.ts +50 -11
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +88 -5
- package/dest/services/encoding.d.ts +26 -7
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +74 -6
- package/dest/services/gossipsub/scoring.d.ts +1 -1
- package/dest/services/index.d.ts +5 -1
- package/dest/services/index.d.ts.map +1 -1
- package/dest/services/index.js +4 -0
- package/dest/services/libp2p/instrumentation.d.ts +20 -0
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -0
- package/dest/services/libp2p/instrumentation.js +164 -0
- package/dest/services/libp2p/libp2p_service.d.ts +78 -89
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +695 -248
- package/dest/services/peer-manager/interface.d.ts +23 -0
- package/dest/services/peer-manager/interface.d.ts.map +1 -0
- package/dest/services/peer-manager/interface.js +1 -0
- package/dest/services/peer-manager/metrics.d.ts +6 -2
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +22 -2
- package/dest/services/peer-manager/peer_manager.d.ts +102 -22
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +549 -72
- package/dest/services/peer-manager/peer_scoring.d.ts +7 -2
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +40 -2
- package/dest/services/reqresp/config.d.ts +11 -9
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/config.js +18 -4
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +2 -2
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +10 -6
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +31 -17
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.js +142 -84
- package/dest/services/reqresp/index.d.ts +3 -2
- package/dest/services/reqresp/index.d.ts.map +1 -1
- package/dest/services/reqresp/index.js +2 -1
- package/dest/services/reqresp/interface.d.ts +73 -24
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +46 -27
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/auth.d.ts +43 -0
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/auth.js +71 -0
- package/dest/services/reqresp/protocols/block.d.ts +6 -1
- package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block.js +30 -6
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +30 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +75 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +11 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +39 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +47 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +75 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +4 -0
- package/dest/services/reqresp/protocols/block_txs/index.d.ts.map +1 -0
- package/dest/services/reqresp/protocols/block_txs/index.js +3 -0
- package/dest/services/reqresp/protocols/goodbye.d.ts +3 -5
- package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/goodbye.js +7 -7
- package/dest/services/reqresp/protocols/index.d.ts +3 -1
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +2 -0
- package/dest/services/reqresp/protocols/ping.d.ts +1 -3
- package/dest/services/reqresp/protocols/ping.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.d.ts +40 -7
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.js +73 -5
- package/dest/services/reqresp/protocols/tx.d.ts +14 -4
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +34 -6
- package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +6 -4
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -2
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +21 -1
- package/dest/services/reqresp/reqresp.d.ts +24 -66
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +298 -207
- package/dest/services/reqresp/status.d.ts +10 -4
- package/dest/services/reqresp/status.d.ts.map +1 -1
- package/dest/services/reqresp/status.js +9 -2
- package/dest/services/service.d.ts +23 -19
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +25 -0
- package/dest/services/tx_collection/config.d.ts.map +1 -0
- package/dest/services/tx_collection/config.js +58 -0
- package/dest/services/tx_collection/fast_tx_collection.d.ts +51 -0
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/fast_tx_collection.js +300 -0
- package/dest/services/tx_collection/index.d.ts +3 -0
- package/dest/services/tx_collection/index.d.ts.map +1 -0
- package/dest/services/tx_collection/index.js +2 -0
- package/dest/services/tx_collection/instrumentation.d.ts +10 -0
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -0
- package/dest/services/tx_collection/instrumentation.js +34 -0
- package/dest/services/tx_collection/slow_tx_collection.d.ts +53 -0
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/slow_tx_collection.js +177 -0
- package/dest/services/tx_collection/tx_collection.d.ts +110 -0
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection.js +128 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts +30 -0
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_collection_sink.js +111 -0
- package/dest/services/tx_collection/tx_source.d.ts +18 -0
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -0
- package/dest/services/tx_collection/tx_source.js +31 -0
- package/dest/services/tx_provider.d.ts +51 -0
- package/dest/services/tx_provider.d.ts.map +1 -0
- package/dest/services/tx_provider.js +217 -0
- package/dest/services/tx_provider_instrumentation.d.ts +16 -0
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -0
- package/dest/services/tx_provider_instrumentation.js +47 -0
- package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
- package/dest/test-helpers/get-ports.d.ts +1 -1
- package/dest/test-helpers/get-ports.d.ts.map +1 -1
- package/dest/test-helpers/index.d.ts +2 -1
- package/dest/test-helpers/index.d.ts.map +1 -1
- package/dest/test-helpers/index.js +1 -0
- package/dest/test-helpers/make-enrs.d.ts +1 -1
- package/dest/test-helpers/make-enrs.d.ts.map +1 -1
- package/dest/test-helpers/make-enrs.js +4 -5
- package/dest/test-helpers/make-test-p2p-clients.d.ts +33 -5
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +86 -16
- package/dest/test-helpers/mock-pubsub.d.ts +59 -0
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -0
- package/dest/test-helpers/mock-pubsub.js +130 -0
- package/dest/test-helpers/mock-tx-helpers.d.ts +12 -0
- package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -0
- package/dest/test-helpers/mock-tx-helpers.js +19 -0
- package/dest/test-helpers/reqresp-nodes.d.ts +15 -11
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +62 -28
- package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +103 -29
- package/dest/testbench/parse_log_file.d.ts +1 -1
- package/dest/testbench/parse_log_file.js +4 -4
- package/dest/testbench/testbench.d.ts +1 -1
- package/dest/testbench/testbench.js +4 -4
- package/dest/testbench/worker_client_manager.d.ts +1 -6
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +11 -19
- package/dest/types/index.d.ts +4 -2
- package/dest/types/index.d.ts.map +1 -1
- package/dest/types/index.js +2 -0
- package/dest/util.d.ts +24 -16
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +75 -69
- package/dest/versioning.d.ts +4 -4
- package/dest/versioning.d.ts.map +1 -1
- package/dest/versioning.js +8 -3
- package/package.json +32 -27
- package/src/bootstrap/bootstrap.ts +27 -11
- package/src/client/factory.ts +139 -53
- package/src/client/index.ts +1 -0
- package/src/client/interface.ts +198 -0
- package/src/client/p2p_client.ts +484 -348
- package/src/config.ts +305 -134
- package/src/enr/generate-enr.ts +39 -6
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/index.ts +4 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +75 -7
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +266 -67
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +174 -35
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +156 -30
- package/src/mem_pools/attestation_pool/mocks.ts +13 -12
- package/src/mem_pools/instrumentation.ts +70 -40
- package/src/mem_pools/interface.ts +2 -4
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +555 -110
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +160 -46
- package/src/mem_pools/tx_pool/priority.ts +1 -1
- package/src/mem_pools/tx_pool/tx_pool.ts +69 -9
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +224 -35
- package/src/msg_validators/attestation_validator/attestation_validator.ts +54 -11
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +91 -0
- package/src/msg_validators/attestation_validator/index.ts +1 -0
- package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +82 -14
- package/src/msg_validators/msg_seen_validator/msg_seen_validator.ts +36 -0
- package/src/msg_validators/tx_validator/allowed_public_setup.ts +35 -0
- package/src/msg_validators/tx_validator/archive_cache.ts +28 -0
- package/src/msg_validators/tx_validator/block_header_validator.ts +5 -5
- package/src/msg_validators/tx_validator/data_validator.ts +81 -69
- package/src/msg_validators/tx_validator/double_spend_validator.ts +19 -17
- package/src/msg_validators/tx_validator/factory.ts +110 -0
- package/src/msg_validators/tx_validator/gas_validator.ts +134 -0
- package/src/msg_validators/tx_validator/index.ts +7 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +59 -22
- package/src/msg_validators/tx_validator/phases_validator.ts +116 -0
- package/src/msg_validators/tx_validator/test_utils.ts +43 -0
- package/src/msg_validators/tx_validator/timestamp_validator.ts +47 -0
- package/src/msg_validators/tx_validator/tx_permitted_validator.ts +17 -0
- package/src/msg_validators/tx_validator/tx_proof_validator.ts +6 -5
- package/src/services/discv5/discV5_service.ts +84 -38
- package/src/services/dummy_service.ts +147 -9
- package/src/services/encoding.ts +81 -6
- package/src/services/index.ts +4 -0
- package/src/services/libp2p/instrumentation.ts +167 -0
- package/src/services/libp2p/libp2p_service.ts +865 -298
- package/src/services/peer-manager/interface.ts +29 -0
- package/src/services/peer-manager/metrics.ts +26 -1
- package/src/services/peer-manager/peer_manager.ts +654 -78
- package/src/services/peer-manager/peer_scoring.ts +46 -3
- package/src/services/reqresp/config.ts +26 -9
- package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +12 -6
- package/src/services/reqresp/connection-sampler/connection_sampler.ts +148 -95
- package/src/services/reqresp/index.ts +2 -0
- package/src/services/reqresp/interface.ts +92 -37
- package/src/services/reqresp/metrics.ts +4 -1
- package/src/services/reqresp/protocols/auth.ts +83 -0
- package/src/services/reqresp/protocols/block.ts +26 -4
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +90 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +53 -0
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +79 -0
- package/src/services/reqresp/protocols/block_txs/index.ts +3 -0
- package/src/services/reqresp/protocols/goodbye.ts +9 -7
- package/src/services/reqresp/protocols/index.ts +2 -0
- package/src/services/reqresp/protocols/status.ts +118 -5
- package/src/services/reqresp/protocols/tx.ts +36 -8
- package/src/services/reqresp/rate-limiter/rate_limiter.ts +12 -3
- package/src/services/reqresp/rate-limiter/rate_limits.ts +21 -1
- package/src/services/reqresp/reqresp.ts +387 -256
- package/src/services/reqresp/status.ts +12 -3
- package/src/services/service.ts +45 -21
- package/src/services/tx_collection/config.ts +84 -0
- package/src/services/tx_collection/fast_tx_collection.ts +341 -0
- package/src/services/tx_collection/index.ts +2 -0
- package/src/services/tx_collection/instrumentation.ts +43 -0
- package/src/services/tx_collection/slow_tx_collection.ts +233 -0
- package/src/services/tx_collection/tx_collection.ts +216 -0
- package/src/services/tx_collection/tx_collection_sink.ts +129 -0
- package/src/services/tx_collection/tx_source.ts +37 -0
- package/src/services/tx_provider.ts +229 -0
- package/src/services/tx_provider_instrumentation.ts +61 -0
- package/src/test-helpers/index.ts +1 -0
- package/src/test-helpers/make-enrs.ts +4 -5
- package/src/test-helpers/make-test-p2p-clients.ts +111 -21
- package/src/test-helpers/mock-pubsub.ts +188 -0
- package/src/test-helpers/mock-tx-helpers.ts +24 -0
- package/src/test-helpers/reqresp-nodes.ts +87 -36
- package/src/testbench/p2p_client_testbench_worker.ts +151 -25
- package/src/testbench/parse_log_file.ts +4 -4
- package/src/testbench/testbench.ts +4 -4
- package/src/testbench/worker_client_manager.ts +17 -23
- package/src/types/index.ts +2 -0
- package/src/util.ts +105 -91
- package/src/versioning.ts +11 -4
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
|
+
import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
|
|
5
6
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
7
|
+
import { Timer } from '@aztec/foundation/timer';
|
|
6
8
|
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
7
|
-
import
|
|
9
|
+
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
10
|
+
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
11
|
+
import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
|
|
12
|
+
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
13
|
+
import { GasFees } from '@aztec/stdlib/gas';
|
|
8
14
|
import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
9
15
|
import {
|
|
10
16
|
BlockAttestation,
|
|
11
17
|
BlockProposal,
|
|
12
18
|
type Gossipable,
|
|
13
19
|
P2PClientType,
|
|
20
|
+
P2PMessage,
|
|
14
21
|
PeerErrorSeverity,
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
TopicType,
|
|
23
|
+
createTopicString,
|
|
24
|
+
getTopicsForClientAndConfig,
|
|
17
25
|
metricsTopicStrToLabels,
|
|
18
26
|
} from '@aztec/stdlib/p2p';
|
|
19
27
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
20
|
-
import { Tx, type TxHash, type TxValidationResult } from '@aztec/stdlib/tx';
|
|
28
|
+
import { Tx, type TxHash, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
|
|
29
|
+
import type { UInt64 } from '@aztec/stdlib/types';
|
|
30
|
+
import { compressComponentVersions } from '@aztec/stdlib/versioning';
|
|
21
31
|
import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
|
|
22
32
|
|
|
23
|
-
import type { ENR } from '@chainsafe/enr';
|
|
24
33
|
import {
|
|
25
34
|
type GossipSub,
|
|
26
35
|
type GossipSubComponents,
|
|
@@ -33,17 +42,26 @@ import { noise } from '@chainsafe/libp2p-noise';
|
|
|
33
42
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
34
43
|
import { bootstrap } from '@libp2p/bootstrap';
|
|
35
44
|
import { identify } from '@libp2p/identify';
|
|
36
|
-
import { type Message, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
45
|
+
import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
37
46
|
import type { ConnectionManager } from '@libp2p/interface-internal';
|
|
38
|
-
import '@libp2p/kad-dht';
|
|
39
47
|
import { mplex } from '@libp2p/mplex';
|
|
40
48
|
import { tcp } from '@libp2p/tcp';
|
|
49
|
+
import { ENR } from '@nethermindeth/enr';
|
|
41
50
|
import { createLibp2p } from 'libp2p';
|
|
42
51
|
|
|
43
52
|
import type { P2PConfig } from '../../config.js';
|
|
53
|
+
import { ProposalSlotCapExceededError } from '../../errors/attestation-pool.error.js';
|
|
44
54
|
import type { MemPools } from '../../mem_pools/interface.js';
|
|
45
|
-
import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
|
|
46
55
|
import {
|
|
56
|
+
AttestationValidator,
|
|
57
|
+
BlockProposalValidator,
|
|
58
|
+
FishermanAttestationValidator,
|
|
59
|
+
} from '../../msg_validators/index.js';
|
|
60
|
+
import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
|
|
61
|
+
import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
|
|
62
|
+
import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
|
|
63
|
+
import {
|
|
64
|
+
AggregateTxValidator,
|
|
47
65
|
DataTxValidator,
|
|
48
66
|
DoubleSpendTxValidator,
|
|
49
67
|
MetadataTxValidator,
|
|
@@ -51,23 +69,40 @@ import {
|
|
|
51
69
|
} from '../../msg_validators/tx_validator/index.js';
|
|
52
70
|
import { GossipSubEvent } from '../../types/index.js';
|
|
53
71
|
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
72
|
+
import { getVersions } from '../../versioning.js';
|
|
54
73
|
import { AztecDatastore } from '../data_store.js';
|
|
74
|
+
import { DiscV5Service } from '../discv5/discV5_service.js';
|
|
55
75
|
import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
56
76
|
import { gossipScoreThresholds } from '../gossipsub/scoring.js';
|
|
77
|
+
import type { PeerManagerInterface } from '../peer-manager/interface.js';
|
|
57
78
|
import { PeerManager } from '../peer-manager/peer_manager.js';
|
|
58
79
|
import { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
59
|
-
import {
|
|
80
|
+
import type { P2PReqRespConfig } from '../reqresp/config.js';
|
|
81
|
+
import {
|
|
82
|
+
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
83
|
+
type ReqRespInterface,
|
|
84
|
+
ReqRespSubProtocol,
|
|
85
|
+
type ReqRespSubProtocolHandler,
|
|
86
|
+
type ReqRespSubProtocolHandlers,
|
|
87
|
+
type ReqRespSubProtocolValidators,
|
|
88
|
+
type SubProtocolMap,
|
|
89
|
+
ValidationError,
|
|
90
|
+
} from '../reqresp/interface.js';
|
|
91
|
+
import { reqRespBlockTxsHandler } from '../reqresp/protocols/block_txs/block_txs_handler.js';
|
|
60
92
|
import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
|
|
61
|
-
import {
|
|
93
|
+
import {
|
|
94
|
+
AuthRequest,
|
|
95
|
+
BlockTxsRequest,
|
|
96
|
+
BlockTxsResponse,
|
|
97
|
+
StatusMessage,
|
|
98
|
+
pingHandler,
|
|
99
|
+
reqRespBlockHandler,
|
|
100
|
+
reqRespStatusHandler,
|
|
101
|
+
reqRespTxHandler,
|
|
102
|
+
} from '../reqresp/protocols/index.js';
|
|
62
103
|
import { ReqResp } from '../reqresp/reqresp.js';
|
|
63
|
-
import type { P2PService, PeerDiscoveryService } from '../service.js';
|
|
64
|
-
|
|
65
|
-
interface MessageValidator {
|
|
66
|
-
validator: {
|
|
67
|
-
validateTx(tx: Tx): Promise<TxValidationResult>;
|
|
68
|
-
};
|
|
69
|
-
severity: PeerErrorSeverity;
|
|
70
|
-
}
|
|
104
|
+
import type { P2PBlockReceivedCallback, P2PService, PeerDiscoveryService } from '../service.js';
|
|
105
|
+
import { P2PInstrumentation } from './instrumentation.js';
|
|
71
106
|
|
|
72
107
|
interface ValidationResult {
|
|
73
108
|
name: string;
|
|
@@ -77,74 +112,98 @@ interface ValidationResult {
|
|
|
77
112
|
|
|
78
113
|
type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
|
|
79
114
|
|
|
115
|
+
// REFACTOR: Unify with the type above
|
|
116
|
+
type ReceivedMessageValidationResult<T> =
|
|
117
|
+
| { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject> }
|
|
118
|
+
| { obj?: undefined; result: TopicValidatorResult.Reject };
|
|
119
|
+
|
|
80
120
|
/**
|
|
81
121
|
* Lib P2P implementation of the P2PService interface.
|
|
82
122
|
*/
|
|
83
|
-
export class LibP2PService<T extends P2PClientType> extends WithTracer implements P2PService {
|
|
84
|
-
private jobQueue: SerialQueue = new SerialQueue();
|
|
85
|
-
private peerManager: PeerManager;
|
|
123
|
+
export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
|
|
86
124
|
private discoveryRunningPromise?: RunningPromise;
|
|
125
|
+
private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
|
|
87
126
|
|
|
88
127
|
// Message validators
|
|
89
128
|
private attestationValidator: AttestationValidator;
|
|
90
129
|
private blockProposalValidator: BlockProposalValidator;
|
|
91
130
|
|
|
92
|
-
|
|
93
|
-
|
|
131
|
+
private protocolVersion = '';
|
|
132
|
+
private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
|
|
133
|
+
|
|
134
|
+
private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
|
|
94
135
|
|
|
95
136
|
/**
|
|
96
137
|
* Callback for when a block is received from a peer.
|
|
97
138
|
* @param block - The block received from the peer.
|
|
98
139
|
* @returns The attestation for the block, if any.
|
|
99
140
|
*/
|
|
100
|
-
private blockReceivedCallback:
|
|
141
|
+
private blockReceivedCallback: P2PBlockReceivedCallback;
|
|
142
|
+
|
|
143
|
+
private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
|
|
144
|
+
|
|
145
|
+
private instrumentation: P2PInstrumentation;
|
|
146
|
+
|
|
147
|
+
protected logger: Logger;
|
|
101
148
|
|
|
102
149
|
constructor(
|
|
103
150
|
private clientType: T,
|
|
104
151
|
private config: P2PConfig,
|
|
105
|
-
|
|
152
|
+
protected node: PubSubLibp2p,
|
|
106
153
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
107
|
-
private
|
|
108
|
-
private
|
|
109
|
-
|
|
154
|
+
private reqresp: ReqRespInterface,
|
|
155
|
+
private peerManager: PeerManagerInterface,
|
|
156
|
+
protected mempools: MemPools,
|
|
157
|
+
private archiver: L2BlockSource & ContractDataSource,
|
|
158
|
+
private epochCache: EpochCacheInterface,
|
|
110
159
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
111
160
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
112
161
|
telemetry: TelemetryClient,
|
|
113
|
-
|
|
162
|
+
logger: Logger = createLogger('p2p:libp2p_service'),
|
|
114
163
|
) {
|
|
115
164
|
super(telemetry, 'LibP2PService');
|
|
116
165
|
|
|
117
|
-
|
|
118
|
-
this.
|
|
166
|
+
// Create child logger with fisherman prefix if in fisherman mode
|
|
167
|
+
this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
|
|
119
168
|
|
|
120
|
-
this.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
169
|
+
this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
|
|
170
|
+
|
|
171
|
+
this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
172
|
+
this.msgIdSeenValidators[TopicType.block_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
173
|
+
this.msgIdSeenValidators[TopicType.block_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
|
|
174
|
+
|
|
175
|
+
const versions = getVersions(config);
|
|
176
|
+
this.protocolVersion = compressComponentVersions(versions);
|
|
177
|
+
logger.info(`Started libp2p service with protocol version ${this.protocolVersion}`);
|
|
178
|
+
|
|
179
|
+
this.topicStrings[TopicType.tx] = createTopicString(TopicType.tx, this.protocolVersion);
|
|
180
|
+
this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
|
|
181
|
+
this.topicStrings[TopicType.block_attestation] = createTopicString(
|
|
182
|
+
TopicType.block_attestation,
|
|
183
|
+
this.protocolVersion,
|
|
128
184
|
);
|
|
129
185
|
|
|
130
|
-
//
|
|
131
|
-
this.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.
|
|
186
|
+
// Use FishermanAttestationValidator in fisherman mode to validate attestation payloads against proposals
|
|
187
|
+
this.attestationValidator = config.fishermanMode
|
|
188
|
+
? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry)
|
|
189
|
+
: new AttestationValidator(epochCache);
|
|
190
|
+
this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
|
|
135
191
|
|
|
136
|
-
this.
|
|
137
|
-
this.blockProposalValidator = new BlockProposalValidator(epochCache);
|
|
192
|
+
this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
|
|
138
193
|
|
|
139
|
-
this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation | undefined> => {
|
|
194
|
+
this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
|
|
140
195
|
this.logger.debug(
|
|
141
|
-
`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber
|
|
142
|
-
{ p2pMessageIdentifier: await block.
|
|
196
|
+
`Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber} from peer.`,
|
|
197
|
+
{ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
|
|
143
198
|
);
|
|
144
199
|
return undefined;
|
|
145
200
|
};
|
|
146
201
|
}
|
|
147
202
|
|
|
203
|
+
public updateConfig(config: Partial<P2PReqRespConfig>) {
|
|
204
|
+
this.reqresp.updateConfig(config);
|
|
205
|
+
}
|
|
206
|
+
|
|
148
207
|
/**
|
|
149
208
|
* Creates an instance of the LibP2P service.
|
|
150
209
|
* @param config - The configuration to use when creating the service.
|
|
@@ -154,69 +213,151 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
154
213
|
public static async new<T extends P2PClientType>(
|
|
155
214
|
clientType: T,
|
|
156
215
|
config: P2PConfig,
|
|
157
|
-
peerDiscoveryService: PeerDiscoveryService,
|
|
158
216
|
peerId: PeerId,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
217
|
+
deps: {
|
|
218
|
+
mempools: MemPools;
|
|
219
|
+
l2BlockSource: L2BlockSource & ContractDataSource;
|
|
220
|
+
epochCache: EpochCacheInterface;
|
|
221
|
+
proofVerifier: ClientProtocolCircuitVerifier;
|
|
222
|
+
worldStateSynchronizer: WorldStateSynchronizer;
|
|
223
|
+
peerStore: AztecAsyncKVStore;
|
|
224
|
+
telemetry: TelemetryClient;
|
|
225
|
+
logger: Logger;
|
|
226
|
+
packageVersion: string;
|
|
227
|
+
},
|
|
167
228
|
) {
|
|
168
|
-
const {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
229
|
+
const {
|
|
230
|
+
worldStateSynchronizer,
|
|
231
|
+
epochCache,
|
|
232
|
+
l2BlockSource,
|
|
233
|
+
mempools,
|
|
234
|
+
proofVerifier,
|
|
235
|
+
peerStore,
|
|
236
|
+
telemetry,
|
|
237
|
+
logger,
|
|
238
|
+
packageVersion,
|
|
239
|
+
} = deps;
|
|
240
|
+
const { p2pPort, maxPeerCount, listenAddress } = config;
|
|
241
|
+
const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
|
|
172
242
|
|
|
173
|
-
const datastore = new AztecDatastore(
|
|
243
|
+
const datastore = new AztecDatastore(peerStore);
|
|
174
244
|
|
|
175
245
|
const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
|
|
176
246
|
|
|
177
|
-
|
|
247
|
+
const peerDiscoveryService = new DiscV5Service(
|
|
248
|
+
peerId,
|
|
249
|
+
config,
|
|
250
|
+
packageVersion,
|
|
251
|
+
telemetry,
|
|
252
|
+
createLogger(`${logger.module}:discv5_service`),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Seed libp2p's bootstrap discovery with private and trusted peers
|
|
256
|
+
const bootstrapNodes = [...config.privatePeers, ...config.trustedPeers];
|
|
257
|
+
|
|
178
258
|
const peerDiscovery = [];
|
|
179
|
-
if (
|
|
180
|
-
peerDiscovery.push(bootstrap({ list:
|
|
259
|
+
if (bootstrapNodes.length > 0) {
|
|
260
|
+
peerDiscovery.push(bootstrap({ list: bootstrapNodes }));
|
|
181
261
|
}
|
|
182
262
|
|
|
263
|
+
const versions = getVersions(config);
|
|
264
|
+
const protocolVersion = compressComponentVersions(versions);
|
|
265
|
+
|
|
266
|
+
const txTopic = createTopicString(TopicType.tx, protocolVersion);
|
|
267
|
+
const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion);
|
|
268
|
+
const blockAttestationTopic = createTopicString(TopicType.block_attestation, protocolVersion);
|
|
269
|
+
|
|
270
|
+
const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
|
|
271
|
+
const directPeers = (
|
|
272
|
+
await Promise.all(
|
|
273
|
+
preferredPeersEnrs.map(async enr => {
|
|
274
|
+
const peerId = await enr.peerId();
|
|
275
|
+
const address = enr.getLocationMultiaddr('tcp');
|
|
276
|
+
if (address === undefined) {
|
|
277
|
+
throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
id: peerId,
|
|
281
|
+
addrs: [address],
|
|
282
|
+
};
|
|
283
|
+
}),
|
|
284
|
+
)
|
|
285
|
+
).filter(peer => peer !== undefined);
|
|
286
|
+
|
|
287
|
+
const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
|
|
288
|
+
|
|
183
289
|
const node = await createLibp2p({
|
|
184
290
|
start: false,
|
|
185
291
|
peerId,
|
|
186
292
|
addresses: {
|
|
187
293
|
listen: [bindAddrTcp],
|
|
188
|
-
announce:
|
|
294
|
+
announce: announceTcpMultiaddr,
|
|
189
295
|
},
|
|
190
296
|
transports: [
|
|
191
297
|
tcp({
|
|
192
|
-
|
|
298
|
+
// It's better to have this number a bit higher than our maxPeerCount because it's sets the limit on transport (TCP) layer
|
|
299
|
+
// The connection attempts to the node on TCP layer are not necessarily valid Aztec peers so we want to have a bit of leeway here
|
|
300
|
+
// If we hit the limit, the connection will be temporarily accepted and immediately dropped.
|
|
301
|
+
// Docs: https://nodejs.org/api/net.html#servermaxconnections
|
|
302
|
+
maxConnections: maxPeerCount * 2,
|
|
193
303
|
// socket option: the maximum length of the queue of pending connections
|
|
194
|
-
// https://nodejs.org/dist/latest-
|
|
304
|
+
// https://nodejs.org/dist/latest-v22.x/docs/api/net.html#serverlisten
|
|
195
305
|
// it's not safe if we increase this number
|
|
196
306
|
backlog: 5,
|
|
197
307
|
closeServerOnMaxConnections: {
|
|
198
|
-
|
|
199
|
-
|
|
308
|
+
// The property `maxConnections` will protect us against the most DDOS attack
|
|
309
|
+
// This property protects us in case of burst of new connections where server is not able to close them quickly enough
|
|
310
|
+
// In case closeAbove is reached, the server stops listening altogether
|
|
311
|
+
// It's important that there is enough difference between closeAbove and listenAbove,
|
|
312
|
+
// otherwise the server.listener will flap between being closed and open potentially degrading perf even more
|
|
313
|
+
closeAbove: maxPeerCount * 3,
|
|
314
|
+
listenBelow: Math.floor(maxPeerCount * 0.9),
|
|
200
315
|
},
|
|
201
316
|
}),
|
|
202
317
|
],
|
|
203
318
|
datastore,
|
|
204
319
|
peerDiscovery,
|
|
205
|
-
streamMuxers: [
|
|
320
|
+
streamMuxers: [yamux(), mplex()],
|
|
206
321
|
connectionEncryption: [noise()],
|
|
207
322
|
connectionManager: {
|
|
208
|
-
minConnections: 0,
|
|
209
|
-
|
|
323
|
+
minConnections: 0, // Disable libp2p peer dialing, we do it manually
|
|
324
|
+
// We set maxConnections above maxPeerCount because if we hit limit of maxPeerCount
|
|
325
|
+
// libp2p will start aggressively rejecting all new connections, preventing network discovery and crawling.
|
|
326
|
+
maxConnections: maxPeerCount * 2,
|
|
210
327
|
maxParallelDials: 100,
|
|
211
328
|
dialTimeout: 30_000,
|
|
212
329
|
maxPeerAddrsToDial: 5,
|
|
213
330
|
maxIncomingPendingConnections: 5,
|
|
214
331
|
},
|
|
332
|
+
connectionGater: {
|
|
333
|
+
denyInboundConnection: (maConn: MultiaddrConnection) => {
|
|
334
|
+
const allowed = peerManager.isNodeAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
|
|
335
|
+
if (allowed) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
logger.debug(`Connection gater: Denying inbound connection from ${maConn.remoteAddr.toString()}`);
|
|
340
|
+
return true;
|
|
341
|
+
},
|
|
342
|
+
denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
|
|
343
|
+
//NOTE: it is not necessary to check address here because this was already done by
|
|
344
|
+
// denyInboundConnection
|
|
345
|
+
const allowed = peerManager.isNodeAllowedToConnect(peerId);
|
|
346
|
+
if (allowed) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
logger.debug(`Connection gater: Denying inbound encrypted connection from ${peerId.toString()}`);
|
|
351
|
+
return true;
|
|
352
|
+
},
|
|
353
|
+
},
|
|
215
354
|
services: {
|
|
216
355
|
identify: identify({
|
|
217
356
|
protocolPrefix: 'aztec',
|
|
357
|
+
runOnConnectionOpen: true,
|
|
218
358
|
}),
|
|
219
359
|
pubsub: gossipsub({
|
|
360
|
+
directPeers,
|
|
220
361
|
debugName: 'gossipsub',
|
|
221
362
|
globalSignaturePolicy: SignaturePolicy.StrictNoSign,
|
|
222
363
|
allowPublishToZeroTopicPeers: true,
|
|
@@ -228,29 +369,30 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
228
369
|
heartbeatInterval: config.gossipsubInterval,
|
|
229
370
|
mcacheLength: config.gossipsubMcacheLength,
|
|
230
371
|
mcacheGossip: config.gossipsubMcacheGossip,
|
|
372
|
+
seenTTL: config.gossipsubSeenTTL,
|
|
231
373
|
msgIdFn: getMsgIdFn,
|
|
232
374
|
msgIdToStrFn: msgIdToStrFn,
|
|
233
375
|
fastMsgIdFn: fastMsgIdFn,
|
|
234
376
|
dataTransform: new SnappyTransform(),
|
|
235
377
|
metricsRegister: otelMetricsAdapter,
|
|
236
|
-
metricsTopicStrToLabel: metricsTopicStrToLabels(),
|
|
378
|
+
metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
|
|
237
379
|
asyncValidation: true,
|
|
238
380
|
scoreThresholds: gossipScoreThresholds,
|
|
239
381
|
scoreParams: createPeerScoreParams({
|
|
240
382
|
// IPColocation factor can be disabled for local testing - default to -5
|
|
241
383
|
IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
|
|
242
384
|
topics: {
|
|
243
|
-
[
|
|
385
|
+
[txTopic]: createTopicScoreParams({
|
|
244
386
|
topicWeight: 1,
|
|
245
387
|
invalidMessageDeliveriesWeight: -20,
|
|
246
388
|
invalidMessageDeliveriesDecay: 0.5,
|
|
247
389
|
}),
|
|
248
|
-
[
|
|
390
|
+
[blockAttestationTopic]: createTopicScoreParams({
|
|
249
391
|
topicWeight: 1,
|
|
250
392
|
invalidMessageDeliveriesWeight: -20,
|
|
251
393
|
invalidMessageDeliveriesDecay: 0.5,
|
|
252
394
|
}),
|
|
253
|
-
[
|
|
395
|
+
[blockProposalTopic]: createTopicScoreParams({
|
|
254
396
|
topicWeight: 1,
|
|
255
397
|
invalidMessageDeliveriesWeight: -20,
|
|
256
398
|
invalidMessageDeliveriesDecay: 0.5,
|
|
@@ -265,11 +407,34 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
265
407
|
logger: createLibp2pComponentLogger(logger.module),
|
|
266
408
|
});
|
|
267
409
|
|
|
410
|
+
const peerScoring = new PeerScoring(config, telemetry);
|
|
411
|
+
const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
|
|
412
|
+
|
|
413
|
+
const peerManager = new PeerManager(
|
|
414
|
+
node,
|
|
415
|
+
peerDiscoveryService,
|
|
416
|
+
config,
|
|
417
|
+
telemetry,
|
|
418
|
+
createLogger(`${logger.module}:peer_manager`),
|
|
419
|
+
peerScoring,
|
|
420
|
+
reqresp,
|
|
421
|
+
worldStateSynchronizer,
|
|
422
|
+
protocolVersion,
|
|
423
|
+
epochCache,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
// Update gossipsub score params
|
|
427
|
+
node.services.pubsub.score.params.appSpecificWeight = 10;
|
|
428
|
+
node.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
|
|
429
|
+
peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
|
|
430
|
+
|
|
268
431
|
return new LibP2PService(
|
|
269
432
|
clientType,
|
|
270
433
|
config,
|
|
271
434
|
node,
|
|
272
435
|
peerDiscoveryService,
|
|
436
|
+
reqresp,
|
|
437
|
+
peerManager,
|
|
273
438
|
mempools,
|
|
274
439
|
l2BlockSource,
|
|
275
440
|
epochCache,
|
|
@@ -291,41 +456,53 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
291
456
|
}
|
|
292
457
|
|
|
293
458
|
// Get listen & announce addresses for logging
|
|
294
|
-
const {
|
|
295
|
-
if (!
|
|
459
|
+
const { p2pIp, p2pPort } = this.config;
|
|
460
|
+
if (!p2pIp) {
|
|
296
461
|
throw new Error('Announce address not provided.');
|
|
297
462
|
}
|
|
298
|
-
const announceTcpMultiaddr = convertToMultiaddr(
|
|
463
|
+
const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
|
|
299
464
|
|
|
300
|
-
|
|
301
|
-
this.
|
|
302
|
-
|
|
465
|
+
await this.peerManager.initializePeers();
|
|
466
|
+
if (!this.config.p2pDiscoveryDisabled) {
|
|
467
|
+
await this.peerDiscoveryService.start();
|
|
468
|
+
}
|
|
303
469
|
await this.node.start();
|
|
304
470
|
|
|
305
471
|
// Subscribe to standard GossipSub topics by default
|
|
306
|
-
for (const topic of
|
|
307
|
-
this.subscribeToTopic(
|
|
472
|
+
for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
|
|
473
|
+
this.subscribeToTopic(this.topicStrings[topic]);
|
|
308
474
|
}
|
|
309
475
|
|
|
310
476
|
// Create request response protocol handlers
|
|
311
477
|
const txHandler = reqRespTxHandler(this.mempools);
|
|
312
478
|
const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
|
|
313
|
-
const blockHandler = reqRespBlockHandler(this.
|
|
479
|
+
const blockHandler = reqRespBlockHandler(this.archiver);
|
|
480
|
+
const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
|
|
314
481
|
|
|
315
|
-
const requestResponseHandlers = {
|
|
482
|
+
const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
|
|
316
483
|
[ReqRespSubProtocol.PING]: pingHandler,
|
|
317
|
-
[ReqRespSubProtocol.STATUS]: statusHandler,
|
|
318
|
-
[ReqRespSubProtocol.TX]: txHandler.bind(this),
|
|
484
|
+
[ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
|
|
319
485
|
[ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
|
|
320
486
|
[ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
|
|
321
487
|
};
|
|
322
488
|
|
|
489
|
+
if (!this.config.disableTransactions) {
|
|
490
|
+
const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.mempools.txPool);
|
|
491
|
+
requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (!this.config.disableTransactions) {
|
|
495
|
+
requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
|
|
496
|
+
}
|
|
497
|
+
|
|
323
498
|
// add GossipSub listener
|
|
324
|
-
this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.
|
|
499
|
+
this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
325
500
|
|
|
326
|
-
// Start running promise for peer discovery
|
|
501
|
+
// Start running promise for peer discovery and metrics collection
|
|
327
502
|
this.discoveryRunningPromise = new RunningPromise(
|
|
328
|
-
() =>
|
|
503
|
+
async () => {
|
|
504
|
+
await this.peerManager.heartbeat();
|
|
505
|
+
},
|
|
329
506
|
this.logger,
|
|
330
507
|
this.config.peerCheckIntervalMS,
|
|
331
508
|
);
|
|
@@ -334,12 +511,14 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
334
511
|
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
335
512
|
const reqrespSubProtocolValidators = {
|
|
336
513
|
...DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
337
|
-
|
|
338
|
-
[ReqRespSubProtocol.
|
|
514
|
+
[ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
|
|
515
|
+
[ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
|
|
516
|
+
[ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
|
|
339
517
|
};
|
|
340
518
|
await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
|
|
341
519
|
this.logger.info(`Started P2P service`, {
|
|
342
|
-
listen:
|
|
520
|
+
listen: this.config.listenAddress,
|
|
521
|
+
port: this.config.p2pPort,
|
|
343
522
|
announce: announceTcpMultiaddr,
|
|
344
523
|
peerId: this.node.peerId.toString(),
|
|
345
524
|
});
|
|
@@ -351,14 +530,11 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
351
530
|
*/
|
|
352
531
|
public async stop() {
|
|
353
532
|
// Remove gossip sub listener
|
|
354
|
-
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.
|
|
533
|
+
this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
|
|
355
534
|
|
|
356
535
|
// Stop peer manager
|
|
357
536
|
this.logger.debug('Stopping peer manager...');
|
|
358
537
|
await this.peerManager.stop();
|
|
359
|
-
|
|
360
|
-
this.logger.debug('Stopping job queue...');
|
|
361
|
-
await this.jobQueue.end();
|
|
362
538
|
this.logger.debug('Stopping running promise...');
|
|
363
539
|
await this.discoveryRunningPromise?.stop();
|
|
364
540
|
this.logger.debug('Stopping peer discovery service...');
|
|
@@ -370,6 +546,18 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
370
546
|
this.logger.info('LibP2P service stopped');
|
|
371
547
|
}
|
|
372
548
|
|
|
549
|
+
addReqRespSubProtocol(
|
|
550
|
+
subProtocol: ReqRespSubProtocol,
|
|
551
|
+
handler: ReqRespSubProtocolHandler,
|
|
552
|
+
validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
|
|
553
|
+
): Promise<void> {
|
|
554
|
+
return this.reqresp.addSubProtocol(subProtocol, handler, validator);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
public registerThisValidatorAddresses(address: EthAddress[]): void {
|
|
558
|
+
this.peerManager.registerThisValidatorAddresses(address);
|
|
559
|
+
}
|
|
560
|
+
|
|
373
561
|
public getPeers(includePending?: boolean): PeerInfo[] {
|
|
374
562
|
return this.peerManager.getPeers(includePending);
|
|
375
563
|
}
|
|
@@ -387,23 +575,6 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
387
575
|
setImmediate(() => void safeJob());
|
|
388
576
|
}
|
|
389
577
|
|
|
390
|
-
/**
|
|
391
|
-
* Send Request via the ReqResp service
|
|
392
|
-
* The subprotocol defined will determine the request and response types
|
|
393
|
-
*
|
|
394
|
-
* See the subProtocolMap for the mapping of subprotocols to request/response types in `interface.ts`
|
|
395
|
-
*
|
|
396
|
-
* @param protocol The request response protocol to use
|
|
397
|
-
* @param request The request type to send
|
|
398
|
-
* @returns
|
|
399
|
-
*/
|
|
400
|
-
sendRequest<SubProtocol extends ReqRespSubProtocol>(
|
|
401
|
-
protocol: SubProtocol,
|
|
402
|
-
request: InstanceType<SubProtocolMap[SubProtocol]['request']>,
|
|
403
|
-
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']> | undefined> {
|
|
404
|
-
return this.reqresp.sendRequest(protocol, request);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
578
|
/**
|
|
408
579
|
* Send a batch of requests to peers, and return the responses
|
|
409
580
|
* @param protocol - The request response protocol to use
|
|
@@ -413,8 +584,9 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
413
584
|
sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
|
|
414
585
|
protocol: SubProtocol,
|
|
415
586
|
requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
|
|
416
|
-
|
|
417
|
-
|
|
587
|
+
pinnedPeerId: PeerId | undefined,
|
|
588
|
+
): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
|
|
589
|
+
return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
|
|
418
590
|
}
|
|
419
591
|
|
|
420
592
|
/**
|
|
@@ -425,9 +597,8 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
425
597
|
return this.peerDiscoveryService.getEnr();
|
|
426
598
|
}
|
|
427
599
|
|
|
428
|
-
public registerBlockReceivedCallback(callback:
|
|
600
|
+
public registerBlockReceivedCallback(callback: P2PBlockReceivedCallback) {
|
|
429
601
|
this.blockReceivedCallback = callback;
|
|
430
|
-
this.logger.verbose('Block received callback registered');
|
|
431
602
|
}
|
|
432
603
|
|
|
433
604
|
/**
|
|
@@ -444,160 +615,354 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
444
615
|
/**
|
|
445
616
|
* Publishes data to a topic.
|
|
446
617
|
* @param topic - The topic to publish to.
|
|
447
|
-
* @param data - The
|
|
618
|
+
* @param data - The message to publish.
|
|
448
619
|
* @returns The number of recipients the data was sent to.
|
|
449
620
|
*/
|
|
450
|
-
private async publishToTopic(topic: string,
|
|
621
|
+
private async publishToTopic(topic: string, message: Gossipable) {
|
|
451
622
|
if (!this.node.services.pubsub) {
|
|
452
623
|
throw new Error('Pubsub service not available.');
|
|
453
624
|
}
|
|
454
|
-
const
|
|
455
|
-
|
|
625
|
+
const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages);
|
|
626
|
+
const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
|
|
456
627
|
return result.recipients.length;
|
|
457
628
|
}
|
|
458
629
|
|
|
630
|
+
/**
|
|
631
|
+
* Checks if this message has already been seen, based on its msgId computed from hashing the message data.
|
|
632
|
+
* Note that we do not rely on the seenCache from gossipsub since we want to keep a longer history of seen
|
|
633
|
+
* messages to avoid tx echoes across the network.
|
|
634
|
+
*/
|
|
635
|
+
protected preValidateReceivedMessage(
|
|
636
|
+
msg: Message,
|
|
637
|
+
msgId: string,
|
|
638
|
+
source: PeerId,
|
|
639
|
+
): { result: boolean; topicType?: TopicType } {
|
|
640
|
+
let topicType: TopicType | undefined;
|
|
641
|
+
|
|
642
|
+
switch (msg.topic) {
|
|
643
|
+
case this.topicStrings[TopicType.tx]:
|
|
644
|
+
topicType = TopicType.tx;
|
|
645
|
+
break;
|
|
646
|
+
case this.topicStrings[TopicType.block_attestation]:
|
|
647
|
+
topicType = TopicType.block_attestation;
|
|
648
|
+
break;
|
|
649
|
+
case this.topicStrings[TopicType.block_proposal]:
|
|
650
|
+
topicType = TopicType.block_proposal;
|
|
651
|
+
break;
|
|
652
|
+
default:
|
|
653
|
+
this.logger.error(`Received message on unknown topic: ${msg.topic}`);
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const validator = topicType ? this.msgIdSeenValidators[topicType] : undefined;
|
|
658
|
+
|
|
659
|
+
if (!validator || !validator.addMessage(msgId)) {
|
|
660
|
+
this.instrumentation.incMessagePrevalidationStatus(false, topicType);
|
|
661
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
|
|
662
|
+
return { result: false, topicType };
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
this.instrumentation.incMessagePrevalidationStatus(true, topicType);
|
|
666
|
+
|
|
667
|
+
return { result: true, topicType };
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Safely deserializes a P2PMessage from raw message data.
|
|
672
|
+
* @param msgId - The message ID.
|
|
673
|
+
* @param source - The peer ID of the message source.
|
|
674
|
+
* @param data - The raw message data.
|
|
675
|
+
* @returns The deserialized P2PMessage or undefined if deserialization fails.
|
|
676
|
+
*/
|
|
677
|
+
private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
|
|
678
|
+
try {
|
|
679
|
+
return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
|
|
680
|
+
} catch (err) {
|
|
681
|
+
this.logger.error(`Error deserializing P2PMessage`, err, {
|
|
682
|
+
msgId,
|
|
683
|
+
source: source.toString(),
|
|
684
|
+
});
|
|
685
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
|
|
686
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
|
|
687
|
+
return undefined;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
459
691
|
/**
|
|
460
692
|
* Handles a new gossip message that was received by the client.
|
|
461
693
|
* @param topic - The message's topic.
|
|
462
694
|
* @param data - The message data
|
|
463
695
|
*/
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
696
|
+
protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
|
|
697
|
+
const msgReceivedTime = Date.now();
|
|
698
|
+
let topicType: TopicType | undefined;
|
|
699
|
+
const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
|
|
700
|
+
if (!p2pMessage) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
|
|
705
|
+
|
|
706
|
+
if (!preValidationResult.result) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (msg.topic === this.topicStrings[TopicType.tx]) {
|
|
711
|
+
topicType = TopicType.tx;
|
|
712
|
+
await this.handleGossipedTx(p2pMessage.payload, msgId, source);
|
|
467
713
|
}
|
|
468
|
-
if (msg.topic ===
|
|
469
|
-
|
|
714
|
+
if (msg.topic === this.topicStrings[TopicType.block_attestation]) {
|
|
715
|
+
topicType = TopicType.block_attestation;
|
|
716
|
+
if (this.clientType === P2PClientType.Full) {
|
|
717
|
+
await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
|
|
721
|
+
topicType = TopicType.block_proposal;
|
|
722
|
+
await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
|
|
470
723
|
}
|
|
471
|
-
|
|
472
|
-
|
|
724
|
+
|
|
725
|
+
if (p2pMessage.timestamp !== undefined && topicType !== undefined) {
|
|
726
|
+
const latency = msgReceivedTime - p2pMessage.timestamp.getTime();
|
|
727
|
+
this.instrumentation.recordMessageLatency(topicType, latency);
|
|
473
728
|
}
|
|
474
729
|
|
|
475
730
|
return;
|
|
476
731
|
}
|
|
477
732
|
|
|
478
|
-
|
|
479
|
-
validationFunc: () => Promise<
|
|
733
|
+
protected async validateReceivedMessage<T>(
|
|
734
|
+
validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
|
|
480
735
|
msgId: string,
|
|
481
736
|
source: PeerId,
|
|
482
|
-
|
|
483
|
-
|
|
737
|
+
topicType: TopicType,
|
|
738
|
+
): Promise<ReceivedMessageValidationResult<T>> {
|
|
739
|
+
let resultAndObj: ReceivedMessageValidationResult<T> = { result: TopicValidatorResult.Reject };
|
|
740
|
+
const timer = new Timer();
|
|
484
741
|
try {
|
|
485
742
|
resultAndObj = await validationFunc();
|
|
486
743
|
} catch (err) {
|
|
487
|
-
this.
|
|
744
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
|
|
745
|
+
this.logger.error(`Error deserializing and validating gossipsub message`, err, {
|
|
746
|
+
msgId,
|
|
747
|
+
source: source.toString(),
|
|
748
|
+
topicType,
|
|
749
|
+
});
|
|
488
750
|
}
|
|
489
751
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
);
|
|
752
|
+
if (resultAndObj.result === TopicValidatorResult.Accept) {
|
|
753
|
+
this.instrumentation.recordMessageValidation(topicType, timer);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
|
|
495
757
|
return resultAndObj;
|
|
496
758
|
}
|
|
497
759
|
|
|
498
|
-
|
|
499
|
-
const validationFunc = async () => {
|
|
500
|
-
const tx = Tx.fromBuffer(
|
|
501
|
-
const
|
|
502
|
-
|
|
760
|
+
protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
|
|
761
|
+
const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
|
|
762
|
+
const tx = Tx.fromBuffer(payloadData);
|
|
763
|
+
const isValid = await this.validatePropagatedTx(tx, source);
|
|
764
|
+
const exists = isValid && (await this.mempools.txPool.hasTx(tx.getTxHash()));
|
|
765
|
+
|
|
766
|
+
this.logger.trace(`Validate propagated tx`, {
|
|
767
|
+
isValid,
|
|
768
|
+
exists,
|
|
769
|
+
[Attributes.P2P_ID]: source.toString(),
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
if (!isValid) {
|
|
773
|
+
return { result: TopicValidatorResult.Reject };
|
|
774
|
+
} else if (exists) {
|
|
775
|
+
return { result: TopicValidatorResult.Ignore, obj: tx };
|
|
776
|
+
} else {
|
|
777
|
+
return { result: TopicValidatorResult.Accept, obj: tx };
|
|
778
|
+
}
|
|
503
779
|
};
|
|
504
780
|
|
|
505
|
-
const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source);
|
|
506
|
-
if (
|
|
781
|
+
const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source, TopicType.tx);
|
|
782
|
+
if (result !== TopicValidatorResult.Accept || !tx) {
|
|
507
783
|
return;
|
|
508
784
|
}
|
|
509
|
-
|
|
785
|
+
|
|
786
|
+
const txHash = tx.getTxHash();
|
|
510
787
|
const txHashString = txHash.toString();
|
|
511
|
-
this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()}
|
|
788
|
+
this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
|
|
789
|
+
source: source.toString(),
|
|
790
|
+
txHash: txHashString,
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
|
|
794
|
+
this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
this.instrumentation.incrementTxReceived(1);
|
|
512
799
|
await this.mempools.txPool.addTxs([tx]);
|
|
513
800
|
}
|
|
514
801
|
|
|
515
|
-
/**
|
|
802
|
+
/**
|
|
803
|
+
* Process Attestation From Peer
|
|
516
804
|
* When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
|
|
517
805
|
*
|
|
518
806
|
* @param attestation - The attestation to process.
|
|
519
807
|
*/
|
|
520
|
-
private async processAttestationFromPeer(
|
|
521
|
-
const validationFunc = async () => {
|
|
522
|
-
const attestation = BlockAttestation.fromBuffer(
|
|
523
|
-
const
|
|
524
|
-
this.
|
|
525
|
-
|
|
808
|
+
private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
809
|
+
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
|
|
810
|
+
const attestation = BlockAttestation.fromBuffer(payloadData);
|
|
811
|
+
const pool = this.mempools.attestationPool;
|
|
812
|
+
const isValid = await this.validateAttestation(source, attestation);
|
|
813
|
+
const exists = isValid && (await pool.hasAttestation(attestation));
|
|
814
|
+
|
|
815
|
+
let canAdd = true;
|
|
816
|
+
if (isValid && !exists) {
|
|
817
|
+
const slot = attestation.payload.header.slotNumber;
|
|
818
|
+
const { committee } = await this.epochCache.getCommittee(slot);
|
|
819
|
+
const committeeSize = committee?.length ?? 0;
|
|
820
|
+
canAdd = await pool.canAddAttestation(attestation, committeeSize);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
this.logger.trace(`Validate propagated block attestation`, {
|
|
824
|
+
isValid,
|
|
825
|
+
exists,
|
|
826
|
+
canAdd,
|
|
827
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
|
|
526
828
|
[Attributes.P2P_ID]: source.toString(),
|
|
527
829
|
});
|
|
528
|
-
|
|
830
|
+
|
|
831
|
+
if (!isValid) {
|
|
832
|
+
return { result: TopicValidatorResult.Reject };
|
|
833
|
+
} else if (exists) {
|
|
834
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
835
|
+
} else if (!canAdd) {
|
|
836
|
+
this.logger.warn(`Dropping block attestation due to per-(slot, proposalId) attestation cap`, {
|
|
837
|
+
slot: attestation.payload.header.slotNumber.toString(),
|
|
838
|
+
archive: attestation.archive.toString(),
|
|
839
|
+
source: source.toString(),
|
|
840
|
+
});
|
|
841
|
+
return { result: TopicValidatorResult.Ignore, obj: attestation };
|
|
842
|
+
} else {
|
|
843
|
+
return { result: TopicValidatorResult.Accept, obj: attestation };
|
|
844
|
+
}
|
|
529
845
|
};
|
|
530
846
|
|
|
531
847
|
const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
|
|
532
848
|
validationFunc,
|
|
533
849
|
msgId,
|
|
534
850
|
source,
|
|
851
|
+
TopicType.block_attestation,
|
|
535
852
|
);
|
|
536
|
-
|
|
853
|
+
|
|
854
|
+
if (result !== TopicValidatorResult.Accept || !attestation) {
|
|
537
855
|
return;
|
|
538
856
|
}
|
|
857
|
+
|
|
539
858
|
this.logger.debug(
|
|
540
|
-
`Received attestation for
|
|
859
|
+
`Received attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
|
|
541
860
|
{
|
|
542
|
-
p2pMessageIdentifier: await attestation.
|
|
543
|
-
slot: attestation.slotNumber
|
|
861
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
862
|
+
slot: attestation.slotNumber,
|
|
544
863
|
archive: attestation.archive.toString(),
|
|
545
|
-
|
|
864
|
+
source: source.toString(),
|
|
546
865
|
},
|
|
547
866
|
);
|
|
548
|
-
|
|
867
|
+
|
|
868
|
+
await this.mempools.attestationPool.addAttestations([attestation]);
|
|
549
869
|
}
|
|
550
870
|
|
|
551
|
-
private async processBlockFromPeer(
|
|
552
|
-
const validationFunc = async () => {
|
|
553
|
-
const block = BlockProposal.fromBuffer(
|
|
554
|
-
const
|
|
555
|
-
this.
|
|
556
|
-
|
|
871
|
+
private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
|
|
872
|
+
const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
|
|
873
|
+
const block = BlockProposal.fromBuffer(payloadData);
|
|
874
|
+
const isValid = await this.validateBlockProposal(source, block);
|
|
875
|
+
const pool = this.mempools.attestationPool;
|
|
876
|
+
|
|
877
|
+
const exists = isValid && (await pool.hasBlockProposal(block));
|
|
878
|
+
const canAdd = isValid && (await pool.canAddProposal(block));
|
|
879
|
+
|
|
880
|
+
this.logger.trace(`Validate propagated block proposal`, {
|
|
881
|
+
isValid,
|
|
882
|
+
exists,
|
|
883
|
+
canAdd,
|
|
884
|
+
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
557
885
|
[Attributes.P2P_ID]: source.toString(),
|
|
558
886
|
});
|
|
559
|
-
|
|
887
|
+
|
|
888
|
+
if (!isValid) {
|
|
889
|
+
return { result: TopicValidatorResult.Reject };
|
|
890
|
+
} else if (exists) {
|
|
891
|
+
return { result: TopicValidatorResult.Ignore, obj: block };
|
|
892
|
+
} else if (!canAdd) {
|
|
893
|
+
this.peerManager.penalizePeer(source, PeerErrorSeverity.MidToleranceError);
|
|
894
|
+
this.logger.warn(`Penalizing peer for block proposal exceeding per-slot cap`, {
|
|
895
|
+
slot: block.slotNumber.toString(),
|
|
896
|
+
archive: block.archive.toString(),
|
|
897
|
+
source: source.toString(),
|
|
898
|
+
});
|
|
899
|
+
return { result: TopicValidatorResult.Reject };
|
|
900
|
+
} else {
|
|
901
|
+
return { result: TopicValidatorResult.Accept, obj: block };
|
|
902
|
+
}
|
|
560
903
|
};
|
|
561
904
|
|
|
562
|
-
const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
|
|
905
|
+
const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
|
|
906
|
+
validationFunc,
|
|
907
|
+
msgId,
|
|
908
|
+
source,
|
|
909
|
+
TopicType.block_proposal,
|
|
910
|
+
);
|
|
911
|
+
|
|
563
912
|
if (!result || !block) {
|
|
564
913
|
return;
|
|
565
914
|
}
|
|
566
|
-
|
|
915
|
+
|
|
916
|
+
await this.processValidBlockProposal(block, source);
|
|
567
917
|
}
|
|
568
918
|
|
|
569
919
|
// REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
|
|
570
920
|
@trackSpan('Libp2pService.processValidBlockProposal', async block => ({
|
|
571
|
-
[Attributes.
|
|
572
|
-
[Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
|
|
921
|
+
[Attributes.SLOT_NUMBER]: block.slotNumber,
|
|
573
922
|
[Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
|
|
574
|
-
[Attributes.P2P_ID]: await block.
|
|
923
|
+
[Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
575
924
|
}))
|
|
576
|
-
private async processValidBlockProposal(block: BlockProposal) {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
);
|
|
586
|
-
|
|
925
|
+
private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
|
|
926
|
+
const slot = block.slotNumber;
|
|
927
|
+
const previousSlot = SlotNumber(slot - 1);
|
|
928
|
+
this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
|
|
929
|
+
p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
|
|
930
|
+
slot: block.slotNumber,
|
|
931
|
+
archive: block.archive.toString(),
|
|
932
|
+
source: sender.toString(),
|
|
933
|
+
});
|
|
934
|
+
const attestationsForPreviousSlot = await this.mempools.attestationPool.getAttestationsForSlot(previousSlot);
|
|
935
|
+
this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
|
|
936
|
+
|
|
937
|
+
// Attempt to add proposal, then mark the txs in this proposal as non-evictable
|
|
938
|
+
try {
|
|
939
|
+
await this.mempools.attestationPool.addBlockProposal(block);
|
|
940
|
+
} catch (err: unknown) {
|
|
941
|
+
// Drop proposals if we hit per-slot cap in the attestation pool; rethrow unknown errors
|
|
942
|
+
if (err instanceof ProposalSlotCapExceededError) {
|
|
943
|
+
this.logger.warn(`Dropping block proposal due to per-slot proposal cap`, {
|
|
944
|
+
slot: String(slot),
|
|
945
|
+
archive: block.archive.toString(),
|
|
946
|
+
error: (err as Error).message,
|
|
947
|
+
});
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
throw err;
|
|
951
|
+
}
|
|
952
|
+
await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
|
|
953
|
+
const attestations = await this.blockReceivedCallback(block, sender);
|
|
587
954
|
|
|
588
955
|
// TODO: fix up this pattern - the abstraction is not nice
|
|
589
|
-
// The attestation can be undefined if no handler is registered / the validator deems the block invalid
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
`Broadcasting attestation for
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
slot: attestation.slotNumber.toNumber(),
|
|
956
|
+
// The attestation can be undefined if no handler is registered / the validator deems the block invalid / in fisherman mode
|
|
957
|
+
if (attestations?.length) {
|
|
958
|
+
for (const attestation of attestations) {
|
|
959
|
+
this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber}`, {
|
|
960
|
+
p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
|
|
961
|
+
slot: attestation.slotNumber,
|
|
596
962
|
archive: attestation.archive.toString(),
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
await this.broadcastAttestation(attestation);
|
|
963
|
+
});
|
|
964
|
+
await this.broadcastAttestation(attestation);
|
|
965
|
+
}
|
|
601
966
|
}
|
|
602
967
|
}
|
|
603
968
|
|
|
@@ -606,10 +971,9 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
606
971
|
* @param attestation - The attestation to broadcast.
|
|
607
972
|
*/
|
|
608
973
|
@trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
|
|
609
|
-
[Attributes.
|
|
610
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
|
|
974
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
611
975
|
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
612
|
-
[Attributes.P2P_ID]: await attestation.
|
|
976
|
+
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
613
977
|
}))
|
|
614
978
|
private async broadcastAttestation(attestation: BlockAttestation) {
|
|
615
979
|
await this.propagate(attestation);
|
|
@@ -620,111 +984,312 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
620
984
|
* @param message - The message to propagate.
|
|
621
985
|
*/
|
|
622
986
|
public async propagate<T extends Gossipable>(message: T) {
|
|
623
|
-
const p2pMessageIdentifier = await message.
|
|
987
|
+
const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
|
|
624
988
|
this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
|
|
625
|
-
void this.
|
|
626
|
-
.
|
|
627
|
-
|
|
628
|
-
})
|
|
629
|
-
.catch(error => {
|
|
630
|
-
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
631
|
-
});
|
|
989
|
+
void this.sendToPeers(message).catch(error => {
|
|
990
|
+
this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
|
|
991
|
+
});
|
|
632
992
|
}
|
|
633
993
|
|
|
634
994
|
/**
|
|
635
|
-
* Validate
|
|
995
|
+
* Validate the requested block transactions. Allow partial returns.
|
|
996
|
+
* @param request - The block transactions request.
|
|
997
|
+
* @param response - The block transactions response.
|
|
998
|
+
* @param peerId - The ID of the peer that made the request.
|
|
999
|
+
* @returns True if the requested block transactions are valid, false otherwise.
|
|
1000
|
+
*/
|
|
1001
|
+
@trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
|
|
1002
|
+
[Attributes.BLOCK_HASH]: request.blockHash.toString(),
|
|
1003
|
+
}))
|
|
1004
|
+
private async validateRequestedBlockTxs(
|
|
1005
|
+
request: BlockTxsRequest,
|
|
1006
|
+
response: BlockTxsResponse,
|
|
1007
|
+
peerId: PeerId,
|
|
1008
|
+
): Promise<boolean> {
|
|
1009
|
+
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1010
|
+
|
|
1011
|
+
try {
|
|
1012
|
+
if (!response.blockHash.equals(request.blockHash)) {
|
|
1013
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1014
|
+
throw new ValidationError(
|
|
1015
|
+
`Received block txs for unexpected block: expected ${request.blockHash.toString()}, got ${response.blockHash.toString()}`,
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (response.txIndices.getLength() !== request.txIndices.getLength()) {
|
|
1020
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1021
|
+
throw new ValidationError(
|
|
1022
|
+
`Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// Check no duplicates and not exceeding returnable count
|
|
1027
|
+
const requestedIndices = new Set(request.txIndices.getTrueIndices());
|
|
1028
|
+
const availableIndices = new Set(response.txIndices.getTrueIndices());
|
|
1029
|
+
const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
|
|
1030
|
+
|
|
1031
|
+
const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
|
|
1032
|
+
const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
|
|
1033
|
+
if (uniqueReturned.size !== returnedHashes.length) {
|
|
1034
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1035
|
+
throw new ValidationError(`Received duplicate txs in block txs response`);
|
|
1036
|
+
}
|
|
1037
|
+
if (response.txs.length > maxReturnable) {
|
|
1038
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1039
|
+
throw new ValidationError(
|
|
1040
|
+
`Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// Given proposal (should have locally), ensure returned txs are valid subset and match request indices
|
|
1045
|
+
const proposal = await this.mempools.attestationPool.getBlockProposal(request.blockHash.toString());
|
|
1046
|
+
if (proposal) {
|
|
1047
|
+
// Build intersected indices
|
|
1048
|
+
const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
|
|
1049
|
+
|
|
1050
|
+
// Enforce subset membership and preserve increasing order by index.
|
|
1051
|
+
const hashToIndexInProposal = new Map<string, number>(
|
|
1052
|
+
proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
|
|
1053
|
+
);
|
|
1054
|
+
const allowedIndexSet = new Set(intersectIdx);
|
|
1055
|
+
const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
|
|
1056
|
+
const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
|
|
1057
|
+
const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
|
|
1058
|
+
if (!allAllowed || !strictlyIncreasing) {
|
|
1059
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1060
|
+
throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
|
|
1061
|
+
}
|
|
1062
|
+
} else {
|
|
1063
|
+
// No local proposal, cannot check the membership/order of the returned txs
|
|
1064
|
+
this.logger.warn(
|
|
1065
|
+
`Block proposal not found for block hash ${request.blockHash.toString()}; cannot validate membership/order of returned txs`,
|
|
1066
|
+
);
|
|
1067
|
+
return false;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
|
|
1071
|
+
return true;
|
|
1072
|
+
} catch (e: any) {
|
|
1073
|
+
if (e instanceof ValidationError) {
|
|
1074
|
+
this.logger.warn(`Failed validation for requested block txs from peer ${peerId.toString()}`);
|
|
1075
|
+
} else {
|
|
1076
|
+
this.logger.error(`Error during validation of requested block txs`, e);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Validate a collection of txs that has been requested from a peer.
|
|
636
1085
|
*
|
|
637
|
-
* The core component of this validator is that
|
|
1086
|
+
* The core component of this validator is that each tx hash MUST match the requested tx hash,
|
|
638
1087
|
* In order to perform this check, the tx proof must be verified.
|
|
639
1088
|
*
|
|
640
1089
|
* Note: This function is called from within `ReqResp.sendRequest` as part of the
|
|
641
1090
|
* ReqRespSubProtocol.TX subprotocol validation.
|
|
642
1091
|
*
|
|
643
|
-
* @param requestedTxHash - The
|
|
644
|
-
* @param responseTx - The
|
|
1092
|
+
* @param requestedTxHash - The collection of the txs that was requested.
|
|
1093
|
+
* @param responseTx - The collection of txs that was received as a response to the request.
|
|
645
1094
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
646
|
-
* @returns True if the
|
|
1095
|
+
* @returns True if the whole collection of txs is valid, false otherwise.
|
|
647
1096
|
*/
|
|
648
1097
|
@trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
|
|
649
1098
|
[Attributes.TX_HASH]: requestedTxHash.toString(),
|
|
650
1099
|
}))
|
|
651
|
-
private async
|
|
652
|
-
const
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
//
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1100
|
+
private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
|
|
1101
|
+
const requested = new Set(requestedTxHash.map(h => h.toString()));
|
|
1102
|
+
const requestedTxValidator = this.createRequestedTxValidator();
|
|
1103
|
+
|
|
1104
|
+
//TODO: (mralj) - this is somewhat naive implementation, if single tx is invlid we consider the whole response invalid.
|
|
1105
|
+
// I think we should still extract the valid txs and return them, so that we can still use the response.
|
|
1106
|
+
try {
|
|
1107
|
+
await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
|
|
1108
|
+
return true;
|
|
1109
|
+
} catch (e: any) {
|
|
1110
|
+
if (e instanceof ValidationError) {
|
|
1111
|
+
this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
|
|
1112
|
+
} else {
|
|
1113
|
+
this.logger.error(`Error during validation of requested txs`, e);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
659
1116
|
return false;
|
|
660
1117
|
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* Validates a BLOCK response.
|
|
1122
|
+
*
|
|
1123
|
+
* If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
|
|
1124
|
+
* Penalizes on block number mismatch or hash mismatch.
|
|
1125
|
+
*
|
|
1126
|
+
* @param requestedBlockNumber - The requested block number.
|
|
1127
|
+
* @param responseBlock - The block returned by the peer.
|
|
1128
|
+
* @param peerId - The peer that returned the block.
|
|
1129
|
+
* @returns True if the response is valid, false otherwise.
|
|
1130
|
+
*/
|
|
1131
|
+
@trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
|
|
1132
|
+
[Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
|
|
1133
|
+
}))
|
|
1134
|
+
private async validateRequestedBlock(
|
|
1135
|
+
requestedBlockNumber: Fr,
|
|
1136
|
+
responseBlock: L2Block,
|
|
1137
|
+
peerId: PeerId,
|
|
1138
|
+
): Promise<boolean> {
|
|
1139
|
+
try {
|
|
1140
|
+
const reqNum = Number(requestedBlockNumber.toString());
|
|
1141
|
+
if (responseBlock.number !== reqNum) {
|
|
1142
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
661
1145
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
1146
|
+
const local = await this.archiver.getBlock(BlockNumber(reqNum));
|
|
1147
|
+
if (!local) {
|
|
1148
|
+
// We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
|
|
1149
|
+
// TODO: Consider extending this validator to accept an expected hash or
|
|
1150
|
+
// performing quorum-based checks when using P2P syncing prior to L1 sync.
|
|
1151
|
+
this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
|
|
1152
|
+
return false;
|
|
1153
|
+
}
|
|
1154
|
+
const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
|
|
1155
|
+
if (!localHash.equals(respHash)) {
|
|
1156
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return true;
|
|
1161
|
+
} catch (e) {
|
|
1162
|
+
this.logger.warn(`Error validating requested block`, e);
|
|
665
1163
|
return false;
|
|
666
1164
|
}
|
|
1165
|
+
}
|
|
667
1166
|
|
|
668
|
-
|
|
1167
|
+
private createRequestedTxValidator(): TxValidator {
|
|
1168
|
+
return new AggregateTxValidator(
|
|
1169
|
+
new DataTxValidator(),
|
|
1170
|
+
new MetadataTxValidator({
|
|
1171
|
+
l1ChainId: new Fr(this.config.l1ChainId),
|
|
1172
|
+
rollupVersion: new Fr(this.config.rollupVersion),
|
|
1173
|
+
protocolContractsHash,
|
|
1174
|
+
vkTreeRoot: getVKTreeRoot(),
|
|
1175
|
+
}),
|
|
1176
|
+
new TxProofValidator(this.proofVerifier),
|
|
1177
|
+
);
|
|
669
1178
|
}
|
|
670
1179
|
|
|
671
|
-
|
|
672
|
-
|
|
1180
|
+
private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
|
|
1181
|
+
const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
|
|
1182
|
+
|
|
1183
|
+
if (!(await tx.validateTxHash())) {
|
|
1184
|
+
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1185
|
+
throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
if (requested && !requested.has(tx.getTxHash().toString())) {
|
|
1189
|
+
penalize(PeerErrorSeverity.MidToleranceError);
|
|
1190
|
+
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const { result } = await txValidator.validateTx(tx);
|
|
1194
|
+
if (result === 'invalid') {
|
|
1195
|
+
penalize(PeerErrorSeverity.LowToleranceError);
|
|
1196
|
+
throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
@trackSpan('Libp2pService.validatePropagatedTx', tx => ({
|
|
1201
|
+
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
673
1202
|
}))
|
|
674
1203
|
private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
|
|
675
|
-
const
|
|
676
|
-
const messageValidators = this.createMessageValidators(blockNumber);
|
|
677
|
-
const outcome = await this.runValidations(tx, messageValidators);
|
|
1204
|
+
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
678
1205
|
|
|
679
|
-
if (
|
|
680
|
-
|
|
1206
|
+
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
1207
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1208
|
+
const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
1209
|
+
|
|
1210
|
+
for (const validator of messageValidators) {
|
|
1211
|
+
const outcome = await this.runValidations(tx, validator);
|
|
1212
|
+
|
|
1213
|
+
if (outcome.allPassed) {
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
const { name } = outcome.failure;
|
|
1217
|
+
let { severity } = outcome.failure;
|
|
1218
|
+
|
|
1219
|
+
// Double spend validator has a special case handler
|
|
1220
|
+
if (name === 'doubleSpendValidator') {
|
|
1221
|
+
const txBlockNumber = BlockNumber(currentBlockNumber + 1); // tx is expected to be in the next block
|
|
1222
|
+
severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
this.peerManager.penalizePeer(peerId, severity);
|
|
1226
|
+
return false;
|
|
681
1227
|
}
|
|
682
|
-
|
|
683
|
-
|
|
1228
|
+
return true;
|
|
1229
|
+
}
|
|
684
1230
|
|
|
685
|
-
|
|
686
|
-
if (
|
|
687
|
-
|
|
1231
|
+
private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
|
|
1232
|
+
if (blockNumber === this.feesCache?.blockNumber) {
|
|
1233
|
+
return this.feesCache.gasFees;
|
|
688
1234
|
}
|
|
689
1235
|
|
|
690
|
-
this.
|
|
691
|
-
|
|
1236
|
+
const header = await this.archiver.getBlockHeader(blockNumber);
|
|
1237
|
+
const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
|
|
1238
|
+
this.feesCache = { blockNumber, gasFees };
|
|
1239
|
+
return gasFees;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
public async validate(txs: Tx[]): Promise<void> {
|
|
1243
|
+
const currentBlockNumber = await this.archiver.getBlockNumber();
|
|
1244
|
+
|
|
1245
|
+
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
1246
|
+
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1247
|
+
const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
|
|
1248
|
+
|
|
1249
|
+
await Promise.all(
|
|
1250
|
+
txs.map(async tx => {
|
|
1251
|
+
for (const validator of messageValidators) {
|
|
1252
|
+
const outcome = await this.runValidations(tx, validator);
|
|
1253
|
+
if (!outcome.allPassed) {
|
|
1254
|
+
throw new Error('Invalid tx detected', { cause: { outcome } });
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}),
|
|
1258
|
+
);
|
|
692
1259
|
}
|
|
693
1260
|
|
|
694
1261
|
/**
|
|
695
|
-
* Create message validators for the given block number.
|
|
1262
|
+
* Create message validators for the given block number and timestamp.
|
|
696
1263
|
*
|
|
697
1264
|
* Each validator is a pair of a validator and a severity.
|
|
698
1265
|
* If a validator fails, the peer is penalized with the severity of the validator.
|
|
699
1266
|
*
|
|
700
|
-
* @param
|
|
1267
|
+
* @param currentBlockNumber - The current synced block number.
|
|
1268
|
+
* @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
|
|
701
1269
|
* @returns The message validators.
|
|
702
1270
|
*/
|
|
703
|
-
private createMessageValidators(
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
severity: PeerErrorSeverity.HighToleranceError,
|
|
726
|
-
},
|
|
727
|
-
};
|
|
1271
|
+
private async createMessageValidators(
|
|
1272
|
+
currentBlockNumber: BlockNumber,
|
|
1273
|
+
nextSlotTimestamp: UInt64,
|
|
1274
|
+
): Promise<Record<string, MessageValidator>[]> {
|
|
1275
|
+
const gasFees = await this.getGasFees(currentBlockNumber);
|
|
1276
|
+
const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
1277
|
+
|
|
1278
|
+
const blockNumberInWhichTheTxIsConsideredToBeIncluded = BlockNumber(currentBlockNumber + 1);
|
|
1279
|
+
|
|
1280
|
+
return createTxMessageValidators(
|
|
1281
|
+
nextSlotTimestamp,
|
|
1282
|
+
blockNumberInWhichTheTxIsConsideredToBeIncluded,
|
|
1283
|
+
this.worldStateSynchronizer,
|
|
1284
|
+
gasFees,
|
|
1285
|
+
this.config.l1ChainId,
|
|
1286
|
+
this.config.rollupVersion,
|
|
1287
|
+
protocolContractsHash,
|
|
1288
|
+
this.archiver,
|
|
1289
|
+
this.proofVerifier,
|
|
1290
|
+
!this.config.disableTransactions,
|
|
1291
|
+
allowedInSetup,
|
|
1292
|
+
);
|
|
728
1293
|
}
|
|
729
1294
|
|
|
730
1295
|
/**
|
|
@@ -739,28 +1304,26 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
739
1304
|
): Promise<ValidationOutcome> {
|
|
740
1305
|
const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
|
|
741
1306
|
const { result } = await validator.validateTx(tx);
|
|
742
|
-
return { name, isValid: result
|
|
1307
|
+
return { name, isValid: result !== 'invalid', severity };
|
|
743
1308
|
});
|
|
744
1309
|
|
|
745
1310
|
// A promise that resolves when all validations have been run
|
|
746
|
-
const allValidations = Promise.all(validationPromises);
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
// If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
|
|
763
|
-
return result;
|
|
1311
|
+
const allValidations = await Promise.all(validationPromises);
|
|
1312
|
+
const failed = allValidations.find(x => !x.isValid);
|
|
1313
|
+
if (failed) {
|
|
1314
|
+
return {
|
|
1315
|
+
allPassed: false,
|
|
1316
|
+
failure: {
|
|
1317
|
+
isValid: { result: 'invalid' as const, reason: ['Failed validation'] },
|
|
1318
|
+
name: failed.name,
|
|
1319
|
+
severity: failed.severity,
|
|
1320
|
+
},
|
|
1321
|
+
};
|
|
1322
|
+
} else {
|
|
1323
|
+
return {
|
|
1324
|
+
allPassed: true,
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
764
1327
|
}
|
|
765
1328
|
|
|
766
1329
|
/**
|
|
@@ -774,7 +1337,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
774
1337
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
775
1338
|
* @returns Severity
|
|
776
1339
|
*/
|
|
777
|
-
private async handleDoubleSpendFailure(tx: Tx, blockNumber:
|
|
1340
|
+
private async handleDoubleSpendFailure(tx: Tx, blockNumber: BlockNumber): Promise<PeerErrorSeverity> {
|
|
778
1341
|
if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
|
|
779
1342
|
return PeerErrorSeverity.HighToleranceError;
|
|
780
1343
|
}
|
|
@@ -782,7 +1345,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
782
1345
|
const snapshotValidator = new DoubleSpendTxValidator({
|
|
783
1346
|
nullifiersExist: async (nullifiers: Buffer[]) => {
|
|
784
1347
|
const merkleTree = this.worldStateSynchronizer.getSnapshot(
|
|
785
|
-
blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
|
|
1348
|
+
BlockNumber(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow),
|
|
786
1349
|
);
|
|
787
1350
|
const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
788
1351
|
return indices.map(index => index !== undefined);
|
|
@@ -804,10 +1367,9 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
804
1367
|
* @returns True if the attestation is valid, false otherwise.
|
|
805
1368
|
*/
|
|
806
1369
|
@trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
|
|
807
|
-
[Attributes.
|
|
808
|
-
[Attributes.SLOT_NUMBER]: attestation.payload.header.globalVariables.slotNumber.toNumber(),
|
|
1370
|
+
[Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber,
|
|
809
1371
|
[Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
|
|
810
|
-
[Attributes.P2P_ID]: await attestation.
|
|
1372
|
+
[Attributes.P2P_ID]: await attestation.p2pMessageLoggingIdentifier().then(i => i.toString()),
|
|
811
1373
|
}))
|
|
812
1374
|
public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
|
|
813
1375
|
const severity = await this.attestationValidator.validate(attestation);
|
|
@@ -826,11 +1388,12 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
826
1388
|
* @returns True if the block proposal is valid, false otherwise.
|
|
827
1389
|
*/
|
|
828
1390
|
@trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
|
|
829
|
-
[Attributes.SLOT_NUMBER]: block.payload.header.
|
|
1391
|
+
[Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
|
|
830
1392
|
}))
|
|
831
1393
|
public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
|
|
832
1394
|
const severity = await this.blockProposalValidator.validate(block);
|
|
833
1395
|
if (severity) {
|
|
1396
|
+
this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
|
|
834
1397
|
this.peerManager.penalizePeer(peerId, severity);
|
|
835
1398
|
return false;
|
|
836
1399
|
}
|
|
@@ -842,13 +1405,17 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
|
|
|
842
1405
|
return this.node.services.pubsub.score.score(peerId.toString());
|
|
843
1406
|
}
|
|
844
1407
|
|
|
1408
|
+
public handleAuthRequestFromPeer(authRequest: AuthRequest, peerId: PeerId): Promise<StatusMessage> {
|
|
1409
|
+
return this.peerManager.handleAuthRequestFromPeer(authRequest, peerId);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
845
1412
|
private async sendToPeers<T extends Gossipable>(message: T) {
|
|
846
1413
|
const parent = message.constructor as typeof Gossipable;
|
|
847
1414
|
|
|
848
|
-
const identifier = await message.
|
|
1415
|
+
const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
|
|
849
1416
|
this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
|
|
850
1417
|
|
|
851
|
-
const recipientsNum = await this.publishToTopic(parent.p2pTopic, message
|
|
1418
|
+
const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);
|
|
852
1419
|
this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
|
|
853
1420
|
p2pMessageIdentifier: identifier,
|
|
854
1421
|
sourcePeer: this.node.peerId.toString(),
|