@aztec/p2p 0.0.1-commit.96dac018d → 0.0.1-commit.993d52e
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 +4 -5
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +6 -6
- package/dest/client/interface.d.ts +4 -4
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +4 -4
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +1 -1
- package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -2
- package/dest/config.d.ts +10 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +2 -0
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +7 -1
- package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +8 -6
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -2
- package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/index.js +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +30 -7
- package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +62 -16
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +9 -10
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +38 -30
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +10 -0
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +166 -0
- package/dest/services/encoding.d.ts +2 -2
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +7 -7
- package/dest/services/libp2p/libp2p_service.d.ts +6 -7
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +11 -12
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +37 -14
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +11 -17
- package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/peer_collection.js +15 -49
- package/dest/test-helpers/make-test-p2p-clients.d.ts +5 -6
- package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
- package/dest/test-helpers/make-test-p2p-clients.js +1 -2
- package/dest/test-helpers/mock-pubsub.d.ts +2 -3
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +2 -2
- package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +2 -2
- package/dest/testbench/p2p_client_testbench_worker.js +5 -5
- package/package.json +14 -14
- package/src/client/factory.ts +9 -14
- package/src/client/interface.ts +3 -9
- package/src/client/p2p_client.ts +2 -12
- package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -2
- package/src/config.ts +20 -2
- package/src/mem_pools/tx_pool_v2/README.md +9 -1
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -0
- package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +11 -1
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +15 -6
- package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +2 -1
- package/src/mem_pools/tx_pool_v2/index.ts +1 -1
- package/src/mem_pools/tx_pool_v2/interfaces.ts +3 -3
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +89 -17
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +11 -11
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +43 -27
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +1 -1
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +1 -1
- package/src/msg_validators/proposal_validator/proposal_validator.ts +15 -1
- package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +144 -1
- package/src/services/encoding.ts +5 -6
- package/src/services/libp2p/libp2p_service.ts +10 -12
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +42 -14
- package/src/services/reqresp/batch-tx-requester/peer_collection.ts +24 -63
- package/src/test-helpers/make-test-p2p-clients.ts +0 -2
- package/src/test-helpers/mock-pubsub.ts +3 -6
- package/src/test-helpers/reqresp-nodes.ts +2 -5
- package/src/testbench/p2p_client_testbench_worker.ts +2 -6
|
@@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
3
3
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
4
4
|
import { BlockHash, type L2BlockId } from '@aztec/stdlib/block';
|
|
5
5
|
import { Gas } from '@aztec/stdlib/gas';
|
|
6
|
-
import type
|
|
6
|
+
import { type Tx, TxHash } from '@aztec/stdlib/tx';
|
|
7
7
|
|
|
8
8
|
import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
|
|
9
9
|
import { getTxPriorityFee } from '../tx_pool/priority.js';
|
|
@@ -40,6 +40,9 @@ export type TxMetaData = {
|
|
|
40
40
|
/** The transaction hash as hex string */
|
|
41
41
|
readonly txHash: string;
|
|
42
42
|
|
|
43
|
+
/** The transaction hash as bigint (for efficient Fr conversion in comparisons) */
|
|
44
|
+
readonly txHashBigInt: bigint;
|
|
45
|
+
|
|
43
46
|
/** Block ID (number and hash) in which the transaction was mined (undefined if not mined) */
|
|
44
47
|
minedL2BlockId?: L2BlockId;
|
|
45
48
|
|
|
@@ -83,7 +86,9 @@ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
|
|
|
83
86
|
* Fr values are captured in closures for zero-cost re-validation.
|
|
84
87
|
*/
|
|
85
88
|
export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
86
|
-
const
|
|
89
|
+
const txHashObj = tx.getTxHash();
|
|
90
|
+
const txHash = txHashObj.toString();
|
|
91
|
+
const txHashBigInt = txHashObj.toBigInt();
|
|
87
92
|
const nullifierFrs = tx.data.getNonEmptyNullifiers();
|
|
88
93
|
const nullifiers = nullifierFrs.map(n => n.toString());
|
|
89
94
|
const anchorBlockHeaderHashFr = await tx.data.constants.anchorBlockHeader.hash();
|
|
@@ -99,6 +104,7 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
|
|
|
99
104
|
|
|
100
105
|
return {
|
|
101
106
|
txHash,
|
|
107
|
+
txHashBigInt,
|
|
102
108
|
anchorBlockHeaderHash,
|
|
103
109
|
priorityFee,
|
|
104
110
|
feePayer,
|
|
@@ -134,11 +140,11 @@ const HEX_STRING_BYTES = 98;
|
|
|
134
140
|
const BIGINT_BYTES = 32;
|
|
135
141
|
const FR_BYTES = 80;
|
|
136
142
|
// Fixed cost: object shell + txHash + anchorBlockHeaderHash + feePayer (3 hex strings)
|
|
137
|
-
// + priorityFee + claimAmount + feeLimit + includeByTimestamp (
|
|
143
|
+
// + txHashBigInt + priorityFee + claimAmount + feeLimit + includeByTimestamp (5 bigints)
|
|
138
144
|
// + receivedAt (number, 8 bytes) + estimatedSizeBytes (number, 8 bytes)
|
|
139
145
|
// + data closure object (~OBJECT_OVERHEAD + anchorBlockHeaderHashFr Fr + anchorBlockNumber number)
|
|
140
146
|
const FIXED_METADATA_BYTES =
|
|
141
|
-
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES +
|
|
147
|
+
OBJECT_OVERHEAD + 3 * HEX_STRING_BYTES + 5 * BIGINT_BYTES + 8 + 8 + OBJECT_OVERHEAD + FR_BYTES + 8;
|
|
142
148
|
|
|
143
149
|
/** Estimates the in-memory size of a TxMetaData object based on the number of nullifiers. */
|
|
144
150
|
function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
@@ -146,8 +152,13 @@ function estimateTxMetaDataSize(nullifierCount: number): number {
|
|
|
146
152
|
return FIXED_METADATA_BYTES + nullifierCount * (HEX_STRING_BYTES + FR_BYTES);
|
|
147
153
|
}
|
|
148
154
|
|
|
155
|
+
/** Converts a txHash bigint back to the canonical 0x-prefixed 64-char hex string. */
|
|
156
|
+
export function txHashFromBigInt(value: bigint): string {
|
|
157
|
+
return TxHash.fromBigInt(value).toString();
|
|
158
|
+
}
|
|
159
|
+
|
|
149
160
|
/** Minimal fields required for priority comparison. */
|
|
150
|
-
type PriorityComparable = Pick<TxMetaData, '
|
|
161
|
+
type PriorityComparable = Pick<TxMetaData, 'txHashBigInt' | 'priorityFee'>;
|
|
151
162
|
|
|
152
163
|
/**
|
|
153
164
|
* Compares two priority fees in ascending order.
|
|
@@ -162,10 +173,8 @@ export function compareFee(a: bigint, b: bigint): number {
|
|
|
162
173
|
* Uses field element comparison for deterministic ordering.
|
|
163
174
|
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
164
175
|
*/
|
|
165
|
-
export function compareTxHash(a:
|
|
166
|
-
|
|
167
|
-
const fieldB = Fr.fromHexString(b);
|
|
168
|
-
return fieldA.cmp(fieldB);
|
|
176
|
+
export function compareTxHash(a: bigint, b: bigint): number {
|
|
177
|
+
return Fr.cmpAsBigInt(a, b);
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
/**
|
|
@@ -178,24 +187,41 @@ export function comparePriority(a: PriorityComparable, b: PriorityComparable): n
|
|
|
178
187
|
if (feeComparison !== 0) {
|
|
179
188
|
return feeComparison;
|
|
180
189
|
}
|
|
181
|
-
return compareTxHash(a.
|
|
190
|
+
return compareTxHash(a.txHashBigInt, b.txHashBigInt);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Returns the minimum fee required to replace an existing tx with the given price bump percentage.
|
|
195
|
+
* Uses integer arithmetic: `existingFee + existingFee * priceBumpPercentage / 100`.
|
|
196
|
+
*/
|
|
197
|
+
export function getMinimumPriceBumpFee(existingFee: bigint, priceBumpPercentage: bigint): bigint {
|
|
198
|
+
const bump = (existingFee * priceBumpPercentage) / 100n;
|
|
199
|
+
// Ensure the minimum bump is at least 1, so that replacement always requires
|
|
200
|
+
// paying strictly more — even with 0% bump or zero existing fee.
|
|
201
|
+
const effectiveBump = bump > 0n ? bump : 1n;
|
|
202
|
+
return existingFee + effectiveBump;
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
/**
|
|
185
206
|
* Checks for nullifier conflicts between an incoming transaction and existing pool state.
|
|
186
207
|
*
|
|
187
208
|
* When the incoming tx shares nullifiers with existing pending txs:
|
|
188
|
-
* - If the incoming tx
|
|
189
|
-
* -
|
|
209
|
+
* - If the incoming tx meets or exceeds the required priority, mark conflicting txs for eviction
|
|
210
|
+
* - Otherwise, ignore the incoming tx
|
|
211
|
+
*
|
|
212
|
+
* When `priceBumpPercentage` is provided (RPC path), uses fee-only comparison with the
|
|
213
|
+
* percentage bump instead of `comparePriority`.
|
|
190
214
|
*
|
|
191
215
|
* @param incomingMeta - Metadata for the incoming transaction
|
|
192
216
|
* @param getTxHashByNullifier - Accessor to find which tx uses a nullifier
|
|
193
217
|
* @param getMetadata - Accessor to get metadata for a tx hash
|
|
218
|
+
* @param priceBumpPercentage - Optional percentage bump required for fee-based replacement
|
|
194
219
|
*/
|
|
195
220
|
export function checkNullifierConflict(
|
|
196
221
|
incomingMeta: TxMetaData,
|
|
197
222
|
getTxHashByNullifier: (nullifier: string) => string | undefined,
|
|
198
223
|
getMetadata: (txHash: string) => TxMetaData | undefined,
|
|
224
|
+
priceBumpPercentage?: bigint,
|
|
199
225
|
): PreAddResult {
|
|
200
226
|
const txHashesToEvict: string[] = [];
|
|
201
227
|
|
|
@@ -216,19 +242,32 @@ export function checkNullifierConflict(
|
|
|
216
242
|
continue;
|
|
217
243
|
}
|
|
218
244
|
|
|
219
|
-
//
|
|
220
|
-
// Otherwise,
|
|
221
|
-
|
|
222
|
-
|
|
245
|
+
// When price bump is set (RPC path), require the incoming fee to meet the bumped threshold.
|
|
246
|
+
// Otherwise (P2P path), use full comparePriority with tx hash tiebreaker.
|
|
247
|
+
const isHigherPriority =
|
|
248
|
+
priceBumpPercentage !== undefined
|
|
249
|
+
? incomingMeta.priorityFee >= getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
250
|
+
: comparePriority(incomingMeta, conflictingMeta) > 0;
|
|
251
|
+
|
|
252
|
+
if (isHigherPriority) {
|
|
223
253
|
txHashesToEvict.push(conflictingHashStr);
|
|
224
254
|
} else {
|
|
255
|
+
const minimumFee =
|
|
256
|
+
priceBumpPercentage !== undefined
|
|
257
|
+
? getMinimumPriceBumpFee(conflictingMeta.priorityFee, priceBumpPercentage)
|
|
258
|
+
: undefined;
|
|
225
259
|
return {
|
|
226
260
|
shouldIgnore: true,
|
|
227
261
|
txHashesToEvict: [],
|
|
228
262
|
reason: {
|
|
229
263
|
code: TxPoolRejectionCode.NULLIFIER_CONFLICT,
|
|
230
|
-
message:
|
|
264
|
+
message:
|
|
265
|
+
minimumFee !== undefined
|
|
266
|
+
? `Nullifier conflict with existing tx ${conflictingHashStr}. Minimum required fee: ${minimumFee}, got: ${incomingMeta.priorityFee}`
|
|
267
|
+
: `Nullifier conflict with existing tx ${conflictingHashStr}`,
|
|
231
268
|
conflictingTxHash: conflictingHashStr,
|
|
269
|
+
minimumPriceBumpFee: minimumFee,
|
|
270
|
+
txPriorityFee: minimumFee !== undefined ? incomingMeta.priorityFee : undefined,
|
|
232
271
|
},
|
|
233
272
|
};
|
|
234
273
|
}
|
|
@@ -253,3 +292,36 @@ export function stubTxMetaValidationData(overrides: { expirationTimestamp?: bigi
|
|
|
253
292
|
},
|
|
254
293
|
};
|
|
255
294
|
}
|
|
295
|
+
|
|
296
|
+
/** Creates a stub TxMetaData for tests. All fields have sensible defaults and can be overridden. */
|
|
297
|
+
export function stubTxMetaData(
|
|
298
|
+
txHash: string,
|
|
299
|
+
overrides: {
|
|
300
|
+
priorityFee?: bigint;
|
|
301
|
+
feePayer?: string;
|
|
302
|
+
claimAmount?: bigint;
|
|
303
|
+
feeLimit?: bigint;
|
|
304
|
+
nullifiers?: string[];
|
|
305
|
+
expirationTimestamp?: bigint;
|
|
306
|
+
anchorBlockHeaderHash?: string;
|
|
307
|
+
} = {},
|
|
308
|
+
): TxMetaData {
|
|
309
|
+
const txHashBigInt = Fr.fromHexString(txHash).toBigInt();
|
|
310
|
+
// Normalize to canonical zero-padded hex so txHashFromBigInt(txHashBigInt) === normalizedTxHash
|
|
311
|
+
const normalizedTxHash = txHashFromBigInt(txHashBigInt);
|
|
312
|
+
const expirationTimestamp = overrides.expirationTimestamp ?? 0n;
|
|
313
|
+
return {
|
|
314
|
+
txHash: normalizedTxHash,
|
|
315
|
+
txHashBigInt,
|
|
316
|
+
anchorBlockHeaderHash: overrides.anchorBlockHeaderHash ?? '0x1234',
|
|
317
|
+
priorityFee: overrides.priorityFee ?? 100n,
|
|
318
|
+
feePayer: overrides.feePayer ?? '0xfeepayer',
|
|
319
|
+
claimAmount: overrides.claimAmount ?? 0n,
|
|
320
|
+
feeLimit: overrides.feeLimit ?? 100n,
|
|
321
|
+
nullifiers: overrides.nullifiers ?? [`0x${normalizedTxHash.slice(2)}null1`],
|
|
322
|
+
expirationTimestamp,
|
|
323
|
+
receivedAt: 0,
|
|
324
|
+
estimatedSizeBytes: 0,
|
|
325
|
+
data: stubTxMetaValidationData({ expirationTimestamp }),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import type { L2BlockId } from '@aztec/stdlib/block';
|
|
3
3
|
|
|
4
|
-
import { type TxMetaData, type TxState, compareFee, compareTxHash } from './tx_metadata.js';
|
|
4
|
+
import { type TxMetaData, type TxState, compareFee, compareTxHash, txHashFromBigInt } from './tx_metadata.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Manages in-memory indices for the transaction pool.
|
|
@@ -22,8 +22,8 @@ export class TxPoolIndices {
|
|
|
22
22
|
#nullifierToTxHash: Map<string, string> = new Map();
|
|
23
23
|
/** Fee payer to txHashes index (pending txs only) */
|
|
24
24
|
#feePayerToTxHashes: Map<string, Set<string>> = new Map();
|
|
25
|
-
/** Pending
|
|
26
|
-
#pendingByPriority: Map<bigint, Set<
|
|
25
|
+
/** Pending txHash bigints grouped by priority fee */
|
|
26
|
+
#pendingByPriority: Map<bigint, Set<bigint>> = new Map();
|
|
27
27
|
/** Protected transactions: txHash -> slotNumber */
|
|
28
28
|
#protectedTransactions: Map<string, SlotNumber> = new Map();
|
|
29
29
|
|
|
@@ -73,17 +73,17 @@ export class TxPoolIndices {
|
|
|
73
73
|
* @param order - 'desc' for highest priority first, 'asc' for lowest priority first
|
|
74
74
|
*/
|
|
75
75
|
*iteratePendingByPriority(order: 'asc' | 'desc', filter?: (hash: string) => boolean): Generator<string> {
|
|
76
|
-
// Use compareFee from tx_metadata, swap args for descending order
|
|
77
76
|
const feeCompareFn = order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : compareFee;
|
|
78
|
-
const hashCompareFn =
|
|
77
|
+
const hashCompareFn =
|
|
78
|
+
order === 'desc' ? (a: bigint, b: bigint) => compareTxHash(b, a) : (a: bigint, b: bigint) => compareTxHash(a, b);
|
|
79
79
|
|
|
80
80
|
const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
|
|
81
81
|
|
|
82
82
|
for (const fee of sortedFees) {
|
|
83
83
|
const hashesAtFee = this.#pendingByPriority.get(fee)!;
|
|
84
|
-
// Use compareTxHash from tx_metadata, swap args for descending order
|
|
85
84
|
const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
|
|
86
|
-
for (const
|
|
85
|
+
for (const hashBigInt of sortedHashes) {
|
|
86
|
+
const hash = txHashFromBigInt(hashBigInt);
|
|
87
87
|
if (filter === undefined || filter(hash)) {
|
|
88
88
|
yield hash;
|
|
89
89
|
}
|
|
@@ -265,8 +265,8 @@ export class TxPoolIndices {
|
|
|
265
265
|
getPendingTxs(): TxMetaData[] {
|
|
266
266
|
const result: TxMetaData[] = [];
|
|
267
267
|
for (const hashSet of this.#pendingByPriority.values()) {
|
|
268
|
-
for (const
|
|
269
|
-
const meta = this.#metadata.get(
|
|
268
|
+
for (const txHashBigInt of hashSet) {
|
|
269
|
+
const meta = this.#metadata.get(txHashFromBigInt(txHashBigInt));
|
|
270
270
|
if (meta) {
|
|
271
271
|
result.push(meta);
|
|
272
272
|
}
|
|
@@ -414,7 +414,7 @@ export class TxPoolIndices {
|
|
|
414
414
|
prioritySet = new Set();
|
|
415
415
|
this.#pendingByPriority.set(meta.priorityFee, prioritySet);
|
|
416
416
|
}
|
|
417
|
-
prioritySet.add(meta.
|
|
417
|
+
prioritySet.add(meta.txHashBigInt);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
#removeFromPendingIndices(meta: TxMetaData): void {
|
|
@@ -435,7 +435,7 @@ export class TxPoolIndices {
|
|
|
435
435
|
// Remove from priority map
|
|
436
436
|
const hashSet = this.#pendingByPriority.get(meta.priorityFee);
|
|
437
437
|
if (hashSet) {
|
|
438
|
-
hashSet.delete(meta.
|
|
438
|
+
hashSet.delete(meta.txHashBigInt);
|
|
439
439
|
if (hashSet.size === 0) {
|
|
440
440
|
this.#pendingByPriority.delete(meta.priorityFee);
|
|
441
441
|
}
|
|
@@ -187,9 +187,35 @@ export class TxPoolV2Impl {
|
|
|
187
187
|
const errors = new Map<string, TxPoolRejectionError>();
|
|
188
188
|
const acceptedPending = new Set<string>();
|
|
189
189
|
|
|
190
|
+
// Phase 1: Pre-compute all throwable I/O outside the transaction.
|
|
191
|
+
// If any pre-computation throws, the entire call fails before mutations happen.
|
|
192
|
+
const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
|
|
193
|
+
|
|
194
|
+
const validator = await this.#createTxValidator();
|
|
195
|
+
|
|
196
|
+
for (const tx of txs) {
|
|
197
|
+
const txHash = tx.getTxHash();
|
|
198
|
+
const txHashStr = txHash.toString();
|
|
199
|
+
|
|
200
|
+
const meta = await buildTxMetaData(tx);
|
|
201
|
+
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
202
|
+
|
|
203
|
+
// Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
|
|
204
|
+
let isValid = true;
|
|
205
|
+
if (!minedBlockId) {
|
|
206
|
+
isValid = await this.#validateMeta(meta, validator);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
precomputed.set(txHashStr, { meta, minedBlockId, isValid });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Phase 2: Apply mutations inside the transaction using only pre-computed results,
|
|
213
|
+
// in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
|
|
190
214
|
const poolAccess = this.#createPreAddPoolAccess();
|
|
191
215
|
const preAddContext: PreAddContext | undefined =
|
|
192
|
-
opts.feeComparisonOnly !== undefined
|
|
216
|
+
opts.feeComparisonOnly !== undefined
|
|
217
|
+
? { feeComparisonOnly: opts.feeComparisonOnly, priceBumpPercentage: this.#config.priceBumpPercentage }
|
|
218
|
+
: undefined;
|
|
193
219
|
|
|
194
220
|
await this.#store.transactionAsync(async () => {
|
|
195
221
|
for (const tx of txs) {
|
|
@@ -202,22 +228,25 @@ export class TxPoolV2Impl {
|
|
|
202
228
|
continue;
|
|
203
229
|
}
|
|
204
230
|
|
|
205
|
-
|
|
206
|
-
const minedBlockId = await this.#getMinedBlockId(txHash);
|
|
231
|
+
const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
|
|
207
232
|
const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
|
|
208
233
|
|
|
209
234
|
if (minedBlockId) {
|
|
210
235
|
// Already mined - add directly (protection already set if pre-protected)
|
|
211
|
-
await this.#addTx(tx, { mined: minedBlockId }, opts);
|
|
236
|
+
await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
|
|
212
237
|
accepted.push(txHash);
|
|
213
238
|
} else if (preProtectedSlot !== undefined) {
|
|
214
239
|
// Pre-protected and not mined - add as protected (bypass validation)
|
|
215
|
-
await this.#addTx(tx, { protected: preProtectedSlot }, opts);
|
|
240
|
+
await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
|
|
216
241
|
accepted.push(txHash);
|
|
242
|
+
} else if (!isValid) {
|
|
243
|
+
// Failed pre-computed validation
|
|
244
|
+
rejected.push(txHash);
|
|
217
245
|
} else {
|
|
218
|
-
// Regular pending tx -
|
|
246
|
+
// Regular pending tx - run pre-add rules using pre-computed metadata
|
|
219
247
|
const result = await this.#tryAddRegularPendingTx(
|
|
220
248
|
tx,
|
|
249
|
+
meta,
|
|
221
250
|
opts,
|
|
222
251
|
poolAccess,
|
|
223
252
|
acceptedPending,
|
|
@@ -227,8 +256,6 @@ export class TxPoolV2Impl {
|
|
|
227
256
|
);
|
|
228
257
|
if (result.status === 'accepted') {
|
|
229
258
|
acceptedPending.add(txHashStr);
|
|
230
|
-
} else if (result.status === 'rejected') {
|
|
231
|
-
rejected.push(txHash);
|
|
232
259
|
} else {
|
|
233
260
|
ignored.push(txHash);
|
|
234
261
|
}
|
|
@@ -259,27 +286,21 @@ export class TxPoolV2Impl {
|
|
|
259
286
|
return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
|
|
260
287
|
}
|
|
261
288
|
|
|
262
|
-
/**
|
|
289
|
+
/** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
|
|
263
290
|
async #tryAddRegularPendingTx(
|
|
264
291
|
tx: Tx,
|
|
292
|
+
precomputedMeta: TxMetaData,
|
|
265
293
|
opts: { source?: string },
|
|
266
294
|
poolAccess: PreAddPoolAccess,
|
|
267
295
|
acceptedPending: Set<string>,
|
|
268
296
|
ignored: TxHash[],
|
|
269
297
|
errors: Map<string, TxPoolRejectionError>,
|
|
270
298
|
preAddContext?: PreAddContext,
|
|
271
|
-
): Promise<{ status: 'accepted' | 'ignored'
|
|
272
|
-
const
|
|
273
|
-
const txHashStr = txHash.toString();
|
|
274
|
-
|
|
275
|
-
// Build metadata and validate using metadata
|
|
276
|
-
const meta = await buildTxMetaData(tx);
|
|
277
|
-
if (!(await this.#validateMeta(meta))) {
|
|
278
|
-
return { status: 'rejected' };
|
|
279
|
-
}
|
|
299
|
+
): Promise<{ status: 'accepted' | 'ignored' }> {
|
|
300
|
+
const txHashStr = tx.getTxHash().toString();
|
|
280
301
|
|
|
281
302
|
// Run pre-add rules
|
|
282
|
-
const preAddResult = await this.#evictionManager.runPreAddRules(
|
|
303
|
+
const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
|
|
283
304
|
|
|
284
305
|
if (preAddResult.shouldIgnore) {
|
|
285
306
|
this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
|
|
@@ -316,14 +337,8 @@ export class TxPoolV2Impl {
|
|
|
316
337
|
}
|
|
317
338
|
}
|
|
318
339
|
|
|
319
|
-
// Randomly drop the transaction for testing purposes (report as accepted so it propagates)
|
|
320
|
-
if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
|
|
321
|
-
this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
|
|
322
|
-
return { status: 'accepted' };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
340
|
// Add the transaction
|
|
326
|
-
await this.#addTx(tx, 'pending', opts);
|
|
341
|
+
await this.#addTx(tx, 'pending', opts, precomputedMeta);
|
|
327
342
|
return { status: 'accepted' };
|
|
328
343
|
}
|
|
329
344
|
|
|
@@ -765,9 +780,10 @@ export class TxPoolV2Impl {
|
|
|
765
780
|
tx: Tx,
|
|
766
781
|
state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
|
|
767
782
|
opts: { source?: string } = {},
|
|
783
|
+
precomputedMeta?: TxMetaData,
|
|
768
784
|
): Promise<TxMetaData> {
|
|
769
785
|
const txHashStr = tx.getTxHash().toString();
|
|
770
|
-
const meta = await buildTxMetaData(tx);
|
|
786
|
+
const meta = precomputedMeta ?? (await buildTxMetaData(tx));
|
|
771
787
|
meta.receivedAt = this.#dateProvider.now();
|
|
772
788
|
|
|
773
789
|
await this.#txsDB.set(txHashStr, tx.toBuffer());
|
|
@@ -4,7 +4,7 @@ import type { BlockProposal, P2PValidator } from '@aztec/stdlib/p2p';
|
|
|
4
4
|
import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
5
5
|
|
|
6
6
|
export class BlockProposalValidator extends ProposalValidator<BlockProposal> implements P2PValidator<BlockProposal> {
|
|
7
|
-
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
|
|
7
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
|
|
8
8
|
super(epochCache, opts, 'p2p:block_proposal_validator');
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -7,7 +7,7 @@ export class CheckpointProposalValidator
|
|
|
7
7
|
extends ProposalValidator<CheckpointProposal>
|
|
8
8
|
implements P2PValidator<CheckpointProposal>
|
|
9
9
|
{
|
|
10
|
-
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
|
|
10
|
+
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
|
|
11
11
|
super(epochCache, opts, 'p2p:checkpoint_proposal_validator');
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -9,10 +9,16 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
|
|
|
9
9
|
protected epochCache: EpochCacheInterface;
|
|
10
10
|
protected logger: Logger;
|
|
11
11
|
protected txsPermitted: boolean;
|
|
12
|
+
protected maxTxsPerBlock?: number;
|
|
12
13
|
|
|
13
|
-
constructor(
|
|
14
|
+
constructor(
|
|
15
|
+
epochCache: EpochCacheInterface,
|
|
16
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
|
|
17
|
+
loggerName: string,
|
|
18
|
+
) {
|
|
14
19
|
this.epochCache = epochCache;
|
|
15
20
|
this.txsPermitted = opts.txsPermitted;
|
|
21
|
+
this.maxTxsPerBlock = opts.maxTxsPerBlock;
|
|
16
22
|
this.logger = createLogger(loggerName);
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -47,6 +53,14 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
|
|
|
47
53
|
return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
// Max txs per block check
|
|
57
|
+
if (this.maxTxsPerBlock !== undefined && proposal.txHashes.length > this.maxTxsPerBlock) {
|
|
58
|
+
this.logger.warn(
|
|
59
|
+
`Penalizing peer for proposal with ${proposal.txHashes.length} transaction(s) when max is ${this.maxTxsPerBlock}`,
|
|
60
|
+
);
|
|
61
|
+
return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
// Embedded txs must be listed in txHashes
|
|
51
65
|
const hashSet = new Set(proposal.txHashes.map(h => h.toString()));
|
|
52
66
|
const missingTxHashes =
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
+
import { NoCommitteeError } from '@aztec/ethereum/contracts';
|
|
2
3
|
import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
|
|
3
4
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
5
|
import {
|
|
@@ -9,12 +10,13 @@ import {
|
|
|
9
10
|
} from '@aztec/stdlib/p2p';
|
|
10
11
|
import type { TxHash } from '@aztec/stdlib/tx';
|
|
11
12
|
|
|
13
|
+
import { jest } from '@jest/globals';
|
|
12
14
|
import type { MockProxy } from 'jest-mock-extended';
|
|
13
15
|
|
|
14
16
|
export interface ProposalValidatorTestParams<TProposal extends BlockProposal | CheckpointProposal> {
|
|
15
17
|
validatorFactory: (
|
|
16
18
|
epochCache: EpochCacheInterface,
|
|
17
|
-
opts: { txsPermitted: boolean },
|
|
19
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
|
|
18
20
|
) => { validate: (proposal: TProposal) => Promise<ValidationResult> };
|
|
19
21
|
makeProposal: (options?: any) => Promise<TProposal>;
|
|
20
22
|
makeHeader: (epochNumber: number | bigint, slotNumber: number | bigint, blockNumber: number | bigint) => any;
|
|
@@ -105,6 +107,26 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
|
|
|
105
107
|
expect(result).toEqual({ result: 'ignore' });
|
|
106
108
|
});
|
|
107
109
|
|
|
110
|
+
it('returns mid tolerance error if proposal has invalid signature', async () => {
|
|
111
|
+
const currentProposer = getSigner();
|
|
112
|
+
const header = makeHeader(1, 100, 100);
|
|
113
|
+
const mockProposal = await makeProposal({
|
|
114
|
+
blockHeader: header,
|
|
115
|
+
lastBlockHeader: header,
|
|
116
|
+
signer: currentProposer,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Override getSender to return undefined (invalid signature)
|
|
120
|
+
jest.spyOn(mockProposal as any, 'getSender').mockReturnValue(undefined);
|
|
121
|
+
|
|
122
|
+
mockGetProposer(getAddress(currentProposer), getAddress());
|
|
123
|
+
const result = await validator.validate(mockProposal);
|
|
124
|
+
expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
|
|
125
|
+
|
|
126
|
+
// Should not try to resolve proposer if signature is invalid
|
|
127
|
+
expect(epochCache.getProposerAttesterAddressInSlot).not.toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
|
|
108
130
|
it('returns mid tolerance error if proposer is not current proposer for current slot', async () => {
|
|
109
131
|
const currentProposer = getSigner();
|
|
110
132
|
const nextProposer = getSigner();
|
|
@@ -152,6 +174,34 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
|
|
|
152
174
|
expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
|
|
153
175
|
});
|
|
154
176
|
|
|
177
|
+
it('accepts proposal when proposer is undefined (open committee)', async () => {
|
|
178
|
+
const currentProposer = getSigner();
|
|
179
|
+
const header = makeHeader(1, 100, 100);
|
|
180
|
+
const mockProposal = await makeProposal({
|
|
181
|
+
blockHeader: header,
|
|
182
|
+
lastBlockHeader: header,
|
|
183
|
+
signer: currentProposer,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
epochCache.getProposerAttesterAddressInSlot.mockResolvedValue(undefined);
|
|
187
|
+
const result = await validator.validate(mockProposal);
|
|
188
|
+
expect(result).toEqual({ result: 'accept' });
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('returns low tolerance error when getProposerAttesterAddressInSlot throws NoCommitteeError', async () => {
|
|
192
|
+
const currentProposer = getSigner();
|
|
193
|
+
const header = makeHeader(1, 100, 100);
|
|
194
|
+
const mockProposal = await makeProposal({
|
|
195
|
+
blockHeader: header,
|
|
196
|
+
lastBlockHeader: header,
|
|
197
|
+
signer: currentProposer,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
epochCache.getProposerAttesterAddressInSlot.mockRejectedValue(new NoCommitteeError());
|
|
201
|
+
const result = await validator.validate(mockProposal);
|
|
202
|
+
expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.LowToleranceError });
|
|
203
|
+
});
|
|
204
|
+
|
|
155
205
|
it('returns undefined if proposal is valid for current slot and proposer', async () => {
|
|
156
206
|
const currentProposer = getSigner();
|
|
157
207
|
const nextProposer = getSigner();
|
|
@@ -226,5 +276,98 @@ export function sharedProposalValidatorTests<TProposal extends BlockProposal | C
|
|
|
226
276
|
expect(result).toEqual({ result: 'accept' });
|
|
227
277
|
});
|
|
228
278
|
});
|
|
279
|
+
|
|
280
|
+
describe('embedded tx validation', () => {
|
|
281
|
+
it('returns mid tolerance error if embedded txs are not listed in txHashes', async () => {
|
|
282
|
+
const currentProposer = getSigner();
|
|
283
|
+
const txHashes = getTxHashes(2);
|
|
284
|
+
const header = makeHeader(1, 100, 100);
|
|
285
|
+
const mockProposal = await makeProposal({
|
|
286
|
+
blockHeader: header,
|
|
287
|
+
lastBlockHeader: header,
|
|
288
|
+
signer: currentProposer,
|
|
289
|
+
txHashes,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Create a fake tx whose hash is NOT in txHashes
|
|
293
|
+
const fakeTxHash = getTxHashes(1)[0];
|
|
294
|
+
const fakeTx = { getTxHash: () => fakeTxHash, validateTxHash: () => Promise.resolve(true) };
|
|
295
|
+
Object.defineProperty(mockProposal, 'txs', { get: () => [fakeTx], configurable: true });
|
|
296
|
+
|
|
297
|
+
mockGetProposer(getAddress(currentProposer), getAddress());
|
|
298
|
+
const result = await validator.validate(mockProposal);
|
|
299
|
+
expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('returns low tolerance error if embedded tx has invalid tx hash', async () => {
|
|
303
|
+
const currentProposer = getSigner();
|
|
304
|
+
const txHashes = getTxHashes(2);
|
|
305
|
+
const header = makeHeader(1, 100, 100);
|
|
306
|
+
const mockProposal = await makeProposal({
|
|
307
|
+
blockHeader: header,
|
|
308
|
+
lastBlockHeader: header,
|
|
309
|
+
signer: currentProposer,
|
|
310
|
+
txHashes,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Create a fake tx whose hash IS in txHashes but validateTxHash returns false
|
|
314
|
+
const fakeTx = { getTxHash: () => txHashes[0], validateTxHash: () => Promise.resolve(false) };
|
|
315
|
+
Object.defineProperty(mockProposal, 'txs', { get: () => [fakeTx], configurable: true });
|
|
316
|
+
|
|
317
|
+
mockGetProposer(getAddress(currentProposer), getAddress());
|
|
318
|
+
const result = await validator.validate(mockProposal);
|
|
319
|
+
expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.LowToleranceError });
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe('maxTxsPerBlock validation', () => {
|
|
324
|
+
it('rejects proposal when txHashes exceed maxTxsPerBlock', async () => {
|
|
325
|
+
const validatorWithMaxTxs = validatorFactory(epochCache, { txsPermitted: true, maxTxsPerBlock: 2 });
|
|
326
|
+
const currentProposer = getSigner();
|
|
327
|
+
const header = makeHeader(1, 100, 100);
|
|
328
|
+
const mockProposal = await makeProposal({
|
|
329
|
+
blockHeader: header,
|
|
330
|
+
lastBlockHeader: header,
|
|
331
|
+
signer: currentProposer,
|
|
332
|
+
txHashes: getTxHashes(3),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
mockGetProposer(getAddress(currentProposer), getAddress());
|
|
336
|
+
const result = await validatorWithMaxTxs.validate(mockProposal);
|
|
337
|
+
expect(result).toEqual({ result: 'reject', severity: PeerErrorSeverity.MidToleranceError });
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('accepts proposal when txHashes count equals maxTxsPerBlock', async () => {
|
|
341
|
+
const validatorWithMaxTxs = validatorFactory(epochCache, { txsPermitted: true, maxTxsPerBlock: 2 });
|
|
342
|
+
const currentProposer = getSigner();
|
|
343
|
+
const header = makeHeader(1, 100, 100);
|
|
344
|
+
const mockProposal = await makeProposal({
|
|
345
|
+
blockHeader: header,
|
|
346
|
+
lastBlockHeader: header,
|
|
347
|
+
signer: currentProposer,
|
|
348
|
+
txHashes: getTxHashes(2),
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
mockGetProposer(getAddress(currentProposer), getAddress());
|
|
352
|
+
const result = await validatorWithMaxTxs.validate(mockProposal);
|
|
353
|
+
expect(result).toEqual({ result: 'accept' });
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('accepts proposal when maxTxsPerBlock is not set (unlimited)', async () => {
|
|
357
|
+
// Default validator has no maxTxsPerBlock
|
|
358
|
+
const currentProposer = getSigner();
|
|
359
|
+
const header = makeHeader(1, 100, 100);
|
|
360
|
+
const mockProposal = await makeProposal({
|
|
361
|
+
blockHeader: header,
|
|
362
|
+
lastBlockHeader: header,
|
|
363
|
+
signer: currentProposer,
|
|
364
|
+
txHashes: getTxHashes(10),
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
mockGetProposer(getAddress(currentProposer), getAddress());
|
|
368
|
+
const result = await validator.validate(mockProposal);
|
|
369
|
+
expect(result).toEqual({ result: 'accept' });
|
|
370
|
+
});
|
|
371
|
+
});
|
|
229
372
|
});
|
|
230
373
|
}
|
package/src/services/encoding.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Taken from lodestar: https://github.com/ChainSafe/lodestar
|
|
2
|
-
import { sha256 } from '@aztec/foundation/crypto/sha256';
|
|
3
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
3
|
import { MAX_TX_SIZE_KB, TopicType, getTopicFromString } from '@aztec/stdlib/p2p';
|
|
5
4
|
|
|
6
5
|
import type { RPC } from '@chainsafe/libp2p-gossipsub/message';
|
|
7
6
|
import type { DataTransform } from '@chainsafe/libp2p-gossipsub/types';
|
|
8
7
|
import type { Message } from '@libp2p/interface';
|
|
8
|
+
import { webcrypto } from 'node:crypto';
|
|
9
9
|
import { compressSync, uncompressSync } from 'snappy';
|
|
10
10
|
import xxhashFactory from 'xxhash-wasm';
|
|
11
11
|
|
|
@@ -44,11 +44,10 @@ export function msgIdToStrFn(msgId: Uint8Array): string {
|
|
|
44
44
|
* @param message - The libp2p message
|
|
45
45
|
* @returns The message identifier
|
|
46
46
|
*/
|
|
47
|
-
export function getMsgIdFn(
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return sha256(Buffer.concat(vec)).subarray(0, 20);
|
|
47
|
+
export async function getMsgIdFn({ topic, data }: Message): Promise<Uint8Array> {
|
|
48
|
+
const buffer = Buffer.concat([Buffer.from(topic), data]);
|
|
49
|
+
const hash = await webcrypto.subtle.digest('SHA-256', buffer);
|
|
50
|
+
return Buffer.from(hash.slice(0, 20));
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
const DefaultMaxSizesKb: Record<TopicType, number> = {
|