Skip to content

Commit

Permalink
fix: ensure that contracts can be called after deployment in same blo…
Browse files Browse the repository at this point in the history
…ck (#12390)
  • Loading branch information
dbanks12 authored Mar 3, 2025
1 parent 9267a90 commit 90bebdd
Show file tree
Hide file tree
Showing 17 changed files with 283 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,28 @@ describe('e2e_deploy_contract deploy method', () => {
await new BatchCall(wallet, [...deploy.calls, init]).send().wait();
}, 300_000);

it.skip('publicly deploys and calls a public function in a tx in the same block', async () => {
// TODO(@spalladino): Requires being able to read a nullifier on the same block it was emitted.
});
it.skip('publicly deploys a contract in one tx and calls a public function on it later in the same block', async () => {
await t.aztecNode.setConfig({ minTxsPerBlock: 2 });

const owner = wallet.getAddress();
logger.debug('Initializing deploy method');
const deployMethod = StatefulTestContract.deploy(wallet, owner, owner, 42);
logger.debug('Creating request/calls to register and deploy contract');
const deploy = await deployMethod.request();
const deployTx = new BatchCall(wallet, deploy.calls);
logger.debug('Getting an instance of the not-yet-deployed contract to batch calls to');
const contract = await StatefulTestContract.at((await deployMethod.getInstance()).address, wallet);

logger.debug('Creating public call to run in same block as deployment');
const publicCall = contract.methods.increment_public_value(owner, 84);

logger.debug('Deploying a contract and calling a public function in the same block');
const [deployTxReceipt, publicCallTxReceipt] = await Promise.all([
deployTx.send({ skipPublicSimulation: true }).wait({ timeout: 600 }),
publicCall.send({ skipPublicSimulation: true }).wait({ timeout: 600 }),
]);
expect(deployTxReceipt.blockNumber).toEqual(publicCallTxReceipt.blockNumber);
}, 300_000);

describe('regressions', () => {
it('fails properly when trying to deploy a contract with a failing constructor with a pxe client with retries', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import type { Fr } from '@aztec/foundation/fields';
import { createLogger } from '@aztec/foundation/log';
import { type ContractArtifact, FunctionSelector } from '@aztec/stdlib/abi';
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
import {
type ContractClassPublic,
type ContractDataSource,
type ContractInstanceWithAddress,
type PublicFunction,
computePublicBytecodeCommitment,
import type {
ContractClassPublic,
ContractDataSource,
ContractInstanceWithAddress,
PublicFunction,
} from '@aztec/stdlib/contract';

import { PUBLIC_DISPATCH_FN_NAME } from './index.js';
Expand Down Expand Up @@ -63,9 +62,8 @@ export class SimpleContractDataSource implements ContractDataSource {
return Promise.resolve(this.contractClasses.get(id.toString()));
}

async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
const contractClass = await this.getContractClass(id);
return Promise.resolve(computePublicBytecodeCommitment(contractClass!.packedBytecode));
getBytecodeCommitment(_id: Fr): Promise<Fr | undefined> {
return Promise.resolve(undefined);
}

getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
Expand Down
6 changes: 2 additions & 4 deletions yarn-project/simulator/src/public/avm/journal/journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,17 +193,15 @@ export class AvmPersistableStateManager {
`Value mismatch when performing public data write (got value: ${value}, value in tree: ${newLeafPreimage.value})`,
);
} else {
this.log.debug(`insertion witness data length: ${result.insertionWitnessData.length}`);
// The new leaf preimage should have the new value and slot
newLeafPreimage.slot = leafSlot;
newLeafPreimage.value = value;
// TODO: is this necessary?! Why doesn't sequentialInsert return the newLeafPreimage via
// result.insertionWitnessData[0].leafPreimage?

this.log.debug(
`newLeafPreimage.slot: ${newLeafPreimage.slot}, newLeafPreimage.value: ${newLeafPreimage.value}`,
this.log.trace(
`newLeafPreimage.slot: ${newLeafPreimage.slot}, newLeafPreimage.value: ${newLeafPreimage.value}, insertionIndex: ${result.insertionWitnessData[0].index}`,
);
this.log.debug(`insertion index: ${result.insertionWitnessData[0].index}`);
insertionPath = result.insertionWitnessData[0].siblingPath.toFields();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { BaseAvmSimulationTester } from '../avm/fixtures/base_avm_simulation_tes
import { getContractFunctionArtifact, getFunctionSelector } from '../avm/fixtures/index.js';
import { SimpleContractDataSource } from '../avm/fixtures/simple_contract_data_source.js';
import { WorldStateDB } from '../public_db_sources.js';
import { type PublicTxResult, PublicTxSimulator } from '../public_tx_simulator.js';
import { type PublicTxResult, PublicTxSimulator } from '../public_tx_simulator/public_tx_simulator.js';
import { createTxForPublicCalls } from './index.js';

const TIMESTAMP = new Fr(99833);
Expand Down
41 changes: 33 additions & 8 deletions yarn-project/simulator/src/public/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
import { siloNullifier } from '@aztec/stdlib/hash';
import {
PartialPrivateTailPublicInputsForPublic,
PartialPrivateTailPublicInputsForRollup,
PrivateKernelTailCircuitPublicInputs,
RollupValidationRequests,
ScopedLogHash,
Expand Down Expand Up @@ -90,6 +91,28 @@ export async function createTxForPublicCalls(
return tx;
}

export function createTxForPrivateOnly(feePayer = AztecAddress.zero(), gasUsedByPrivate: Gas = new Gas(10, 10)): Tx {
// use max limits
const gasLimits = new Gas(DEFAULT_GAS_LIMIT, MAX_L2_GAS_PER_TX_PUBLIC_PORTION);

const forRollup = PartialPrivateTailPublicInputsForRollup.empty();

const maxFeesPerGas = feePayer.isZero() ? GasFees.empty() : new GasFees(10, 10);
const gasSettings = new GasSettings(gasLimits, Gas.empty(), maxFeesPerGas, GasFees.empty());
const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
const constantData = new TxConstantData(BlockHeader.empty(), txContext, Fr.zero(), Fr.zero());

const txData = new PrivateKernelTailCircuitPublicInputs(
constantData,
RollupValidationRequests.empty(),
/*gasUsed=*/ gasUsedByPrivate,
feePayer,
/*forPublic=*/ undefined,
forRollup,
);
return Tx.newWithTxData(txData);
}

export async function addNewContractClassToTx(
tx: Tx,
contractClass: ContractClassPublic,
Expand All @@ -116,13 +139,14 @@ export async function addNewContractClassToTx(
new Fr(REGISTERER_CONTRACT_ADDRESS),
]);

const accumulatedData = tx.data.forPublic ? tx.data.forPublic!.revertibleAccumulatedData : tx.data.forRollup!.end;
if (!skipNullifierInsertion) {
const nextNullifierIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.nullifiers);
tx.data.forPublic!.revertibleAccumulatedData.nullifiers[nextNullifierIndex] = contractClass.id;
const nextNullifierIndex = countAccumulatedItems(accumulatedData.nullifiers);
accumulatedData.nullifiers[nextNullifierIndex] = contractClass.id;
}

const nextLogIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.contractClassLogsHashes);
tx.data.forPublic!.revertibleAccumulatedData.contractClassLogsHashes[nextLogIndex] = contractClassLogHash;
const nextLogIndex = countAccumulatedItems(accumulatedData.contractClassLogsHashes);
accumulatedData.contractClassLogsHashes[nextLogIndex] = contractClassLogHash;

tx.contractClassLogs.push(contractClassLog);
}
Expand Down Expand Up @@ -164,11 +188,12 @@ export async function addNewContractInstanceToTx(
contractInstance.address.toField(),
);

const accumulatedData = tx.data.forPublic ? tx.data.forPublic!.revertibleAccumulatedData : tx.data.forRollup!.end;
if (!skipNullifierInsertion) {
const nextNullifierIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.nullifiers);
tx.data.forPublic!.revertibleAccumulatedData.nullifiers[nextNullifierIndex] = contractAddressNullifier;
const nextNullifierIndex = countAccumulatedItems(accumulatedData.nullifiers);
accumulatedData.nullifiers[nextNullifierIndex] = contractAddressNullifier;
}

const nextLogIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.privateLogs);
tx.data.forPublic!.revertibleAccumulatedData.privateLogs[nextLogIndex] = contractInstanceLog;
const nextLogIndex = countAccumulatedItems(accumulatedData.privateLogs);
accumulatedData.privateLogs[nextLogIndex] = contractInstanceLog;
}
4 changes: 2 additions & 2 deletions yarn-project/simulator/src/public/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export * from '../common/db_interfaces.js';
export * from './public_tx_simulator.js';
export * from './public_tx_simulator/public_tx_simulator.js';
export { type EnqueuedPublicCallExecutionResult, type PublicFunctionCallResult } from './execution.js';
export * from './public_db_sources.js';
export { PublicProcessor, PublicProcessorFactory } from './public_processor.js';
export { PublicProcessor, PublicProcessorFactory } from './public_processor/public_processor.js';
export { SideEffectTrace } from './side_effect_trace.js';
export { getExecutionRequestsByPhase } from './utils.js';
export { PublicTxSimulationTester } from './fixtures/index.js';
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/simulator/src/public/public_db_sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,23 +189,22 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB {
// Try and retrieve from cache
const key = contractClassId.toString();
const result = this.bytecodeCommitmentCache.get(key);

if (result !== undefined) {
return result;
}

// Now try from the store
const fromStore = await this.dataSource.getBytecodeCommitment(contractClassId);
if (fromStore !== undefined) {
this.bytecodeCommitmentCache.set(key, fromStore);
return fromStore;
}

// Not in either the store or the caches, build it here and cache
// Not in either the store or the cache, build it here and cache
const contractClass = await this.getContractClass(contractClassId);
if (contractClass === undefined) {
return undefined;
}

const value = await computePublicBytecodeCommitment(contractClass.packedBytecode);
this.bytecodeCommitmentCache.set(key, value);
return value;
Expand Down
Loading

0 comments on commit 90bebdd

Please sign in to comment.