Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BlockNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-
import { Grumpkin } from '@aztec/foundation/crypto/grumpkin';
import { Fr } from '@aztec/foundation/curves/bn254';
import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
import { promiseWithResolvers } from '@aztec/foundation/promise';
import { MembershipWitness } from '@aztec/foundation/trees';
import type { KeyStore } from '@aztec/key-store';
import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
Expand Down Expand Up @@ -35,11 +36,13 @@ import {
CallContext,
Capsule,
DroppedTxReceipt,
type GetTxReceiptOptions,
GlobalVariables,
MinedTxReceipt,
TxEffect,
TxExecutionResult,
TxHash,
type TxReceipt,
TxStatus,
} from '@aztec/stdlib/tx';

Expand Down Expand Up @@ -761,6 +764,51 @@ describe('Utility Execution test suite', () => {
expect(aztecNode.getTxReceipt).toHaveBeenCalledTimes(3);
});

it('bounds concurrent batched tx-effect reads', async () => {
const service = new EphemeralArrayService();
const oracle = makeOracle({ scopes: [scope] });
const txHashes = Array.from({ length: 17 }, () => TxHash.random());
const reads = txHashes.map(txHash => ({
txHash,
deferred: promiseWithResolvers<MinedTxReceipt<{ includeTxEffect: true }>>(),
}));

aztecNode.getTxReceipt.mockImplementation(
<TGetTxReceiptOptions extends GetTxReceiptOptions = {}>(txHash: TxHash) => {
const read = reads.find(({ txHash: candidate }) => candidate.equals(txHash));
if (!read) {
throw new Error(`unexpected tx hash ${txHash}`);
}
return read.deferred.promise as Promise<TxReceipt<TGetTxReceiptOptions>>;
},
);

const resultPromise = oracle.getTxEffects(EphemeralArray.fromValues(service, txHashes));
await Promise.resolve();
await Promise.resolve();

expect(aztecNode.getTxReceipt).toHaveBeenCalledTimes(16);

reads[0].deferred.resolve(makeMinedReceipt(reads[0].txHash));
await Promise.resolve();
await Promise.resolve();

expect(aztecNode.getTxReceipt).toHaveBeenCalledTimes(16);

for (const read of reads.slice(1, 16)) {
read.deferred.resolve(makeMinedReceipt(read.txHash));
}
await Promise.resolve();
await Promise.resolve();

expect(aztecNode.getTxReceipt).toHaveBeenCalledTimes(17);

reads[16].deferred.resolve(makeMinedReceipt(reads[16].txHash));

const result = await resultPromise;
expect(result.readAll(service).every(option => option.isSome())).toBe(true);
});

it('does not share batched tx-effect reads across utility executions', async () => {
const service = new EphemeralArrayService();
const txHash = TxHash.random();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ import type { TransientArrayService } from '../transient_array_service.js';
import { buildACIRCallback } from './acir_callback.js';
import type { IMiscOracle, IUtilityExecutionOracle } from './interfaces.js';

const TX_EFFECT_READ_CONCURRENCY = 16;

/** Args for UtilityExecutionOracle constructor. */
export type UtilityExecutionOracleArgs = {
callContext: CallContext;
Expand Down Expand Up @@ -696,7 +698,14 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
}

const uniqueTxHashes = uniqueBy(hashes, h => h.toString());
const options = await Promise.all(uniqueTxHashes.map(txHash => this.#getTxEffectOption(txHash)));
const options: Option<TxEffectData>[] = [];
for (let i = 0; i < uniqueTxHashes.length; i += TX_EFFECT_READ_CONCURRENCY) {
options.push(
...(await Promise.all(
uniqueTxHashes.slice(i, i + TX_EFFECT_READ_CONCURRENCY).map(txHash => this.#getTxEffectOption(txHash)),
)),
);
}
const optionsByHash = new Map(uniqueTxHashes.map((txHash, i) => [txHash.toString(), options[i]]));

return EphemeralArray.fromValues(
Expand Down
Loading