Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions yarn-project/circuit-types/src/tx/processed_tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export type ProcessedTx = {
* Gas used by the entire transaction.
*/
gasUsed: GasUsed;
/**
* Code the tx was reverted (or OK).
*/
revertCode: RevertCode;
/**
* Reason the tx was reverted.
*/
Expand Down Expand Up @@ -113,6 +117,7 @@ export async function makeProcessedTxFromPrivateOnlyTx(
constants,
txEffect,
gasUsed,
revertCode: RevertCode.OK,
revertReason: undefined,
};
}
Expand Down Expand Up @@ -167,6 +172,7 @@ export async function makeProcessedTxFromTxWithPublicCalls(
constants,
txEffect,
gasUsed,
revertCode,
revertReason,
};
}
5 changes: 4 additions & 1 deletion yarn-project/circuits.js/src/logs/private_log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import { BufferReader, FieldReader, type Tuple, serializeToBuffer } from '@aztec
import { inspect } from 'util';
import { z } from 'zod';

const PRIVATE_LOG_SIZE_IN_FIELDS_PLUS_ONE = PRIVATE_LOG_SIZE_IN_FIELDS + 1;
export class PrivateLog {
static SIZE_IN_BYTES = Fr.SIZE_IN_BYTES * PRIVATE_LOG_SIZE_IN_FIELDS;

constructor(public fields: Tuple<Fr, typeof PRIVATE_LOG_SIZE_IN_FIELDS>) {}
constructor(
public fields: Tuple<Fr, typeof PRIVATE_LOG_SIZE_IN_FIELDS> | Tuple<Fr, typeof PRIVATE_LOG_SIZE_IN_FIELDS_PLUS_ONE>,
) {}

toFields(): Fr[] {
return this.fields;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { AztecAddress } from '@aztec/circuits.js/aztec-address';
import type { ContractInstanceWithAddress } from '@aztec/circuits.js/contract';
import { PublicKeys } from '@aztec/circuits.js/keys';
import type { PrivateLog } from '@aztec/circuits.js/logs';
import { PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants';
import { Fr } from '@aztec/foundation/fields';
import { BufferReader } from '@aztec/foundation/serialize';

import { strict as assert } from 'assert';

import { DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_TAG } from '../protocol_contract_data.js';

/** Event emitted from the ContractInstanceDeployer. */
Expand All @@ -24,6 +27,12 @@ export class ContractInstanceDeployedEvent {
}

static fromLog(log: PrivateLog) {
assert(
log.fields.length === PRIVATE_LOG_SIZE_IN_FIELDS + 1,
`Logs for contract instance deployed event must have length PRIVATE_LOG_SIZE_IN_FIELDS + 1 (${
PRIVATE_LOG_SIZE_IN_FIELDS + 1
}), but got ${log.fields.length}`,
);
const bufferWithoutTag = log.toBuffer().subarray(32);
const reader = new BufferReader(bufferWithoutTag);
const address = reader.readObject(AztecAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { DEPLOYER_CONTRACT_ADDRESS } from '@aztec/constants';
import { Fr } from '@aztec/foundation/fields';
import { createLogger } from '@aztec/foundation/log';
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
import { computeFeePayerBalanceStorageSlot, getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice';

import { createContractClassAndInstance } from './index.js';
import { type SimpleContractDataSource } from './simple_contract_data_source.js';

/**
Expand Down Expand Up @@ -58,25 +59,31 @@ export abstract class BaseAvmSimulationTester {
seed = 0,
originalContractClassId?: Fr, // if previously upgraded
): Promise<ContractInstanceWithAddress> {
const contractInstance = await this.contractDataSource.registerAndDeployContract(
const { contractClass, contractInstance } = await createContractClassAndInstance(
constructorArgs,
deployer,
contractArtifact,
seed,
originalContractClassId,
);

await this.contractDataSource.addNewContract(contractArtifact, contractClass, contractInstance);

if (!skipNullifierInsertion) {
await this.insertContractAddressNullifier(contractInstance.address);
}
return contractInstance;
}

async registerFeeJuiceContract(): Promise<ContractInstanceWithAddress> {
return await this.contractDataSource.registerFeeJuiceContract();
}

getFirstContractInstance(): ContractInstanceWithAddress {
return this.contractDataSource.getFirstContractInstance();
const feeJuice = await getCanonicalFeeJuice();
const feeJuiceContractClassPublic = {
...feeJuice.contractClass,
privateFunctions: [],
unconstrainedFunctions: [],
};
await this.contractDataSource.addNewContract(feeJuice.artifact, feeJuiceContractClassPublic, feeJuice.instance);
return feeJuice.instance;
}

addContractClass(contractClass: ContractClassPublic, contractArtifact: ContractArtifact): Promise<void> {
Expand Down
62 changes: 61 additions & 1 deletion yarn-project/simulator/src/avm/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@ import { isNoirCallStackUnresolved } from '@aztec/circuit-types';
import { type MerkleTreeWriteOperations } from '@aztec/circuit-types/interfaces/server';
import { type ContractArtifact, type FunctionArtifact, FunctionSelector } from '@aztec/circuits.js/abi';
import { AztecAddress } from '@aztec/circuits.js/aztec-address';
import {
type ContractClassPublic,
type ContractInstanceWithAddress,
computeInitializationHash,
} from '@aztec/circuits.js/contract';
import { GasFees } from '@aztec/circuits.js/gas';
import { siloNullifier } from '@aztec/circuits.js/hash';
import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/circuits.js/testing';
import { GlobalVariables } from '@aztec/circuits.js/tx';
import { MAX_L2_GAS_PER_TX_PUBLIC_PORTION } from '@aztec/constants';
import {
DEPLOYER_CONTRACT_ADDRESS,
MAX_L2_GAS_PER_TX_PUBLIC_PORTION,
PUBLIC_DISPATCH_SELECTOR,
} from '@aztec/constants';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { AvmGadgetsTestContractArtifact } from '@aztec/noir-contracts.js/AvmGadgetsTest';
Expand Down Expand Up @@ -233,3 +244,52 @@ export function resolveAvmGadgetsTestContractAssertionMessage(

return resolveAssertionMessageFromRevertData(output, functionArtifact);
}

/**
* Create a contract class and instance given constructor args, artifact, etc.
* NOTE: This is useful for testing real-ish contract class registration and instance deployment TXs (via logs)
* @param constructorArgs - The constructor arguments for the contract.
* @param deployer - The deployer of the contract.
* @param contractArtifact - The contract artifact for the contract.
* @param seed - The seed for the contract.
* @param originalContractClassId - The original contract class ID (if upgraded)
* @returns The contract class, instance, and contract address nullifier.
*/
export async function createContractClassAndInstance(
constructorArgs: any[],
deployer: AztecAddress,
contractArtifact: ContractArtifact,
seed = 0,
originalContractClassId?: Fr, // if previously upgraded
): Promise<{
contractClass: ContractClassPublic;
contractInstance: ContractInstanceWithAddress;
contractAddressNullifier: Fr;
}> {
const bytecode = getContractFunctionArtifact(PUBLIC_DISPATCH_FN_NAME, contractArtifact)!.bytecode;
const contractClass = await makeContractClassPublic(
seed,
/*publicDispatchFunction=*/ { bytecode, selector: new FunctionSelector(PUBLIC_DISPATCH_SELECTOR) },
);

const constructorAbi = getContractFunctionArtifact('constructor', contractArtifact);
const initializationHash = await computeInitializationHash(constructorAbi, constructorArgs);
const contractInstance =
originalContractClassId === undefined
? await makeContractInstanceFromClassId(contractClass.id, seed, {
deployer,
initializationHash,
})
: await makeContractInstanceFromClassId(originalContractClassId, seed, {
deployer,
initializationHash,
currentClassId: contractClass.id,
});

const contractAddressNullifier = await siloNullifier(
AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
contractInstance.address.toField(),
);

return { contractClass, contractInstance, contractAddressNullifier };
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { type ContractArtifact, FunctionSelector } from '@aztec/circuits.js/abi';
import { type AztecAddress } from '@aztec/circuits.js/aztec-address';
import { AztecAddress } from '@aztec/circuits.js/aztec-address';
import {
type ContractClassPublic,
type ContractDataSource,
type ContractInstanceWithAddress,
type PublicFunction,
computeInitializationHash,
computePublicBytecodeCommitment,
} from '@aztec/circuits.js/contract';
import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/circuits.js/testing';
import { PUBLIC_DISPATCH_SELECTOR } from '@aztec/constants';
import { type Fr } from '@aztec/foundation/fields';
import { createLogger } from '@aztec/foundation/log';
import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice';

import { PUBLIC_DISPATCH_FN_NAME, getContractFunctionArtifact } from './index.js';
import { PUBLIC_DISPATCH_FN_NAME } from './index.js';

/**
* This class is used during public/avm testing to function as a database of
Expand All @@ -39,64 +35,26 @@ export class SimpleContractDataSource implements ContractDataSource {
* Derive the contract class and instance with some seed.
* Add both to the contract data source along with the contract artifact.
*/
async registerAndDeployContract(
constructorArgs: any[],
deployer: AztecAddress,
async addNewContract(
contractArtifact: ContractArtifact,
seed = 0,
originalContractClassId?: Fr, // if previously upgraded
): Promise<ContractInstanceWithAddress> {
const bytecode = getContractFunctionArtifact(PUBLIC_DISPATCH_FN_NAME, contractArtifact)!.bytecode;
const contractClass = await makeContractClassPublic(
seed,
/*publicDispatchFunction=*/ { bytecode, selector: new FunctionSelector(PUBLIC_DISPATCH_SELECTOR) },
);

const constructorAbi = getContractFunctionArtifact('constructor', contractArtifact);
const initializationHash = await computeInitializationHash(constructorAbi, constructorArgs);
this.logger.trace(`Initialization hash for contract class ${contractClass.id}: ${initializationHash.toString()}`);
const contractInstance =
originalContractClassId === undefined
? await makeContractInstanceFromClassId(contractClass.id, seed, {
deployer,
initializationHash,
})
: await makeContractInstanceFromClassId(originalContractClassId, seed, {
deployer,
initializationHash,
currentClassId: contractClass.id,
});

contractClass: ContractClassPublic,
contractInstance: ContractInstanceWithAddress,
) {
this.addContractArtifact(contractClass.id, contractArtifact);
await this.addContractClass(contractClass);
await this.addContractInstance(contractInstance);
return contractInstance;
}

async registerFeeJuiceContract(): Promise<ContractInstanceWithAddress> {
const feeJuice = await getCanonicalFeeJuice();
const feeJuiceContractClassPublic = {
...feeJuice.contractClass,
privateFunctions: [],
unconstrainedFunctions: [],
};

this.addContractArtifact(feeJuiceContractClassPublic.id, feeJuice.artifact);
await this.addContractClass(feeJuiceContractClassPublic);
await this.addContractInstance(feeJuice.instance);
return feeJuice.instance;
}

getFirstContractInstance(): ContractInstanceWithAddress {
return this.contractInstances.values().next().value;
}

addContractArtifact(classId: Fr, artifact: ContractArtifact): void {
this.contractArtifacts.set(classId.toString(), artifact);
}

getContractArtifactByClassId(classId: Fr): ContractArtifact | undefined {
return this.contractArtifacts.get(classId.toString());
}

/////////////////////////////////////////////////////////////
// ContractDataSource function impelementations
// ContractDataSource function implementations
getPublicFunction(_address: AztecAddress, _selector: FunctionSelector): Promise<PublicFunction> {
throw new Error('Method not implemented.');
}
Expand Down
Loading