@aztec/p2p 3.0.0-rc.5 → 4.0.0-nightly.20260107
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/client/factory.d.ts +2 -2
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +2 -3
- package/dest/client/p2p_client.d.ts +2 -2
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +395 -21
- package/dest/config.d.ts +4 -7
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -9
- package/dest/mem_pools/instrumentation.d.ts +7 -1
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +29 -2
- 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 +28 -24
- 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 +261 -323
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +18 -0
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +56 -0
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +83 -0
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +5 -0
- package/dest/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.d.ts +15 -0
- package/dest/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.js +88 -0
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +17 -0
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +84 -0
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +19 -0
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +76 -0
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +26 -0
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +1 -0
- package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +84 -0
- package/dest/mem_pools/tx_pool/index.d.ts +1 -2
- package/dest/mem_pools/tx_pool/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/index.js +0 -1
- package/dest/mem_pools/tx_pool/priority.d.ts +5 -1
- package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/priority.js +6 -1
- package/dest/mem_pools/tx_pool/tx_pool.d.ts +8 -4
- 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 +25 -20
- package/dest/services/libp2p/libp2p_service.d.ts +4 -4
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +447 -64
- package/dest/services/peer-manager/metrics.d.ts +6 -1
- package/dest/services/peer-manager/metrics.d.ts.map +1 -1
- package/dest/services/peer-manager/metrics.js +17 -0
- package/dest/services/peer-manager/peer_manager.d.ts +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +385 -9
- package/dest/services/reqresp/protocols/tx.d.ts +2 -3
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +402 -24
- package/dest/services/tx_provider.d.ts +2 -1
- package/dest/services/tx_provider.d.ts.map +1 -1
- package/dest/services/tx_provider.js +11 -2
- package/dest/services/tx_provider_instrumentation.d.ts +5 -2
- package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
- package/dest/services/tx_provider_instrumentation.js +14 -1
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -2
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +1 -0
- package/package.json +14 -14
- package/src/client/factory.ts +5 -10
- package/src/client/p2p_client.ts +12 -17
- package/src/config.ts +8 -14
- package/src/mem_pools/instrumentation.ts +33 -0
- package/src/mem_pools/interface.ts +2 -4
- package/src/mem_pools/tx_pool/README.md +255 -0
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +308 -368
- package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +71 -0
- package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +93 -0
- package/src/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.ts +108 -0
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +104 -0
- package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +91 -0
- package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +106 -0
- package/src/mem_pools/tx_pool/index.ts +0 -1
- package/src/mem_pools/tx_pool/priority.ts +8 -1
- package/src/mem_pools/tx_pool/tx_pool.ts +8 -3
- package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +18 -13
- package/src/services/libp2p/libp2p_service.ts +12 -17
- package/src/services/peer-manager/metrics.ts +22 -0
- package/src/services/peer-manager/peer_manager.ts +2 -0
- package/src/services/reqresp/protocols/tx.ts +1 -2
- package/src/services/tx_provider.ts +17 -2
- package/src/services/tx_provider_instrumentation.ts +19 -2
- package/src/test-helpers/mock-pubsub.ts +1 -1
- package/src/test-helpers/reqresp-nodes.ts +1 -1
- package/src/testbench/p2p_client_testbench_worker.ts +2 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -81
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +0 -239
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +0 -285
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
3
|
+
import { BlockHeader, TxHash } from '@aztec/stdlib/tx';
|
|
4
|
+
import type { TxPoolOptions } from '../tx_pool.js';
|
|
5
|
+
import { type EvictionRule, type TxPoolOperations } from './eviction_strategy.js';
|
|
6
|
+
export declare class EvictionManager {
|
|
7
|
+
private txPool;
|
|
8
|
+
private log;
|
|
9
|
+
private rules;
|
|
10
|
+
constructor(txPool: TxPoolOperations, log?: import("@aztec/foundation/log").Logger);
|
|
11
|
+
evictAfterNewTxs(newTxs: TxHash[]): Promise<void>;
|
|
12
|
+
evictAfterNewBlock(block: BlockHeader, newNullifiers: Fr[], minedFeePayers: AztecAddress[]): Promise<void>;
|
|
13
|
+
evictAfterChainPrune(blockNumber: number): Promise<void>;
|
|
14
|
+
registerRule(rule: EvictionRule): void;
|
|
15
|
+
updateConfig(config: TxPoolOptions): void;
|
|
16
|
+
private runEvictionRules;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZpY3Rpb25fbWFuYWdlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL21lbV9wb29scy90eF9wb29sL2V2aWN0aW9uL2V2aWN0aW9uX21hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXBELE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFdkQsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBdUMsS0FBSyxZQUFZLEVBQUUsS0FBSyxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRXZILHFCQUFhLGVBQWU7SUFJeEIsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsR0FBRztJQUpiLE9BQU8sQ0FBQyxLQUFLLENBQXNCO0lBRW5DLFlBQ1UsTUFBTSxFQUFFLGdCQUFnQixFQUN4QixHQUFHLHlDQUF1RCxFQUNoRTtJQUVTLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBTTdEO0lBRVksa0JBQWtCLENBQzdCLEtBQUssRUFBRSxXQUFXLEVBQ2xCLGFBQWEsRUFBRSxFQUFFLEVBQUUsRUFDbkIsY0FBYyxFQUFFLFlBQVksRUFBRSxHQUM3QixPQUFPLENBQUMsSUFBSSxDQUFDLENBU2Y7SUFFWSxvQkFBb0IsQ0FBQyxXQUFXLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FNcEU7SUFFTSxZQUFZLENBQUMsSUFBSSxFQUFFLFlBQVksUUFFckM7SUFFTSxZQUFZLENBQUMsTUFBTSxFQUFFLGFBQWEsR0FBRyxJQUFJLENBSS9DO1lBRWEsZ0JBQWdCO0NBYS9CIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eviction_manager.d.ts","sourceRoot":"","sources":["../../../../src/mem_pools/tx_pool/eviction/eviction_manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAuC,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEvH,qBAAa,eAAe;IAIxB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,GAAG;IAJb,OAAO,CAAC,KAAK,CAAsB;IAEnC,YACU,MAAM,EAAE,gBAAgB,EACxB,GAAG,yCAAuD,EAChE;IAES,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7D;IAEY,kBAAkB,CAC7B,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,EAAE,EAAE,EACnB,cAAc,EAAE,YAAY,EAAE,GAC7B,OAAO,CAAC,IAAI,CAAC,CASf;IAEY,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMpE;IAEM,YAAY,CAAC,IAAI,EAAE,YAAY,QAErC;IAEM,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAI/C;YAEa,gBAAgB;CAa/B"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { EvictionEvent } from './eviction_strategy.js';
|
|
3
|
+
export class EvictionManager {
|
|
4
|
+
txPool;
|
|
5
|
+
log;
|
|
6
|
+
rules;
|
|
7
|
+
constructor(txPool, log = createLogger('p2p:mempool:tx_pool:eviction_manager')){
|
|
8
|
+
this.txPool = txPool;
|
|
9
|
+
this.log = log;
|
|
10
|
+
this.rules = [];
|
|
11
|
+
}
|
|
12
|
+
async evictAfterNewTxs(newTxs) {
|
|
13
|
+
const ctx = {
|
|
14
|
+
event: EvictionEvent.TXS_ADDED,
|
|
15
|
+
newTxs
|
|
16
|
+
};
|
|
17
|
+
await this.runEvictionRules(ctx);
|
|
18
|
+
}
|
|
19
|
+
async evictAfterNewBlock(block, newNullifiers, minedFeePayers) {
|
|
20
|
+
const ctx = {
|
|
21
|
+
event: EvictionEvent.BLOCK_MINED,
|
|
22
|
+
block,
|
|
23
|
+
newNullifiers,
|
|
24
|
+
minedFeePayers
|
|
25
|
+
};
|
|
26
|
+
await this.runEvictionRules(ctx);
|
|
27
|
+
}
|
|
28
|
+
async evictAfterChainPrune(blockNumber) {
|
|
29
|
+
const ctx = {
|
|
30
|
+
event: EvictionEvent.CHAIN_PRUNED,
|
|
31
|
+
blockNumber
|
|
32
|
+
};
|
|
33
|
+
await this.runEvictionRules(ctx);
|
|
34
|
+
}
|
|
35
|
+
registerRule(rule) {
|
|
36
|
+
this.rules.push(rule);
|
|
37
|
+
}
|
|
38
|
+
updateConfig(config) {
|
|
39
|
+
for (const rule of this.rules){
|
|
40
|
+
rule.updateConfig(config);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async runEvictionRules(ctx) {
|
|
44
|
+
for (const rule of this.rules){
|
|
45
|
+
try {
|
|
46
|
+
await rule.evict(ctx, this.txPool);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
this.log.warn(`Eviction rule ${rule.name} unexpected error: ${String(err)}`, {
|
|
49
|
+
err,
|
|
50
|
+
evictionRule: rule.name,
|
|
51
|
+
evictionEvent: ctx.event
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
3
|
+
import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
|
|
4
|
+
import type { TxPoolOptions } from '../tx_pool.js';
|
|
5
|
+
export declare const EvictionEvent: {
|
|
6
|
+
readonly TXS_ADDED: "txs_added";
|
|
7
|
+
readonly BLOCK_MINED: "block_mined";
|
|
8
|
+
readonly CHAIN_PRUNED: "chain_pruned";
|
|
9
|
+
};
|
|
10
|
+
type EvictionEvent = (typeof EvictionEvent)[keyof typeof EvictionEvent];
|
|
11
|
+
export type EvictionContext = {
|
|
12
|
+
event: typeof EvictionEvent.TXS_ADDED;
|
|
13
|
+
newTxs: TxHash[];
|
|
14
|
+
} | {
|
|
15
|
+
event: typeof EvictionEvent.CHAIN_PRUNED;
|
|
16
|
+
blockNumber: number;
|
|
17
|
+
} | {
|
|
18
|
+
event: typeof EvictionEvent.BLOCK_MINED;
|
|
19
|
+
block: BlockHeader;
|
|
20
|
+
newNullifiers: Fr[];
|
|
21
|
+
minedFeePayers: AztecAddress[];
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Result of an eviction operation
|
|
25
|
+
*/
|
|
26
|
+
export interface EvictionResult {
|
|
27
|
+
readonly txsEvicted: TxHash[];
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
readonly success: boolean;
|
|
30
|
+
readonly error?: Error;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Information about a pending transaction
|
|
34
|
+
*/
|
|
35
|
+
export interface PendingTxInfo {
|
|
36
|
+
txHash: TxHash;
|
|
37
|
+
blockHash: Fr;
|
|
38
|
+
isEvictable: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Information about a transaction that references a specific block
|
|
42
|
+
*/
|
|
43
|
+
export interface TxBlockReference {
|
|
44
|
+
txHash: TxHash;
|
|
45
|
+
blockHash: Fr;
|
|
46
|
+
isEvictable: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Operations that eviction strategies can perform on the pool
|
|
50
|
+
*/
|
|
51
|
+
export interface TxPoolOperations {
|
|
52
|
+
getTxByHash(txHash: TxHash): Promise<Tx | undefined>;
|
|
53
|
+
getPendingTxInfos(): Promise<PendingTxInfo[]>;
|
|
54
|
+
getPendingTxsReferencingBlocks(blockHashes: Fr[]): Promise<TxBlockReference[]>;
|
|
55
|
+
getPendingTxsWithFeePayer(feePayer: AztecAddress[]): Promise<PendingTxInfo[]>;
|
|
56
|
+
/** Cheap count of current pending transactions. */
|
|
57
|
+
getPendingTxCount(): Promise<number>;
|
|
58
|
+
/**
|
|
59
|
+
* Returns up to `limit` lowest-priority evictable pending tx hashes.
|
|
60
|
+
* Ordering should be from lowest priority upwards.
|
|
61
|
+
*/
|
|
62
|
+
getLowestPriorityEvictable(limit: number): Promise<TxHash[]>;
|
|
63
|
+
deleteTxs(txHashes: TxHash[], opts?: {
|
|
64
|
+
permanently?: boolean;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Strategy interface for different eviction behaviors
|
|
69
|
+
*/
|
|
70
|
+
export interface EvictionRule {
|
|
71
|
+
readonly name: string;
|
|
72
|
+
/**
|
|
73
|
+
* Performs the eviction logic
|
|
74
|
+
*/
|
|
75
|
+
evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Updates the configuration for this eviction rule.
|
|
78
|
+
* Rules should ignore config options that don't apply to them.
|
|
79
|
+
*/
|
|
80
|
+
updateConfig(config: TxPoolOptions): void;
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZpY3Rpb25fc3RyYXRlZ3kuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9tZW1fcG9vbHMvdHhfcG9vbC9ldmljdGlvbi9ldmljdGlvbl9zdHJhdGVneS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDcEQsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVoRSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFbkQsZUFBTyxNQUFNLGFBQWE7Ozs7Q0FJaEIsQ0FBQztBQUVYLEtBQUssYUFBYSxHQUFHLENBQUMsT0FBTyxhQUFhLENBQUMsQ0FBQyxNQUFNLE9BQU8sYUFBYSxDQUFDLENBQUM7QUFFeEUsTUFBTSxNQUFNLGVBQWUsR0FDdkI7SUFDRSxLQUFLLEVBQUUsT0FBTyxhQUFhLENBQUMsU0FBUyxDQUFDO0lBQ3RDLE1BQU0sRUFBRSxNQUFNLEVBQUUsQ0FBQztDQUNsQixHQUNEO0lBQ0UsS0FBSyxFQUFFLE9BQU8sYUFBYSxDQUFDLFlBQVksQ0FBQztJQUN6QyxXQUFXLEVBQUUsTUFBTSxDQUFDO0NBQ3JCLEdBQ0Q7SUFDRSxLQUFLLEVBQUUsT0FBTyxhQUFhLENBQUMsV0FBVyxDQUFDO0lBQ3hDLEtBQUssRUFBRSxXQUFXLENBQUM7SUFDbkIsYUFBYSxFQUFFLEVBQUUsRUFBRSxDQUFDO0lBQ3BCLGNBQWMsRUFBRSxZQUFZLEVBQUUsQ0FBQztDQUNoQyxDQUFDO0FBRU47O0dBRUc7QUFDSCxNQUFNLFdBQVcsY0FBYztJQUM3QixRQUFRLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQzlCLFFBQVEsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDO0lBQ3hCLFFBQVEsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDO0lBQzFCLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxLQUFLLENBQUM7Q0FDeEI7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyxhQUFhO0lBQzVCLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFDZixTQUFTLEVBQUUsRUFBRSxDQUFDO0lBQ2QsV0FBVyxFQUFFLE9BQU8sQ0FBQztDQUN0QjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGdCQUFnQjtJQUMvQixNQUFNLEVBQUUsTUFBTSxDQUFDO0lBQ2YsU0FBUyxFQUFFLEVBQUUsQ0FBQztJQUNkLFdBQVcsRUFBRSxPQUFPLENBQUM7Q0FDdEI7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyxnQkFBZ0I7SUFDL0IsV0FBVyxDQUFDLE1BQU0sRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLEVBQUUsR0FBRyxTQUFTLENBQUMsQ0FBQztJQUNyRCxpQkFBaUIsSUFBSSxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUMsQ0FBQztJQUM5Qyw4QkFBOEIsQ0FBQyxXQUFXLEVBQUUsRUFBRSxFQUFFLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixFQUFFLENBQUMsQ0FBQztJQUMvRSx5QkFBeUIsQ0FBQyxRQUFRLEVBQUUsWUFBWSxFQUFFLEdBQUcsT0FBTyxDQUFDLGFBQWEsRUFBRSxDQUFDLENBQUM7SUFDOUUsbURBQW1EO0lBQ25ELGlCQUFpQixJQUFJLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNyQzs7O09BR0c7SUFDSCwwQkFBMEIsQ0FBQyxLQUFLLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzdELFNBQVMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUU7UUFBRSxXQUFXLENBQUMsRUFBRSxPQUFPLENBQUE7S0FBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztDQUNoRjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLFlBQVk7SUFDM0IsUUFBUSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUM7SUFFdEI7O09BRUc7SUFDSCxLQUFLLENBQUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBRW5GOzs7T0FHRztJQUNILFlBQVksQ0FBQyxNQUFNLEVBQUUsYUFBYSxHQUFHLElBQUksQ0FBQztDQUMzQyJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eviction_strategy.d.ts","sourceRoot":"","sources":["../../../../src/mem_pools/tx_pool/eviction/eviction_strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX,KAAK,aAAa,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAExE,MAAM,MAAM,eAAe,GACvB;IACE,KAAK,EAAE,OAAO,aAAa,CAAC,SAAS,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,GACD;IACE,KAAK,EAAE,OAAO,aAAa,CAAC,YAAY,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,KAAK,EAAE,OAAO,aAAa,CAAC,WAAW,CAAC;IACxC,KAAK,EAAE,WAAW,CAAC;IACnB,aAAa,EAAE,EAAE,EAAE,CAAC;IACpB,cAAc,EAAE,YAAY,EAAE,CAAC;CAChC,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,EAAE,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,EAAE,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC;IACrD,iBAAiB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC9C,8BAA8B,CAAC,WAAW,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC/E,yBAAyB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC9E,mDAAmD;IACnD,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAEnF;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CAC3C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReadonlyWorldStateAccess } from '@aztec/stdlib/interfaces/server';
|
|
2
|
+
import type { TxPoolOptions } from '../tx_pool.js';
|
|
3
|
+
import { type EvictionContext, type EvictionResult, type EvictionRule, type TxPoolOperations } from './eviction_strategy.js';
|
|
4
|
+
export declare class InsufficientFeePayerBalanceRule implements EvictionRule {
|
|
5
|
+
private worldState;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly reason: string;
|
|
8
|
+
constructor(worldState: ReadonlyWorldStateAccess);
|
|
9
|
+
private log;
|
|
10
|
+
evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult>;
|
|
11
|
+
private evictTxs;
|
|
12
|
+
private createGasTxValidator;
|
|
13
|
+
updateConfig(_config: TxPoolOptions): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdWZmaWNpZW50X2ZlZV9wYXllcl9iYWxhbmNlX3J1bGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9tZW1fcG9vbHMvdHhfcG9vbC9ldmljdGlvbi9pbnN1ZmZpY2llbnRfZmVlX3BheWVyX2JhbGFuY2VfcnVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHQSxPQUFPLEtBQUssRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBS2hGLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNuRCxPQUFPLEVBQ0wsS0FBSyxlQUFlLEVBRXBCLEtBQUssY0FBYyxFQUNuQixLQUFLLFlBQVksRUFFakIsS0FBSyxnQkFBZ0IsRUFDdEIsTUFBTSx3QkFBd0IsQ0FBQztBQUVoQyxxQkFBYSwrQkFBZ0MsWUFBVyxZQUFZO0lBSS9DLE9BQU8sQ0FBQyxVQUFVO0lBSHJDLFNBQWdCLElBQUksU0FBaUM7SUFDckQsU0FBZ0IsTUFBTSxTQUE0QjtJQUVsRCxZQUEyQixVQUFVLEVBQUUsd0JBQXdCLEVBQUk7SUFFbkUsT0FBTyxDQUFDLEdBQUcsQ0FBMkU7SUFFaEYsS0FBSyxDQUFDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxFQUFFLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FrQ3ZGO1lBRWEsUUFBUTtJQXdDdEIsT0FBTyxDQUFDLG9CQUFvQjtJQUk1QixZQUFZLENBQUMsT0FBTyxFQUFFLGFBQWEsR0FBRyxJQUFJLENBQUc7Q0FDOUMifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insufficient_fee_payer_balance_rule.d.ts","sourceRoot":"","sources":["../../../../src/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAKhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,cAAc,EACnB,KAAK,YAAY,EAEjB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,+BAAgC,YAAW,YAAY;IAI/C,OAAO,CAAC,UAAU;IAHrC,SAAgB,IAAI,SAAiC;IACrD,SAAgB,MAAM,SAA4B;IAElD,YAA2B,UAAU,EAAE,wBAAwB,EAAI;IAEnE,OAAO,CAAC,GAAG,CAA2E;IAEhF,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAkCvF;YAEa,QAAQ;IAwCtB,OAAO,CAAC,oBAAoB;IAI5B,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAG;CAC9C"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
3
|
+
import { GasFees } from '@aztec/stdlib/gas';
|
|
4
|
+
import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
|
|
5
|
+
import { GasTxValidator } from '../../../msg_validators/index.js';
|
|
6
|
+
import { EvictionEvent } from './eviction_strategy.js';
|
|
7
|
+
export class InsufficientFeePayerBalanceRule {
|
|
8
|
+
worldState;
|
|
9
|
+
name;
|
|
10
|
+
reason;
|
|
11
|
+
constructor(worldState){
|
|
12
|
+
this.worldState = worldState;
|
|
13
|
+
this.name = 'InsufficientFeePayerBalance';
|
|
14
|
+
this.reason = 'insufficient_fee_juice';
|
|
15
|
+
this.log = createLogger('p2p:mempool:tx_pool:insufficient_fee_payer_balance_rule');
|
|
16
|
+
}
|
|
17
|
+
log;
|
|
18
|
+
async evict(context, txPool) {
|
|
19
|
+
try {
|
|
20
|
+
if (context.event === EvictionEvent.CHAIN_PRUNED) {
|
|
21
|
+
const affectedTxs = await txPool.getPendingTxInfos();
|
|
22
|
+
return this.evictTxs(affectedTxs, context.blockNumber, txPool);
|
|
23
|
+
}
|
|
24
|
+
if (context.event === EvictionEvent.BLOCK_MINED) {
|
|
25
|
+
const affectedTxs = await txPool.getPendingTxsWithFeePayer(context.minedFeePayers);
|
|
26
|
+
// TODO: fix this edge-case
|
|
27
|
+
// This can lead to a race condition if we are catching up in the p2p client.
|
|
28
|
+
// Let's say we have 3 txs for the same fee payer, which get mined in blocks 1, 2, 3.
|
|
29
|
+
// Tx1 consumes fee juice, tx2 increases it, tx3 consumes it again. We see block1 with tx1 first, run this rule, and evict tx3.
|
|
30
|
+
// But tx3 was valid (due to tx2) and mined on block3. And we have just removed from the mempool a tx we needed for proving/reexec.
|
|
31
|
+
// -----
|
|
32
|
+
// Proposed fix: evict only if node is synched
|
|
33
|
+
return this.evictTxs(affectedTxs, context.block.getBlockNumber(), txPool);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
reason: this.reason,
|
|
37
|
+
success: true,
|
|
38
|
+
txsEvicted: []
|
|
39
|
+
};
|
|
40
|
+
} catch (err) {
|
|
41
|
+
this.log.error('Failed to evict invalid transactions after mining', {
|
|
42
|
+
err
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
reason: this.reason,
|
|
46
|
+
success: false,
|
|
47
|
+
txsEvicted: [],
|
|
48
|
+
error: new Error('Failed to evict invalid txs after mining', {
|
|
49
|
+
cause: err
|
|
50
|
+
})
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async evictTxs(candidateTxs, blockNumber, txPool) {
|
|
55
|
+
const txsToEvict = [];
|
|
56
|
+
const gasValidator = this.createGasTxValidator(this.worldState.getSnapshot(blockNumber));
|
|
57
|
+
for (const { txHash, isEvictable } of candidateTxs){
|
|
58
|
+
if (!isEvictable) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const tx = await txPool.getTxByHash(txHash);
|
|
62
|
+
if (!tx) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
this.log.debug(`Validating tx balance ${txHash}`);
|
|
66
|
+
if ((await gasValidator.validateTxFee(tx)).result === 'invalid') {
|
|
67
|
+
this.log.verbose(`Evicting tx ${txHash} from pool due to an insufficient fee payer balance`);
|
|
68
|
+
txsToEvict.push(txHash);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (txsToEvict.length > 0) {
|
|
73
|
+
await txPool.deleteTxs(txsToEvict);
|
|
74
|
+
}
|
|
75
|
+
this.log.verbose(`Evicted ${txsToEvict.length} invalid txs after block mined`, {
|
|
76
|
+
txsToEvict
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
reason: this.reason,
|
|
80
|
+
success: true,
|
|
81
|
+
txsEvicted: txsToEvict
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
createGasTxValidator(db) {
|
|
85
|
+
return new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, GasFees.empty());
|
|
86
|
+
}
|
|
87
|
+
updateConfig(_config) {}
|
|
88
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TxPoolOptions } from '../tx_pool.js';
|
|
2
|
+
import { type EvictionContext, type EvictionResult, type EvictionRule, type TxPoolOperations } from './eviction_strategy.js';
|
|
3
|
+
/**
|
|
4
|
+
* Eviction rule that removes invalid transactions after a block is mined.
|
|
5
|
+
* Only triggers on BLOCK_MINED events.
|
|
6
|
+
*
|
|
7
|
+
* Eviction criteria includes:
|
|
8
|
+
* - Transactions with nullifiers that are already included in the mined block
|
|
9
|
+
* - Transactions with an expiration timestamp less than or equal to the mined block timestamp
|
|
10
|
+
*/
|
|
11
|
+
export declare class InvalidTxsAfterMiningRule implements EvictionRule {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
private log;
|
|
14
|
+
evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult>;
|
|
15
|
+
updateConfig(_config: TxPoolOptions): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW52YWxpZF90eHNfYWZ0ZXJfbWluaW5nX3J1bGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9tZW1fcG9vbHMvdHhfcG9vbC9ldmljdGlvbi9pbnZhbGlkX3R4c19hZnRlcl9taW5pbmdfcnVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHQSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUNMLEtBQUssZUFBZSxFQUVwQixLQUFLLGNBQWMsRUFDbkIsS0FBSyxZQUFZLEVBQ2pCLEtBQUssZ0JBQWdCLEVBQ3RCLE1BQU0sd0JBQXdCLENBQUM7QUFFaEM7Ozs7Ozs7R0FPRztBQUNILHFCQUFhLHlCQUEwQixZQUFXLFlBQVk7SUFDNUQsU0FBZ0IsSUFBSSxTQUEyQjtJQUUvQyxPQUFPLENBQUMsR0FBRyxDQUFxRTtJQUUxRSxLQUFLLENBQUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQTJFdkY7SUFFRCxZQUFZLENBQUMsT0FBTyxFQUFFLGFBQWEsR0FBRyxJQUFJLENBQUc7Q0FDOUMifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalid_txs_after_mining_rule.d.ts","sourceRoot":"","sources":["../../../../src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;GAOG;AACH,qBAAa,yBAA0B,YAAW,YAAY;IAC5D,SAAgB,IAAI,SAA2B;IAE/C,OAAO,CAAC,GAAG,CAAqE;IAE1E,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA2EvF;IAED,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAG;CAC9C"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { EvictionEvent } from './eviction_strategy.js';
|
|
3
|
+
/**
|
|
4
|
+
* Eviction rule that removes invalid transactions after a block is mined.
|
|
5
|
+
* Only triggers on BLOCK_MINED events.
|
|
6
|
+
*
|
|
7
|
+
* Eviction criteria includes:
|
|
8
|
+
* - Transactions with nullifiers that are already included in the mined block
|
|
9
|
+
* - Transactions with an expiration timestamp less than or equal to the mined block timestamp
|
|
10
|
+
*/ export class InvalidTxsAfterMiningRule {
|
|
11
|
+
name = 'InvalidTxsAfterMining';
|
|
12
|
+
log = createLogger('p2p:mempool:tx_pool:invalid_txs_after_mining_rule');
|
|
13
|
+
async evict(context, txPool) {
|
|
14
|
+
if (context.event !== EvictionEvent.BLOCK_MINED) {
|
|
15
|
+
return {
|
|
16
|
+
reason: 'block_mined_invalid_txs',
|
|
17
|
+
success: true,
|
|
18
|
+
txsEvicted: []
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (!context.block || !context.newNullifiers) {
|
|
22
|
+
this.log.warn('Invalid context for block mined eviction', {
|
|
23
|
+
context
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
reason: 'block_mined_invalid_txs',
|
|
27
|
+
success: false,
|
|
28
|
+
txsEvicted: [],
|
|
29
|
+
error: new Error('Invalid block mined context')
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const { timestamp } = context.block.globalVariables;
|
|
34
|
+
const txsToEvict = [];
|
|
35
|
+
const pendingTxs = await txPool.getPendingTxInfos();
|
|
36
|
+
const minedNullifiers = new Set(context.newNullifiers.map((n)=>n.toString()));
|
|
37
|
+
for (const { txHash, isEvictable } of pendingTxs){
|
|
38
|
+
if (!isEvictable) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const tx = await txPool.getTxByHash(txHash);
|
|
42
|
+
if (!tx) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Evict pending txs that share nullifiers with mined txs
|
|
46
|
+
const txNullifiers = tx.data.getNonEmptyNullifiers();
|
|
47
|
+
if (txNullifiers.some((nullifier)=>minedNullifiers.has(nullifier.toString()))) {
|
|
48
|
+
this.log.verbose(`Evicting tx ${txHash} from pool due to a duplicate nullifier with a mined tx`);
|
|
49
|
+
txsToEvict.push(txHash);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// Evict pending txs with an expiration timestamp less than or equal to the mined block timestamp
|
|
53
|
+
const includeByTimestamp = tx.data.includeByTimestamp;
|
|
54
|
+
if (includeByTimestamp <= timestamp) {
|
|
55
|
+
this.log.verbose(`Evicting tx ${txHash} from pool due to the tx being expired (includeByTimestamp: ${includeByTimestamp}, mined block timestamp: ${timestamp})`);
|
|
56
|
+
txsToEvict.push(txHash);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (txsToEvict.length > 0) {
|
|
61
|
+
await txPool.deleteTxs(txsToEvict);
|
|
62
|
+
}
|
|
63
|
+
this.log.debug(`Evicted ${txsToEvict.length} invalid txs after block mined`);
|
|
64
|
+
return {
|
|
65
|
+
reason: 'block_mined_invalid_txs',
|
|
66
|
+
success: true,
|
|
67
|
+
txsEvicted: txsToEvict
|
|
68
|
+
};
|
|
69
|
+
} catch (err) {
|
|
70
|
+
this.log.error('Failed to evict invalid transactions after mining', {
|
|
71
|
+
err
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
reason: 'block_mined_invalid_txs',
|
|
75
|
+
success: false,
|
|
76
|
+
txsEvicted: [],
|
|
77
|
+
error: new Error('Failed to evict invalid txs after mining', {
|
|
78
|
+
cause: err
|
|
79
|
+
})
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
updateConfig(_config) {}
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReadonlyWorldStateAccess } from '@aztec/stdlib/interfaces/server';
|
|
2
|
+
import type { TxPoolOptions } from '../tx_pool.js';
|
|
3
|
+
import { type EvictionContext, type EvictionResult, type EvictionRule, type TxPoolOperations } from './eviction_strategy.js';
|
|
4
|
+
/**
|
|
5
|
+
* Eviction rule that removes invalid transactions after a blockchain reorganization.
|
|
6
|
+
* Only triggers on CHAIN_PRUNED events.
|
|
7
|
+
*
|
|
8
|
+
* Eviction criteria includes:
|
|
9
|
+
* - Transactions that reference pruned block hashes (invalid by definition)
|
|
10
|
+
*/
|
|
11
|
+
export declare class InvalidTxsAfterReorgRule implements EvictionRule {
|
|
12
|
+
private worldState;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
private log;
|
|
15
|
+
constructor(worldState: ReadonlyWorldStateAccess);
|
|
16
|
+
evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult>;
|
|
17
|
+
updateConfig(_config: TxPoolOptions): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW52YWxpZF90eHNfYWZ0ZXJfcmVvcmdfcnVsZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL21lbV9wb29scy90eF9wb29sL2V2aWN0aW9uL2ludmFsaWRfdHhzX2FmdGVyX3Jlb3JnX3J1bGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUloRixPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUNMLEtBQUssZUFBZSxFQUVwQixLQUFLLGNBQWMsRUFDbkIsS0FBSyxZQUFZLEVBQ2pCLEtBQUssZ0JBQWdCLEVBQ3RCLE1BQU0sd0JBQXdCLENBQUM7QUFFaEM7Ozs7OztHQU1HO0FBQ0gscUJBQWEsd0JBQXlCLFlBQVcsWUFBWTtJQUt4QyxPQUFPLENBQUMsVUFBVTtJQUpyQyxTQUFnQixJQUFJLFNBQTBCO0lBRTlDLE9BQU8sQ0FBQyxHQUFHLENBQW9FO0lBRS9FLFlBQTJCLFVBQVUsRUFBRSx3QkFBd0IsRUFBSTtJQUU3RCxLQUFLLENBQUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQXlEdkY7SUFFRCxZQUFZLENBQUMsT0FBTyxFQUFFLGFBQWEsR0FBRyxJQUFJLENBQUc7Q0FDOUMifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalid_txs_after_reorg_rule.d.ts","sourceRoot":"","sources":["../../../../src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAIhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;GAMG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAKxC,OAAO,CAAC,UAAU;IAJrC,SAAgB,IAAI,SAA0B;IAE9C,OAAO,CAAC,GAAG,CAAoE;IAE/E,YAA2B,UAAU,EAAE,wBAAwB,EAAI;IAE7D,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAyDvF;IAED,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAG;CAC9C"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { findIndexInSortedArray, insertIntoSortedArray } from '@aztec/foundation/array';
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
5
|
+
import { EvictionEvent } from './eviction_strategy.js';
|
|
6
|
+
/**
|
|
7
|
+
* Eviction rule that removes invalid transactions after a blockchain reorganization.
|
|
8
|
+
* Only triggers on CHAIN_PRUNED events.
|
|
9
|
+
*
|
|
10
|
+
* Eviction criteria includes:
|
|
11
|
+
* - Transactions that reference pruned block hashes (invalid by definition)
|
|
12
|
+
*/ export class InvalidTxsAfterReorgRule {
|
|
13
|
+
worldState;
|
|
14
|
+
name;
|
|
15
|
+
log;
|
|
16
|
+
constructor(worldState){
|
|
17
|
+
this.worldState = worldState;
|
|
18
|
+
this.name = 'InvalidTxsAfterReorg';
|
|
19
|
+
this.log = createLogger('p2p:mempool:tx_pool:invalid_txs_after_reorg_rule');
|
|
20
|
+
}
|
|
21
|
+
async evict(context, txPool) {
|
|
22
|
+
if (context.event !== EvictionEvent.CHAIN_PRUNED) {
|
|
23
|
+
return {
|
|
24
|
+
reason: 'reorg_invalid_txs',
|
|
25
|
+
success: true,
|
|
26
|
+
txsEvicted: []
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const candidateTxs = (await txPool.getPendingTxInfos()).filter(({ isEvictable })=>isEvictable);
|
|
31
|
+
// Deduplicate block hashes to reduce redundant DB lookups (many txs may share the same blockHash).
|
|
32
|
+
const uniqueBlockHashes = [];
|
|
33
|
+
candidateTxs.forEach(({ blockHash })=>{
|
|
34
|
+
insertIntoSortedArray(uniqueBlockHashes, blockHash, Fr.cmp, false);
|
|
35
|
+
});
|
|
36
|
+
const db = this.worldState.getSnapshot(context.blockNumber);
|
|
37
|
+
const blocksFromDb = await db.findLeafIndices(MerkleTreeId.ARCHIVE, uniqueBlockHashes);
|
|
38
|
+
// Identify txs whose blockHash is not found in the archive (pruned)
|
|
39
|
+
const txsToEvict = [];
|
|
40
|
+
for (const tx of candidateTxs){
|
|
41
|
+
const idx = findIndexInSortedArray(uniqueBlockHashes, tx.blockHash, Fr.cmp);
|
|
42
|
+
const blockPruned = idx === -1 || blocksFromDb[idx] === undefined;
|
|
43
|
+
if (blockPruned) {
|
|
44
|
+
txsToEvict.push(tx.txHash);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (txsToEvict.length > 0) {
|
|
48
|
+
this.log.verbose(`Evicting ${txsToEvict.length} txs from pool due to referencing pruned blocks`);
|
|
49
|
+
await txPool.deleteTxs(txsToEvict);
|
|
50
|
+
}
|
|
51
|
+
const keptCount = candidateTxs.length - txsToEvict.length;
|
|
52
|
+
if (keptCount > 0) {
|
|
53
|
+
this.log.verbose(`Kept ${keptCount} txs that did not reference pruned blocks`);
|
|
54
|
+
}
|
|
55
|
+
this.log.debug(`Evicted ${txsToEvict.length} invalid txs after reorg`);
|
|
56
|
+
return {
|
|
57
|
+
reason: 'reorg_invalid_txs',
|
|
58
|
+
success: true,
|
|
59
|
+
txsEvicted: txsToEvict
|
|
60
|
+
};
|
|
61
|
+
} catch (err) {
|
|
62
|
+
this.log.error('Failed to evict invalid transactions after reorg', {
|
|
63
|
+
err
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
reason: 'reorg_invalid_txs',
|
|
67
|
+
success: false,
|
|
68
|
+
txsEvicted: [],
|
|
69
|
+
error: new Error('Failed to evict invalid txs after reorg', {
|
|
70
|
+
cause: err
|
|
71
|
+
})
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
updateConfig(_config) {}
|
|
76
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TxPoolOptions } from '../tx_pool.js';
|
|
2
|
+
import { type EvictionContext, type EvictionResult, type EvictionRule, type TxPoolOperations } from './eviction_strategy.js';
|
|
3
|
+
export interface LowPriorityEvictionConfig {
|
|
4
|
+
/** Maximum number of pending transactions before eviction kicks in */
|
|
5
|
+
maxPoolSize: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Eviction rule that removes low-priority transactions when the number of pending transactions exceeds configured limits.
|
|
9
|
+
* Only triggers on TXS_ADDED events and respects non-evictable transactions.
|
|
10
|
+
*/
|
|
11
|
+
export declare class LowPriorityEvictionRule implements EvictionRule {
|
|
12
|
+
private config;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
private log;
|
|
15
|
+
constructor(config: LowPriorityEvictionConfig);
|
|
16
|
+
evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Updates the configuration for this eviction rule
|
|
19
|
+
*/
|
|
20
|
+
updateConfig(config: TxPoolOptions): void;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the current configuration
|
|
23
|
+
*/
|
|
24
|
+
getConfig(): LowPriorityEvictionConfig;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG93X3ByaW9yaXR5X2V2aWN0aW9uX3J1bGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9tZW1fcG9vbHMvdHhfcG9vbC9ldmljdGlvbi9sb3dfcHJpb3JpdHlfZXZpY3Rpb25fcnVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHQSxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbkQsT0FBTyxFQUNMLEtBQUssZUFBZSxFQUVwQixLQUFLLGNBQWMsRUFDbkIsS0FBSyxZQUFZLEVBQ2pCLEtBQUssZ0JBQWdCLEVBQ3RCLE1BQU0sd0JBQXdCLENBQUM7QUFFaEMsTUFBTSxXQUFXLHlCQUF5QjtJQUN4QyxzRUFBc0U7SUFDdEUsV0FBVyxFQUFFLE1BQU0sQ0FBQztDQUNyQjtBQUVEOzs7R0FHRztBQUNILHFCQUFhLHVCQUF3QixZQUFXLFlBQVk7SUFLOUMsT0FBTyxDQUFDLE1BQU07SUFKMUIsU0FBZ0IsSUFBSSxTQUF5QjtJQUU3QyxPQUFPLENBQUMsR0FBRyxDQUFrRTtJQUU3RSxZQUFvQixNQUFNLEVBQUUseUJBQXlCLEVBQUk7SUFFNUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxFQUFFLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0E0RDlGO0lBRUQ7O09BRUc7SUFDSCxZQUFZLENBQUMsTUFBTSxFQUFFLGFBQWEsR0FBRyxJQUFJLENBSXhDO0lBRUQ7O09BRUc7SUFDSCxTQUFTLElBQUkseUJBQXlCLENBRXJDO0NBQ0YifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"low_priority_eviction_rule.d.ts","sourceRoot":"","sources":["../../../../src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,yBAAyB;IACxC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,YAAY;IAK9C,OAAO,CAAC,MAAM;IAJ1B,SAAgB,IAAI,SAAyB;IAE7C,OAAO,CAAC,GAAG,CAAkE;IAE7E,YAAoB,MAAM,EAAE,yBAAyB,EAAI;IAE5C,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4D9F;IAED;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAIxC;IAED;;OAEG;IACH,SAAS,IAAI,yBAAyB,CAErC;CACF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { EvictionEvent } from './eviction_strategy.js';
|
|
3
|
+
/**
|
|
4
|
+
* Eviction rule that removes low-priority transactions when the number of pending transactions exceeds configured limits.
|
|
5
|
+
* Only triggers on TXS_ADDED events and respects non-evictable transactions.
|
|
6
|
+
*/ export class LowPriorityEvictionRule {
|
|
7
|
+
config;
|
|
8
|
+
name;
|
|
9
|
+
log;
|
|
10
|
+
constructor(config){
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.name = 'LowPriorityEviction';
|
|
13
|
+
this.log = createLogger('p2p:mempool:tx_pool:low_priority_eviction_rule');
|
|
14
|
+
}
|
|
15
|
+
async evict(context, txPool) {
|
|
16
|
+
if (context.event !== EvictionEvent.TXS_ADDED) {
|
|
17
|
+
return {
|
|
18
|
+
reason: 'low_priority',
|
|
19
|
+
success: true,
|
|
20
|
+
txsEvicted: []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (this.config.maxPoolSize === 0) {
|
|
24
|
+
return {
|
|
25
|
+
reason: 'low_priority',
|
|
26
|
+
success: true,
|
|
27
|
+
txsEvicted: []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const currentTxCount = await txPool.getPendingTxCount();
|
|
32
|
+
const maxCount = this.config.maxPoolSize;
|
|
33
|
+
if (currentTxCount <= maxCount) {
|
|
34
|
+
this.log.trace(`Not evicting low priority txs. Pending tx count below limit ${currentTxCount} <= ${maxCount}`);
|
|
35
|
+
return {
|
|
36
|
+
reason: 'low_priority',
|
|
37
|
+
success: true,
|
|
38
|
+
txsEvicted: []
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
this.log.verbose(`Evicting low priority txs. Pending tx count above limit: ${currentTxCount} > ${maxCount}`);
|
|
42
|
+
const numberToEvict = currentTxCount - maxCount;
|
|
43
|
+
const txsToEvict = await txPool.getLowestPriorityEvictable(numberToEvict);
|
|
44
|
+
if (txsToEvict.length > 0) {
|
|
45
|
+
await txPool.deleteTxs(txsToEvict);
|
|
46
|
+
}
|
|
47
|
+
const numNewTxsEvicted = context.newTxs.filter((newTxHash)=>txsToEvict.some((evictedTx)=>evictedTx.equals(newTxHash))).length;
|
|
48
|
+
this.log.verbose(`Evicted ${txsToEvict.length} low priority txs, including ${numNewTxsEvicted} newly added txs`, {
|
|
49
|
+
txsEvicted: txsToEvict
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
reason: 'low_priority',
|
|
53
|
+
success: true,
|
|
54
|
+
txsEvicted: txsToEvict
|
|
55
|
+
};
|
|
56
|
+
} catch (err) {
|
|
57
|
+
this.log.error('Failed to evict low priority transactions', {
|
|
58
|
+
err
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
reason: 'low_priority',
|
|
62
|
+
success: false,
|
|
63
|
+
txsEvicted: [],
|
|
64
|
+
error: new Error('Failed to evict low priority txs', {
|
|
65
|
+
cause: err
|
|
66
|
+
})
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Updates the configuration for this eviction rule
|
|
72
|
+
*/ updateConfig(config) {
|
|
73
|
+
if (config.maxPendingTxCount !== undefined) {
|
|
74
|
+
this.config.maxPoolSize = config.maxPendingTxCount;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the current configuration
|
|
79
|
+
*/ getConfig() {
|
|
80
|
+
return {
|
|
81
|
+
...this.config
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export * from './tx_pool.js';
|
|
2
|
-
export * from './memory_tx_pool.js';
|
|
3
2
|
export * from './aztec_kv_tx_pool.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tZW1fcG9vbHMvdHhfcG9vbC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLHVCQUF1QixDQUFDIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/tx_pool/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/tx_pool/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC"}
|
|
@@ -5,4 +5,8 @@ import type { Tx } from '@aztec/stdlib/tx';
|
|
|
5
5
|
* We currently use the sum of the priority fees for the tx for this value, represented as hex.
|
|
6
6
|
*/
|
|
7
7
|
export declare function getPendingTxPriority(tx: Tx): string;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Returns the priority of a tx.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getTxPriorityFee(tx: Tx): bigint;
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJpb3JpdHkuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tZW1fcG9vbHMvdHhfcG9vbC9wcmlvcml0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUUzQzs7OztHQUlHO0FBQ0gsd0JBQWdCLG9CQUFvQixDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsTUFBTSxDQUVuRDtBQUVEOztHQUVHO0FBQ0gsd0JBQWdCLGdCQUFnQixDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsTUFBTSxDQUkvQyJ9
|