@aztec/p2p 3.0.0-canary.a9708bd → 3.0.0-devnet.2-patch.1
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 +14 -4
- package/dest/client/factory.d.ts +2 -1
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +8 -3
- package/dest/client/index.d.ts +1 -1
- package/dest/client/interface.d.ts +8 -6
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +11 -34
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +68 -46
- package/dest/config.d.ts +65 -56
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +21 -5
- package/dest/enr/generate-enr.d.ts +2 -2
- package/dest/enr/generate-enr.d.ts.map +1 -1
- package/dest/enr/generate-enr.js +1 -1
- 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 +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +43 -6
- 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 +72 -46
- package/dest/mem_pools/attestation_pool/index.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +15 -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 +73 -18
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +13 -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 +69 -11
- package/dest/mem_pools/attestation_pool/mocks.d.ts +226 -5
- package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/mocks.js +9 -7
- package/dest/mem_pools/index.d.ts +1 -1
- package/dest/mem_pools/instrumentation.d.ts +3 -1
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +11 -2
- package/dest/mem_pools/interface.d.ts +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +17 -40
- 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 +103 -44
- package/dest/mem_pools/tx_pool/index.d.ts +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +19 -6
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +88 -9
- package/dest/mem_pools/tx_pool/priority.d.ts +1 -1
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +21 -6
- 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 +132 -7
- 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 +32 -5
- 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 +5 -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 +56 -9
- 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 +1 -1
- package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -1
- 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 +1 -1
- package/dest/msg_validators/tx_validator/archive_cache.d.ts +2 -2
- package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
- 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 +1 -1
- 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/double_spend_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/double_spend_validator.js +1 -1
- package/dest/msg_validators/tx_validator/factory.d.ts +4 -3
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +11 -5
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.d.ts +2 -1
- package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/index.js +1 -0
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts +3 -6
- package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/metadata_validator.js +6 -24
- package/dest/msg_validators/tx_validator/phases_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/phases_validator.js +3 -1
- package/dest/msg_validators/tx_validator/test_utils.d.ts +2 -2
- package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +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 +1 -1
- package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
- 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/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 +3 -3
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +2 -2
- package/dest/services/dummy_service.d.ts +2 -2
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/encoding.d.ts +25 -4
- 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 +1 -1
- package/dest/services/libp2p/instrumentation.d.ts +3 -1
- package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
- package/dest/services/libp2p/instrumentation.js +9 -2
- package/dest/services/libp2p/libp2p_service.d.ts +29 -69
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +375 -133
- package/dest/services/peer-manager/interface.d.ts +1 -1
- package/dest/services/peer-manager/metrics.d.ts +3 -1
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +11 -0
- package/dest/services/peer-manager/peer_manager.d.ts +1 -32
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +27 -12
- 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 +1 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +1 -1
- package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +1 -4
- package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
- package/dest/services/reqresp/index.d.ts +1 -1
- package/dest/services/reqresp/interface.d.ts +2 -11
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +1 -18
- 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 +2 -2
- package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/auth.js +2 -2
- package/dest/services/reqresp/protocols/block.d.ts +1 -1
- package/dest/services/reqresp/protocols/block.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block.js +3 -2
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +4 -6
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +1 -1
- package/dest/services/reqresp/protocols/block_txs/index.d.ts +1 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts +1 -1
- package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.d.ts +1 -1
- package/dest/services/reqresp/protocols/ping.d.ts +1 -1
- package/dest/services/reqresp/protocols/status.d.ts +6 -5
- package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/status.js +4 -3
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +2 -2
- package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts +1 -41
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +14 -10
- package/dest/services/reqresp/status.d.ts +2 -2
- package/dest/services/reqresp/status.d.ts.map +1 -1
- package/dest/services/service.d.ts +2 -2
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/config.d.ts +1 -1
- package/dest/services/tx_collection/fast_tx_collection.d.ts +4 -9
- package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/fast_tx_collection.js +6 -1
- package/dest/services/tx_collection/index.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts +1 -1
- package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -7
- package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/slow_tx_collection.js +2 -1
- package/dest/services/tx_collection/tx_collection.d.ts +12 -11
- package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection.js +3 -2
- package/dest/services/tx_collection/tx_collection_sink.d.ts +3 -3
- package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_collection_sink.js +34 -4
- package/dest/services/tx_collection/tx_source.d.ts +1 -1
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +2 -2
- package/dest/services/tx_provider.d.ts +5 -4
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +8 -4
- package/dest/services/tx_provider_instrumentation.d.ts +1 -1
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
- 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 +1 -1
- package/dest/test-helpers/make-enrs.d.ts +1 -1
- package/dest/test-helpers/make-enrs.js +1 -1
- package/dest/test-helpers/make-test-p2p-clients.d.ts +2 -2
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.d.ts +4 -4
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- 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 +2 -2
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +4 -3
- package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +16 -9
- package/dest/testbench/parse_log_file.d.ts +1 -1
- package/dest/testbench/testbench.d.ts +1 -1
- package/dest/testbench/testbench.js +2 -2
- package/dest/testbench/worker_client_manager.d.ts +1 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/types/index.d.ts +1 -1
- package/dest/util.d.ts +2 -1
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +11 -2
- package/dest/versioning.d.ts +2 -2
- package/dest/versioning.d.ts.map +1 -1
- package/dest/versioning.js +2 -2
- package/package.json +21 -21
- package/src/bootstrap/bootstrap.ts +15 -4
- package/src/client/factory.ts +17 -3
- package/src/client/interface.ts +8 -5
- package/src/client/p2p_client.ts +88 -74
- package/src/config.ts +33 -6
- package/src/enr/generate-enr.ts +1 -1
- package/src/errors/attestation-pool.error.ts +13 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +46 -5
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +89 -48
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +107 -24
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +98 -19
- package/src/mem_pools/attestation_pool/mocks.ts +11 -8
- package/src/mem_pools/instrumentation.ts +13 -0
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +116 -48
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +98 -12
- package/src/mem_pools/tx_pool/tx_pool.ts +20 -5
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +115 -7
- package/src/msg_validators/attestation_validator/attestation_validator.ts +41 -6
- 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 +63 -18
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/block_header_validator.ts +2 -2
- package/src/msg_validators/tx_validator/double_spend_validator.ts +1 -1
- package/src/msg_validators/tx_validator/factory.ts +13 -6
- package/src/msg_validators/tx_validator/index.ts +1 -0
- package/src/msg_validators/tx_validator/metadata_validator.ts +8 -42
- package/src/msg_validators/tx_validator/phases_validator.ts +3 -1
- package/src/msg_validators/tx_validator/test_utils.ts +1 -1
- package/src/msg_validators/tx_validator/timestamp_validator.ts +47 -0
- package/src/services/discv5/discV5_service.ts +2 -2
- package/src/services/dummy_service.ts +1 -1
- package/src/services/encoding.ts +81 -6
- package/src/services/libp2p/instrumentation.ts +10 -1
- package/src/services/libp2p/libp2p_service.ts +427 -157
- package/src/services/peer-manager/metrics.ts +10 -0
- package/src/services/peer-manager/peer_manager.ts +22 -14
- package/src/services/peer-manager/peer_scoring.ts +46 -3
- package/src/services/reqresp/interface.ts +1 -22
- package/src/services/reqresp/protocols/auth.ts +2 -2
- package/src/services/reqresp/protocols/block.ts +3 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +1 -1
- package/src/services/reqresp/protocols/status.ts +9 -8
- package/src/services/reqresp/reqresp.ts +15 -11
- package/src/services/service.ts +1 -1
- package/src/services/tx_collection/fast_tx_collection.ts +8 -5
- package/src/services/tx_collection/slow_tx_collection.ts +7 -6
- package/src/services/tx_collection/tx_collection.ts +12 -10
- package/src/services/tx_collection/tx_collection_sink.ts +34 -3
- package/src/services/tx_collection/tx_source.ts +2 -2
- package/src/services/tx_provider.ts +9 -7
- package/src/test-helpers/make-enrs.ts +1 -1
- package/src/test-helpers/mock-tx-helpers.ts +24 -0
- package/src/test-helpers/reqresp-nodes.ts +3 -2
- package/src/testbench/p2p_client_testbench_worker.ts +12 -5
- package/src/testbench/testbench.ts +2 -2
- package/src/util.ts +12 -2
- package/src/versioning.ts +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
3
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
3
4
|
import type { TxAddedToPoolStats } from '@aztec/stdlib/stats';
|
|
@@ -18,8 +19,11 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
18
19
|
* Our tx pool, stored as a Map in-memory, with K: tx hash and V: the transaction.
|
|
19
20
|
*/
|
|
20
21
|
private txs: Map<bigint, Tx>;
|
|
21
|
-
private minedTxs: Map<bigint,
|
|
22
|
+
private minedTxs: Map<bigint, BlockNumber>;
|
|
22
23
|
private pendingTxs: Set<bigint>;
|
|
24
|
+
private deletedMinedTxHashes: Map<bigint, BlockNumber>;
|
|
25
|
+
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
26
|
+
private blockToDeletedMinedTxHash: Map<BlockNumber, Set<bigint>>;
|
|
23
27
|
|
|
24
28
|
private metrics: PoolInstrumentation<Tx>;
|
|
25
29
|
|
|
@@ -35,6 +39,8 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
35
39
|
this.txs = new Map<bigint, Tx>();
|
|
36
40
|
this.minedTxs = new Map();
|
|
37
41
|
this.pendingTxs = new Set();
|
|
42
|
+
this.deletedMinedTxHashes = new Map();
|
|
43
|
+
this.blockToDeletedMinedTxHash = new Map();
|
|
38
44
|
this.metrics = new PoolInstrumentation(telemetry, PoolName.TX_POOL, this.countTx);
|
|
39
45
|
}
|
|
40
46
|
|
|
@@ -54,6 +60,19 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
54
60
|
public markAsMined(txHashes: TxHash[], blockHeader: BlockHeader): Promise<void> {
|
|
55
61
|
const keys = txHashes.map(x => x.toBigInt());
|
|
56
62
|
for (const key of keys) {
|
|
63
|
+
// If this tx was previously soft-deleted, remove it from the deleted sets
|
|
64
|
+
if (this.deletedMinedTxHashes.has(key)) {
|
|
65
|
+
const originalBlock = this.deletedMinedTxHashes.get(key)!;
|
|
66
|
+
this.deletedMinedTxHashes.delete(key);
|
|
67
|
+
// Remove from block-to-hash mapping
|
|
68
|
+
const txHashesForBlock = this.blockToDeletedMinedTxHash.get(originalBlock);
|
|
69
|
+
if (txHashesForBlock) {
|
|
70
|
+
txHashesForBlock.delete(key);
|
|
71
|
+
if (txHashesForBlock.size === 0) {
|
|
72
|
+
this.blockToDeletedMinedTxHash.delete(originalBlock);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
57
76
|
this.minedTxs.set(key, blockHeader.globalVariables.blockNumber);
|
|
58
77
|
this.pendingTxs.delete(key);
|
|
59
78
|
}
|
|
@@ -83,27 +102,37 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
83
102
|
(tx1, tx2) => -getPendingTxPriority(tx1).localeCompare(getPendingTxPriority(tx2)),
|
|
84
103
|
);
|
|
85
104
|
const txHashes = await Promise.all(txs.map(tx => tx.getTxHash()));
|
|
86
|
-
|
|
105
|
+
|
|
106
|
+
// No need to check deleted since pending txs are never soft-deleted
|
|
107
|
+
return txHashes.filter(txHash => {
|
|
108
|
+
const key = txHash.toBigInt();
|
|
109
|
+
return this.pendingTxs.has(key);
|
|
110
|
+
});
|
|
87
111
|
}
|
|
88
112
|
|
|
89
|
-
public getMinedTxHashes(): Promise<[TxHash,
|
|
113
|
+
public getMinedTxHashes(): Promise<[TxHash, BlockNumber][]> {
|
|
90
114
|
return Promise.resolve(
|
|
91
115
|
Array.from(this.minedTxs.entries()).map(([txHash, blockNumber]) => [TxHash.fromBigInt(txHash), blockNumber]),
|
|
92
116
|
);
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
public getPendingTxCount(): Promise<number> {
|
|
120
|
+
// Soft-deleted transactions are always mined, never pending
|
|
96
121
|
return Promise.resolve(this.pendingTxs.size);
|
|
97
122
|
}
|
|
98
123
|
|
|
99
|
-
public getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined> {
|
|
124
|
+
public getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | 'deleted' | undefined> {
|
|
100
125
|
const key = txHash.toBigInt();
|
|
101
|
-
|
|
102
|
-
|
|
126
|
+
|
|
127
|
+
if (this.deletedMinedTxHashes.has(key)) {
|
|
128
|
+
return Promise.resolve('deleted');
|
|
103
129
|
}
|
|
104
130
|
if (this.minedTxs.has(key)) {
|
|
105
131
|
return Promise.resolve('mined');
|
|
106
132
|
}
|
|
133
|
+
if (this.pendingTxs.has(key)) {
|
|
134
|
+
return Promise.resolve('pending');
|
|
135
|
+
}
|
|
107
136
|
return Promise.resolve(undefined);
|
|
108
137
|
}
|
|
109
138
|
|
|
@@ -124,6 +153,11 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
124
153
|
return Promise.resolve(txHashes.map(txHash => this.txs.has(txHash.toBigInt())));
|
|
125
154
|
}
|
|
126
155
|
|
|
156
|
+
async hasTx(txHash: TxHash): Promise<boolean> {
|
|
157
|
+
const result = await this.hasTxs([txHash]);
|
|
158
|
+
return result[0];
|
|
159
|
+
}
|
|
160
|
+
|
|
127
161
|
public getArchivedTxByHash(): Promise<Tx | undefined> {
|
|
128
162
|
return Promise.resolve(undefined);
|
|
129
163
|
}
|
|
@@ -161,15 +195,34 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
161
195
|
|
|
162
196
|
/**
|
|
163
197
|
* Deletes transactions from the pool. Tx hashes that are not present are ignored.
|
|
164
|
-
*
|
|
165
|
-
* @
|
|
198
|
+
* Mined transactions are soft-deleted with a timestamp, pending transactions are permanently deleted.
|
|
199
|
+
* @param txHashes - An array of tx hashes to be deleted from the tx pool.
|
|
200
|
+
* @returns Empty promise.
|
|
166
201
|
*/
|
|
167
|
-
public deleteTxs(txHashes: TxHash[]): Promise<void> {
|
|
202
|
+
public deleteTxs(txHashes: TxHash[], opts?: { permanently?: boolean }): Promise<void> {
|
|
168
203
|
for (const txHash of txHashes) {
|
|
169
204
|
const key = txHash.toBigInt();
|
|
170
|
-
this.txs.
|
|
171
|
-
|
|
172
|
-
|
|
205
|
+
if (this.txs.has(key)) {
|
|
206
|
+
if (this.minedTxs.has(key)) {
|
|
207
|
+
const blockNumber = this.minedTxs.get(key)!;
|
|
208
|
+
this.minedTxs.delete(key);
|
|
209
|
+
// Soft-delete mined transactions: remove from mined set but keep in storage
|
|
210
|
+
if (opts?.permanently) {
|
|
211
|
+
// Permanently delete mined transactions if specified
|
|
212
|
+
this.txs.delete(key);
|
|
213
|
+
} else {
|
|
214
|
+
this.deletedMinedTxHashes.set(key, blockNumber);
|
|
215
|
+
if (!this.blockToDeletedMinedTxHash.has(blockNumber)) {
|
|
216
|
+
this.blockToDeletedMinedTxHash.set(blockNumber, new Set());
|
|
217
|
+
}
|
|
218
|
+
this.blockToDeletedMinedTxHash.get(blockNumber)!.add(key);
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
// Permanently delete pending transactions
|
|
222
|
+
this.txs.delete(key);
|
|
223
|
+
this.pendingTxs.delete(key);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
173
226
|
}
|
|
174
227
|
|
|
175
228
|
return Promise.resolve();
|
|
@@ -196,4 +249,37 @@ export class InMemoryTxPool extends (EventEmitter as new () => TypedEventEmitter
|
|
|
196
249
|
markTxsAsNonEvictable(_: TxHash[]): Promise<void> {
|
|
197
250
|
return Promise.resolve();
|
|
198
251
|
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Permanently deletes deleted mined transactions from blocks up to and including the specified block number.
|
|
255
|
+
* @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.
|
|
256
|
+
* @returns The number of transactions permanently deleted.
|
|
257
|
+
*/
|
|
258
|
+
public cleanupDeletedMinedTxs(blockNumber: BlockNumber): Promise<number> {
|
|
259
|
+
let deletedCount = 0;
|
|
260
|
+
const blocksToDelete: BlockNumber[] = [];
|
|
261
|
+
|
|
262
|
+
// Find all blocks up to the specified block number
|
|
263
|
+
for (const [block, txHashes] of this.blockToDeletedMinedTxHash.entries()) {
|
|
264
|
+
if (block <= blockNumber) {
|
|
265
|
+
// Permanently delete all transactions from this block
|
|
266
|
+
for (const txHash of txHashes) {
|
|
267
|
+
this.txs.delete(txHash);
|
|
268
|
+
this.deletedMinedTxHashes.delete(txHash);
|
|
269
|
+
deletedCount++;
|
|
270
|
+
}
|
|
271
|
+
blocksToDelete.push(block);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Clean up block-to-hash mapping
|
|
276
|
+
for (const block of blocksToDelete) {
|
|
277
|
+
this.blockToDeletedMinedTxHash.delete(block);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (deletedCount > 0) {
|
|
281
|
+
this.log.debug(`Permanently deleted ${deletedCount} deleted mined txs from blocks up to ${blockNumber}`);
|
|
282
|
+
}
|
|
283
|
+
return Promise.resolve(deletedCount);
|
|
284
|
+
}
|
|
199
285
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
2
3
|
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
|
|
3
4
|
|
|
@@ -43,6 +44,13 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
|
|
|
43
44
|
*/
|
|
44
45
|
hasTxs(txHashes: TxHash[]): Promise<boolean[]>;
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Checks if a transaction exists in the pool
|
|
49
|
+
* @param txHash - The hash of the transaction to check for
|
|
50
|
+
* @returns True if the transaction exists, false otherwise
|
|
51
|
+
*/
|
|
52
|
+
hasTx(txHash: TxHash): Promise<boolean>;
|
|
53
|
+
|
|
46
54
|
/**
|
|
47
55
|
* Checks if an archived transaction exists in the pool and returns it.
|
|
48
56
|
* @param txHash - The hash of the transaction, used as an ID.
|
|
@@ -68,7 +76,7 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
|
|
|
68
76
|
* Deletes transactions from the pool. Tx hashes that are not present are ignored.
|
|
69
77
|
* @param txHashes - An array of tx hashes to be removed from the tx pool.
|
|
70
78
|
*/
|
|
71
|
-
deleteTxs(txHashes: TxHash[]): Promise<void>;
|
|
79
|
+
deleteTxs(txHashes: TxHash[], opts?: { permanently?: boolean }): Promise<void>;
|
|
72
80
|
|
|
73
81
|
/**
|
|
74
82
|
* Gets all transactions currently in the tx pool.
|
|
@@ -95,14 +103,14 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
|
|
|
95
103
|
* Gets the hashes of mined transactions currently in the tx pool.
|
|
96
104
|
* @returns An array of mined transaction hashes found in the tx pool.
|
|
97
105
|
*/
|
|
98
|
-
getMinedTxHashes(): Promise<[tx: TxHash, blockNumber:
|
|
106
|
+
getMinedTxHashes(): Promise<[tx: TxHash, blockNumber: BlockNumber][]>;
|
|
99
107
|
|
|
100
108
|
/**
|
|
101
|
-
* Returns whether the given tx hash is flagged as pending or
|
|
109
|
+
* Returns whether the given tx hash is flagged as pending, mined, or deleted.
|
|
102
110
|
* @param txHash - Hash of the tx to query.
|
|
103
|
-
* @returns Pending or
|
|
111
|
+
* @returns Pending, mined, or deleted depending on its status, or undefined if not found.
|
|
104
112
|
*/
|
|
105
|
-
getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined>;
|
|
113
|
+
getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | 'deleted' | undefined>;
|
|
106
114
|
|
|
107
115
|
/**
|
|
108
116
|
* Configure the maximum size of the tx pool
|
|
@@ -118,4 +126,11 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
|
|
|
118
126
|
* @param txHashes - Hashes of the transactions to mark as non-evictible.
|
|
119
127
|
*/
|
|
120
128
|
markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void>;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Permanently deletes deleted mined transactions from blocks up to and including the specified block number.
|
|
132
|
+
* @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.
|
|
133
|
+
* @returns The number of transactions permanently deleted.
|
|
134
|
+
*/
|
|
135
|
+
cleanupDeletedMinedTxs(blockNumber: BlockNumber): Promise<number>;
|
|
121
136
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { unfreeze } from '@aztec/foundation/types';
|
|
2
3
|
import { GasFees } from '@aztec/stdlib/gas';
|
|
3
4
|
import { mockTx } from '@aztec/stdlib/testing';
|
|
@@ -13,7 +14,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
|
|
|
13
14
|
let pool: TxPool;
|
|
14
15
|
|
|
15
16
|
const minedBlockHeader = BlockHeader.empty({
|
|
16
|
-
globalVariables: GlobalVariables.empty({ blockNumber: 1, timestamp: 0n }),
|
|
17
|
+
globalVariables: GlobalVariables.empty({ blockNumber: BlockNumber(1), timestamp: 0n }),
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
beforeEach(() => {
|
|
@@ -54,14 +55,24 @@ export function describeTxPool(getTxPool: () => TxPool) {
|
|
|
54
55
|
expect(txsFromEvent).toEqual(expect.arrayContaining([tx2, tx3]));
|
|
55
56
|
});
|
|
56
57
|
|
|
57
|
-
it('
|
|
58
|
-
const
|
|
58
|
+
it('permanently deletes pending txs and soft-deletes mined txs', async () => {
|
|
59
|
+
const pendingTx = await mockTx(1);
|
|
60
|
+
const minedTx = await mockTx(2);
|
|
59
61
|
|
|
60
|
-
await pool.addTxs([
|
|
61
|
-
await pool.
|
|
62
|
+
await pool.addTxs([pendingTx, minedTx]);
|
|
63
|
+
await pool.markAsMined([minedTx.getTxHash()], minedBlockHeader);
|
|
64
|
+
|
|
65
|
+
// Delete a pending tx - should be permanently deleted
|
|
66
|
+
await pool.deleteTxs([pendingTx.getTxHash()]);
|
|
67
|
+
await expect(pool.getTxByHash(pendingTx.getTxHash())).resolves.toBeUndefined();
|
|
68
|
+
await expect(pool.getTxStatus(pendingTx.getTxHash())).resolves.toBeUndefined();
|
|
69
|
+
|
|
70
|
+
// Delete a mined tx - should be soft-deleted (still in storage)
|
|
71
|
+
await pool.deleteTxs([minedTx.getTxHash()]);
|
|
72
|
+
await expect(pool.getTxByHash(minedTx.getTxHash())).resolves.toBeDefined();
|
|
73
|
+
await expect(pool.getTxStatus(minedTx.getTxHash())).resolves.toEqual('deleted');
|
|
74
|
+
await expect(pool.getMinedTxHashes()).resolves.toEqual([]);
|
|
62
75
|
|
|
63
|
-
await expect(pool.getTxByHash(tx1.getTxHash())).resolves.toBeFalsy();
|
|
64
|
-
await expect(pool.getTxStatus(tx1.getTxHash())).resolves.toBeUndefined();
|
|
65
76
|
await expect(pool.getPendingTxCount()).resolves.toEqual(0);
|
|
66
77
|
});
|
|
67
78
|
|
|
@@ -203,4 +214,101 @@ export function describeTxPool(getTxPool: () => TxPool) {
|
|
|
203
214
|
expect(poolTxHashes).toHaveLength(4);
|
|
204
215
|
expect(poolTxHashes).toEqual([tx4, tx1, tx3, tx2].map(tx => tx.getTxHash()));
|
|
205
216
|
});
|
|
217
|
+
|
|
218
|
+
describe('soft-delete', () => {
|
|
219
|
+
it('soft-deletes mined txs and keeps them in storage', async () => {
|
|
220
|
+
const txs = await Promise.all([mockTx(1), mockTx(2), mockTx(3)]);
|
|
221
|
+
await pool.addTxs(txs);
|
|
222
|
+
|
|
223
|
+
// Mark first tx as mined
|
|
224
|
+
await pool.markAsMined([txs[0].getTxHash()], minedBlockHeader);
|
|
225
|
+
|
|
226
|
+
// Verify initial state
|
|
227
|
+
await expect(pool.getPendingTxCount()).resolves.toBe(2);
|
|
228
|
+
await expect(pool.getTxByHash(txs[0].getTxHash())).resolves.toBeDefined();
|
|
229
|
+
await expect(pool.getTxByHash(txs[1].getTxHash())).resolves.toBeDefined();
|
|
230
|
+
|
|
231
|
+
// Delete mined tx - should be soft-deleted
|
|
232
|
+
await pool.deleteTxs([txs[0].getTxHash()]);
|
|
233
|
+
|
|
234
|
+
// Delete pending tx - should be permanently deleted
|
|
235
|
+
await pool.deleteTxs([txs[1].getTxHash()]);
|
|
236
|
+
|
|
237
|
+
// Verify mined tx still exists in storage but has 'deleted' status
|
|
238
|
+
await expect(pool.getTxByHash(txs[0].getTxHash())).resolves.toBeDefined();
|
|
239
|
+
await expect(pool.getTxStatus(txs[0].getTxHash())).resolves.toEqual('deleted');
|
|
240
|
+
|
|
241
|
+
// Verify pending tx is permanently deleted
|
|
242
|
+
await expect(pool.getTxByHash(txs[1].getTxHash())).resolves.toBeUndefined();
|
|
243
|
+
await expect(pool.getTxStatus(txs[1].getTxHash())).resolves.toBeUndefined();
|
|
244
|
+
|
|
245
|
+
// Verify remaining pending count
|
|
246
|
+
await expect(pool.getPendingTxCount()).resolves.toBe(1);
|
|
247
|
+
|
|
248
|
+
// Verify pending hashes don't include deleted txs
|
|
249
|
+
const pendingHashes = await pool.getPendingTxHashes();
|
|
250
|
+
expect(pendingHashes).toHaveLength(1);
|
|
251
|
+
expect(pendingHashes.map(h => h.toString())).toContain(txs[2].getTxHash().toString());
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('cleans up old deleted mined transactions', async () => {
|
|
255
|
+
const txs = await Promise.all([mockTx(1), mockTx(2), mockTx(3)]);
|
|
256
|
+
await pool.addTxs(txs);
|
|
257
|
+
|
|
258
|
+
// Mark first two as mined in block 1
|
|
259
|
+
await pool.markAsMined([txs[0].getTxHash(), txs[1].getTxHash()], minedBlockHeader);
|
|
260
|
+
|
|
261
|
+
// Soft-delete mined transactions
|
|
262
|
+
await pool.deleteTxs([txs[0].getTxHash(), txs[1].getTxHash()]);
|
|
263
|
+
|
|
264
|
+
// Clean up deleted mined txs from block 1 and earlier
|
|
265
|
+
const deletedCount = await pool.cleanupDeletedMinedTxs(BlockNumber(1));
|
|
266
|
+
|
|
267
|
+
// Verify old transactions are permanently deleted
|
|
268
|
+
expect(deletedCount).toBe(2);
|
|
269
|
+
await expect(pool.getTxByHash(txs[0].getTxHash())).resolves.toBeUndefined();
|
|
270
|
+
await expect(pool.getTxByHash(txs[1].getTxHash())).resolves.toBeUndefined();
|
|
271
|
+
await expect(pool.getTxByHash(txs[2].getTxHash())).resolves.toBeDefined();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('does not clean up recent deleted mined transactions', async () => {
|
|
275
|
+
const txs = await Promise.all([mockTx(1), mockTx(2)]);
|
|
276
|
+
await pool.addTxs(txs);
|
|
277
|
+
|
|
278
|
+
// Mark as mined in block 2
|
|
279
|
+
const laterBlockHeader = BlockHeader.empty({
|
|
280
|
+
globalVariables: GlobalVariables.empty({ blockNumber: BlockNumber(2), timestamp: 0n }),
|
|
281
|
+
});
|
|
282
|
+
await pool.markAsMined([txs[0].getTxHash()], laterBlockHeader);
|
|
283
|
+
|
|
284
|
+
// Soft-delete a mined transaction
|
|
285
|
+
await pool.deleteTxs([txs[0].getTxHash()]);
|
|
286
|
+
|
|
287
|
+
// Try to clean up with block 1 (before the mined block)
|
|
288
|
+
const deletedCount = await pool.cleanupDeletedMinedTxs(BlockNumber(1));
|
|
289
|
+
|
|
290
|
+
// Verify no transactions were cleaned up
|
|
291
|
+
expect(deletedCount).toBe(0);
|
|
292
|
+
await expect(pool.getTxByHash(txs[0].getTxHash())).resolves.toBeDefined();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('restores deleted mined tx when it is mined again', async () => {
|
|
296
|
+
const tx = await mockTx(1);
|
|
297
|
+
await pool.addTxs([tx]);
|
|
298
|
+
|
|
299
|
+
// Mark as mined
|
|
300
|
+
await pool.markAsMined([tx.getTxHash()], minedBlockHeader);
|
|
301
|
+
|
|
302
|
+
// Soft-delete it
|
|
303
|
+
await pool.deleteTxs([tx.getTxHash()]);
|
|
304
|
+
await expect(pool.getTxStatus(tx.getTxHash())).resolves.toEqual('deleted');
|
|
305
|
+
|
|
306
|
+
// Mark as mined again (e.g., after a reorg)
|
|
307
|
+
await pool.markAsMined([tx.getTxHash()], minedBlockHeader);
|
|
308
|
+
|
|
309
|
+
// Should be back to mined status
|
|
310
|
+
await expect(pool.getTxStatus(tx.getTxHash())).resolves.toEqual('mined');
|
|
311
|
+
await expect(pool.getTxByHash(tx.getTxHash())).resolves.toBeDefined();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
206
314
|
}
|
|
@@ -1,31 +1,66 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import { NoCommitteeError } from '@aztec/ethereum';
|
|
2
|
+
import { NoCommitteeError } from '@aztec/ethereum/contracts';
|
|
3
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { type BlockAttestation, type P2PValidator, PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
4
5
|
|
|
5
6
|
export class AttestationValidator implements P2PValidator<BlockAttestation> {
|
|
6
|
-
|
|
7
|
+
protected epochCache: EpochCacheInterface;
|
|
8
|
+
protected logger: Logger;
|
|
7
9
|
|
|
8
10
|
constructor(epochCache: EpochCacheInterface) {
|
|
9
11
|
this.epochCache = epochCache;
|
|
12
|
+
this.logger = createLogger('p2p:attestation-validator');
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
async validate(message: BlockAttestation): Promise<PeerErrorSeverity | undefined> {
|
|
16
|
+
const slotNumber = message.payload.header.slotNumber;
|
|
17
|
+
|
|
13
18
|
try {
|
|
14
|
-
const { currentSlot, nextSlot } =
|
|
19
|
+
const { currentProposer, nextProposer, currentSlot, nextSlot } =
|
|
20
|
+
await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
if (slotNumber !== currentSlot && slotNumber !== nextSlot) {
|
|
23
|
+
this.logger.warn(`Attestation slot ${slotNumber} is not current (${currentSlot}) or next (${nextSlot}) slot`);
|
|
18
24
|
return PeerErrorSeverity.HighToleranceError;
|
|
19
25
|
}
|
|
20
26
|
|
|
27
|
+
// Verify the signature is valid
|
|
21
28
|
const attester = message.getSender();
|
|
22
|
-
if (
|
|
29
|
+
if (attester === undefined) {
|
|
30
|
+
this.logger.warn(`Invalid signature in attestation for slot ${slotNumber}`);
|
|
31
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Verify the attester is in the committee for this slot
|
|
35
|
+
if (!(await this.epochCache.isInCommittee(slotNumber, attester))) {
|
|
36
|
+
this.logger.warn(`Attester ${attester.toString()} is not in committee for slot ${slotNumber}`);
|
|
37
|
+
return PeerErrorSeverity.HighToleranceError;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Verify the proposer signature matches the expected proposer for this slot
|
|
41
|
+
const proposer = message.getProposer();
|
|
42
|
+
const expectedProposer = slotNumber === currentSlot ? currentProposer : nextProposer;
|
|
43
|
+
if (!expectedProposer) {
|
|
44
|
+
this.logger.warn(`No proposer defined for slot ${slotNumber}`);
|
|
45
|
+
return PeerErrorSeverity.HighToleranceError;
|
|
46
|
+
}
|
|
47
|
+
if (!proposer) {
|
|
48
|
+
this.logger.warn(`Invalid proposer signature in attestation for slot ${slotNumber}`);
|
|
49
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
50
|
+
}
|
|
51
|
+
if (!proposer.equals(expectedProposer)) {
|
|
52
|
+
this.logger.warn(
|
|
53
|
+
`Proposer signature mismatch in attestation. ` +
|
|
54
|
+
`Expected ${expectedProposer?.toString() ?? 'none'} but got ${proposer.toString()} for slot ${slotNumber}`,
|
|
55
|
+
);
|
|
23
56
|
return PeerErrorSeverity.HighToleranceError;
|
|
24
57
|
}
|
|
58
|
+
|
|
25
59
|
return undefined;
|
|
26
60
|
} catch (e) {
|
|
27
61
|
// People shouldn't be sending us attestations if the committee doesn't exist
|
|
28
62
|
if (e instanceof NoCommitteeError) {
|
|
63
|
+
this.logger.warn(`No committee exists for attestation for slot ${slotNumber}`);
|
|
29
64
|
return PeerErrorSeverity.LowToleranceError;
|
|
30
65
|
}
|
|
31
66
|
throw e;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
+
import { type BlockAttestation, PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
3
|
+
import { Attributes, Metrics, type TelemetryClient, ValueType } from '@aztec/telemetry-client';
|
|
4
|
+
|
|
5
|
+
import type { AttestationPool } from '../../mem_pools/attestation_pool/attestation_pool.js';
|
|
6
|
+
import { AttestationValidator } from './attestation_validator.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* FishermanAttestationValidator extends the base AttestationValidator to add
|
|
10
|
+
* additional validation for fisherman nodes: verifying that attestations sign
|
|
11
|
+
* the same payload as the original proposal.
|
|
12
|
+
* Invalid attestations are rejected (not propagated), but peer penalization is
|
|
13
|
+
* handled by LibP2PService based on the fishermanMode config to ensure a better
|
|
14
|
+
* view of the network.
|
|
15
|
+
*/
|
|
16
|
+
export class FishermanAttestationValidator extends AttestationValidator {
|
|
17
|
+
private invalidAttestationCounter;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
epochCache: EpochCacheInterface,
|
|
21
|
+
private attestationPool: AttestationPool,
|
|
22
|
+
telemetryClient: TelemetryClient,
|
|
23
|
+
) {
|
|
24
|
+
super(epochCache);
|
|
25
|
+
this.logger = this.logger.createChild('[FISHERMAN]');
|
|
26
|
+
|
|
27
|
+
const meter = telemetryClient.getMeter('FishermanAttestationValidator');
|
|
28
|
+
this.invalidAttestationCounter = meter.createUpDownCounter(Metrics.VALIDATOR_INVALID_ATTESTATION_RECEIVED_COUNT, {
|
|
29
|
+
description: 'The number of invalid attestations received',
|
|
30
|
+
valueType: ValueType.INT,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override async validate(message: BlockAttestation): Promise<PeerErrorSeverity | undefined> {
|
|
35
|
+
// First run the standard validation
|
|
36
|
+
const baseValidationResult = await super.validate(message);
|
|
37
|
+
if (baseValidationResult !== undefined) {
|
|
38
|
+
// Track base validation failures (invalid signature, wrong committee, etc.)
|
|
39
|
+
this.invalidAttestationCounter.add(1, {
|
|
40
|
+
[Attributes.ERROR_TYPE]: 'base_validation_failed',
|
|
41
|
+
});
|
|
42
|
+
return baseValidationResult;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// fisherman validation: verify attestation payload matches proposal payload
|
|
46
|
+
const slotNumberBigInt = message.payload.header.slotNumber;
|
|
47
|
+
const attester = message.getSender();
|
|
48
|
+
const proposer = message.getProposer();
|
|
49
|
+
|
|
50
|
+
if (!attester || !proposer) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const proposalId = message.archive.toString();
|
|
55
|
+
const proposal = await this.attestationPool.getBlockProposal(proposalId);
|
|
56
|
+
|
|
57
|
+
if (proposal) {
|
|
58
|
+
// Compare the attestation payload with the proposal payload
|
|
59
|
+
if (!message.payload.equals(proposal.payload)) {
|
|
60
|
+
this.logger.error(
|
|
61
|
+
`Attestation payload mismatch for slot ${slotNumberBigInt}! ` +
|
|
62
|
+
`Attester ${attester.toString()} signed different data than the proposal.`,
|
|
63
|
+
{
|
|
64
|
+
slot: slotNumberBigInt.toString(),
|
|
65
|
+
attester: attester.toString(),
|
|
66
|
+
proposer: proposer.toString(),
|
|
67
|
+
proposalArchive: proposal.archive.toString(),
|
|
68
|
+
attestationArchive: message.archive.toString(),
|
|
69
|
+
proposalHeader: proposal.payload.header.hash().toString(),
|
|
70
|
+
attestationHeader: message.payload.header.hash().toString(),
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Track invalid attestation metric
|
|
75
|
+
this.invalidAttestationCounter.add(1, {
|
|
76
|
+
[Attributes.ERROR_TYPE]: 'payload_mismatch',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Return error to reject the message, but LibP2PService won't penalize in fisherman mode
|
|
80
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
// We might receive attestations before proposals in some cases
|
|
84
|
+
this.logger.debug(
|
|
85
|
+
`Received attestation for slot ${slotNumberBigInt} but proposal not found yet. ` + `Proposal ID: ${proposalId}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -1,43 +1,88 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import { NoCommitteeError } from '@aztec/ethereum';
|
|
2
|
+
import { NoCommitteeError } from '@aztec/ethereum/contracts';
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { type BlockProposal, type P2PValidator, PeerErrorSeverity } from '@aztec/stdlib/p2p';
|
|
5
5
|
|
|
6
6
|
export class BlockProposalValidator implements P2PValidator<BlockProposal> {
|
|
7
7
|
private epochCache: EpochCacheInterface;
|
|
8
8
|
private logger: Logger;
|
|
9
|
+
private txsPermitted: boolean;
|
|
9
10
|
|
|
10
|
-
constructor(epochCache: EpochCacheInterface) {
|
|
11
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
|
|
11
12
|
this.epochCache = epochCache;
|
|
13
|
+
this.txsPermitted = opts.txsPermitted;
|
|
12
14
|
this.logger = createLogger('p2p:block_proposal_validator');
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
async validate(block: BlockProposal): Promise<PeerErrorSeverity | undefined> {
|
|
16
18
|
try {
|
|
19
|
+
// Check signature validity first - invalid signatures are a high-severity issue
|
|
20
|
+
const proposer = block.getSender();
|
|
21
|
+
if (!proposer) {
|
|
22
|
+
this.logger.debug(`Penalizing peer for block proposal with invalid signature`);
|
|
23
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if transactions are permitted when the proposal contains transaction hashes
|
|
27
|
+
const embeddedTxCount = block.txs?.length ?? 0;
|
|
28
|
+
if (!this.txsPermitted && (block.txHashes.length > 0 || embeddedTxCount > 0)) {
|
|
29
|
+
this.logger.debug(
|
|
30
|
+
`Penalizing peer for block proposal with ${block.txHashes.length} transaction(s) when transactions are not permitted`,
|
|
31
|
+
);
|
|
32
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If there are embedded txs, they must be listed in txHashes; if there are no txHashes, there must be no txs
|
|
36
|
+
const hashSet = new Set(block.txHashes.map(h => h.toString()));
|
|
37
|
+
const missingTxHashes =
|
|
38
|
+
embeddedTxCount > 0
|
|
39
|
+
? block.txs!.filter(tx => !hashSet.has(tx.getTxHash().toString())).map(tx => tx.getTxHash().toString())
|
|
40
|
+
: [];
|
|
41
|
+
if (embeddedTxCount > 0 && missingTxHashes.length > 0) {
|
|
42
|
+
this.logger.warn('Penalizing peer for embedded transaction(s) not included in txHashes', {
|
|
43
|
+
embeddedTxCount,
|
|
44
|
+
txHashesLength: block.txHashes.length,
|
|
45
|
+
missingTxHashes,
|
|
46
|
+
});
|
|
47
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
48
|
+
}
|
|
49
|
+
|
|
17
50
|
const { currentProposer, nextProposer, currentSlot, nextSlot } =
|
|
18
51
|
await this.epochCache.getProposerAttesterAddressInCurrentOrNextSlot();
|
|
19
52
|
|
|
20
53
|
// Check that the attestation is for the current or next slot
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
this.logger.debug(
|
|
24
|
-
`Penalizing peer for invalid slot number ${slotNumberBigInt}, current slot: ${currentSlot}, next slot: ${nextSlot}`,
|
|
25
|
-
);
|
|
54
|
+
const slotNumber = block.payload.header.slotNumber;
|
|
55
|
+
if (slotNumber !== currentSlot && slotNumber !== nextSlot) {
|
|
56
|
+
this.logger.debug(`Penalizing peer for invalid slot number ${slotNumber}`, { currentSlot, nextSlot });
|
|
26
57
|
return PeerErrorSeverity.HighToleranceError;
|
|
27
58
|
}
|
|
28
59
|
|
|
29
60
|
// Check that the block proposal is from the current or next proposer
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
if (slotNumber === currentSlot && currentProposer !== undefined && !proposer.equals(currentProposer)) {
|
|
62
|
+
this.logger.debug(`Penalizing peer for invalid proposer for current slot ${slotNumber}`, {
|
|
63
|
+
currentProposer,
|
|
64
|
+
nextProposer,
|
|
65
|
+
proposer: proposer.toString(),
|
|
66
|
+
});
|
|
67
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (slotNumber === nextSlot && nextProposer !== undefined && !proposer.equals(nextProposer)) {
|
|
71
|
+
this.logger.debug(`Penalizing peer for invalid proposer for next slot ${slotNumber}`, {
|
|
72
|
+
currentProposer,
|
|
73
|
+
nextProposer,
|
|
74
|
+
proposer: proposer.toString(),
|
|
75
|
+
});
|
|
76
|
+
return PeerErrorSeverity.MidToleranceError;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Validate tx hashes for all txs embedded in the proposal
|
|
80
|
+
if (!(await Promise.all(block.txs?.map(tx => tx.validateTxHash()) ?? [])).every(v => v)) {
|
|
81
|
+
this.logger.warn(`Penalizing peer for invalid tx hashes in block proposal`, {
|
|
82
|
+
proposer,
|
|
83
|
+
slotNumber,
|
|
84
|
+
});
|
|
85
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
41
86
|
}
|
|
42
87
|
|
|
43
88
|
return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Fr } from '@aztec/foundation/
|
|
1
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
2
|
import type { ArchiveSource } from '@aztec/p2p';
|
|
3
3
|
import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
|
|
4
4
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|