Skip to content
Merged
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
12 changes: 9 additions & 3 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ inline void read(uint8_t const*& it, ExternalCallHint& hint)
}

struct ContractInstanceHint {
FF address;
FF instance_found_in_address;
FF salt;
FF deployer_addr;
Expand All @@ -73,6 +74,7 @@ struct ContractInstanceHint {
inline void read(uint8_t const*& it, ContractInstanceHint& hint)
{
using serialize::read;
read(it, hint.address);
read(it, hint.instance_found_in_address);
read(it, hint.salt);
read(it, hint.deployer_addr);
Expand All @@ -87,7 +89,6 @@ struct ExecutionHints {
std::vector<std::pair<FF, FF>> nullifier_exists_hints;
std::vector<std::pair<FF, FF>> l1_to_l2_message_exists_hints;
std::vector<ExternalCallHint> externalcall_hints;
// TODO(dbanks): not read yet.
std::map<FF, ContractInstanceHint> contract_instance_hints;

ExecutionHints() = default;
Expand Down Expand Up @@ -150,8 +151,6 @@ struct ExecutionHints {
std::vector<std::pair<FF, FF>> note_hash_exists_hints;
std::vector<std::pair<FF, FF>> nullifier_exists_hints;
std::vector<std::pair<FF, FF>> l1_to_l2_message_exists_hints;
// TODO(dbanks): not read yet.
std::map<FF, ContractInstanceHint> contract_instance_hints;

using serialize::read;
const auto* it = data.data();
Expand All @@ -163,6 +162,13 @@ struct ExecutionHints {
std::vector<ExternalCallHint> externalcall_hints;
read(it, externalcall_hints);

std::vector<ContractInstanceHint> contract_instance_hints_vec;
read(it, contract_instance_hints_vec);
std::map<FF, ContractInstanceHint> contract_instance_hints;
for (const auto& instance : contract_instance_hints_vec) {
contract_instance_hints[instance.address] = instance;
}

return { std::move(storage_value_hints), std::move(note_hash_exists_hints),
std::move(nullifier_exists_hints), std::move(l1_to_l2_message_exists_hints),
std::move(externalcall_hints), std::move(contract_instance_hints) };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2595,6 +2595,7 @@ void AvmTraceBuilder::op_get_contract_instance(uint8_t indirect, uint32_t addres
// Read the contract instance
ContractInstanceHint contract_instance = execution_hints.contract_instance_hints.at(read_address.val);

// NOTE: we don't write the first entry (the contract instance's address/key) to memory
std::vector<FF> contract_instance_vec = { contract_instance.instance_found_in_address,
contract_instance.salt,
contract_instance.deployer_addr,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2000,10 +2000,12 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcodes)
std::vector<FF> returndata = {};

// Generate Hint for call operation
auto execution_hints = ExecutionHints().with_contract_instance_hints({ { address, { 1, 1, 2, 3, 4, 5 } } });
// Note: opcode does not write 'address' into memory
auto execution_hints =
ExecutionHints().with_contract_instance_hints({ { address, { address, 1, 2, 3, 4, 5, 6 } } });

auto trace = Execution::gen_trace(instructions, returndata, calldata, public_inputs_vec, execution_hints);
EXPECT_EQ(returndata, std::vector<FF>({ 1, 1, 2, 3, 4, 5 })); // The first one represents true
EXPECT_EQ(returndata, std::vector<FF>({ 1, 2, 3, 4, 5, 6 })); // The first one represents true

validate_trace(std::move(trace));
}
Expand Down
36 changes: 33 additions & 3 deletions yarn-project/bb-prover/src/avm_proving.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,25 @@ import { computeVarArgsHash } from '@aztec/circuits.js/hash';
import { padArrayEnd } from '@aztec/foundation/collection';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { AvmSimulator, type PublicExecutionResult } from '@aztec/simulator';
import { getAvmTestContractBytecode, initContext, initExecutionEnvironment } from '@aztec/simulator/avm/fixtures';
import { AvmSimulator, type PublicContractsDB, type PublicExecutionResult } from '@aztec/simulator';
import {
getAvmTestContractBytecode,
initContext,
initExecutionEnvironment,
initHostStorage,
} from '@aztec/simulator/avm/fixtures';

import { mock } from 'jest-mock-extended';
import fs from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'path';

import { AvmPersistableStateManager } from '../../simulator/src/avm/journal/journal.js';
import {
convertAvmResultsToPxResult,
createPublicExecution,
} from '../../simulator/src/public/transitional_adaptors.js';
import { SerializableContractInstance } from '../../types/src/contracts/contract_instance.js';
import { type BBSuccess, BB_RESULT, generateAvmProof, verifyAvmProof } from './bb/execute.js';
import { extractVkData } from './verification_key/verification_key_data.js';

Expand Down Expand Up @@ -70,6 +78,14 @@ describe('AVM WitGen, proof generation and verification', () => {
TIMEOUT,
);

it(
'Should prove get contract instance opcode with hints',
async () => {
await proveAndVerifyAvmTestContract('test_get_contract_instance');
},
TIMEOUT,
);

it(
'Should prove new note hash',
async () => {
Expand Down Expand Up @@ -175,7 +191,21 @@ it.each(avmContextFunctions)(
const proveAndVerifyAvmTestContract = async (functionName: string, calldata: Fr[] = []) => {
const startSideEffectCounter = 0;
const environment = initExecutionEnvironment({ calldata });
const context = initContext({ env: environment });

const contractsDb = mock<PublicContractsDB>();
const contractInstance = new SerializableContractInstance({
version: 1,
salt: new Fr(0x123),
deployer: new Fr(0x456),
contractClassId: new Fr(0x789),
initializationHash: new Fr(0x101112),
publicKeysHash: new Fr(0x161718),
}).withAddress(environment.address);
contractsDb.getContractInstance.mockResolvedValue(Promise.resolve(contractInstance));

const hostStorage = initHostStorage({ contractsDb });
const persistableState = new AvmPersistableStateManager(hostStorage);
const context = initContext({ env: environment, persistableState });

const startGas = new Gas(context.machineState.gasLeft.daGas, context.machineState.gasLeft.l2Gas);
const oldPublicExecution = createPublicExecution(startSideEffectCounter, environment, calldata);
Expand Down
118 changes: 106 additions & 12 deletions yarn-project/circuits.js/src/structs/avm/avm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class AvmKeyValueHint {
* @param buffer - Buffer or reader to read from.
* @returns The deserialized instance.
*/
static fromBuffer(buff: Buffer | BufferReader): AvmKeyValueHint {
static fromBuffer(buff: Buffer | BufferReader) {
const reader = BufferReader.asReader(buff);
return new AvmKeyValueHint(Fr.fromBuffer(reader), Fr.fromBuffer(reader));
}
Expand Down Expand Up @@ -147,25 +147,124 @@ export class AvmExternalCallHint {
}
}

export class AvmContractInstanceHint {
constructor(
public readonly address: Fr,
public readonly exists: Fr,
public readonly salt: Fr,
public readonly deployer: Fr,
public readonly contractClassId: Fr,
public readonly initializationHash: Fr,
public readonly publicKeysHash: Fr,
) {}
/**
* Serializes the inputs to a buffer.
* @returns - The inputs serialized to a buffer.
*/
toBuffer() {
return serializeToBuffer(...AvmContractInstanceHint.getFields(this));
}

/**
* Serializes the inputs to a hex string.
* @returns The instance serialized to a hex string.
*/
toString() {
return this.toBuffer().toString('hex');
}

/**
* Is the struct empty?
* @returns whether all members are empty.
*/
isEmpty(): boolean {
return (
this.address.isZero() &&
this.exists.isZero() &&
this.salt.isZero() &&
this.deployer.isZero() &&
this.contractClassId.isZero() &&
this.initializationHash.isZero() &&
this.publicKeysHash.isZero()
);
}

/**
* Creates a new instance from fields.
* @param fields - Fields to create the instance from.
* @returns A new AvmHint instance.
*/
static from(fields: FieldsOf<AvmContractInstanceHint>): AvmContractInstanceHint {
return new AvmContractInstanceHint(...AvmContractInstanceHint.getFields(fields));
}

/**
* Extracts fields from an instance.
* @param fields - Fields to create the instance from.
* @returns An array of fields.
*/
static getFields(fields: FieldsOf<AvmContractInstanceHint>) {
return [
fields.address,
fields.exists,
fields.salt,
fields.deployer,
fields.contractClassId,
fields.initializationHash,
fields.publicKeysHash,
] as const;
}

/**
* Deserializes from a buffer or reader.
* @param buffer - Buffer or reader to read from.
* @returns The deserialized instance.
*/
static fromBuffer(buff: Buffer | BufferReader): AvmContractInstanceHint {
const reader = BufferReader.asReader(buff);
return new AvmContractInstanceHint(
Fr.fromBuffer(reader),
Fr.fromBuffer(reader),
Fr.fromBuffer(reader),
Fr.fromBuffer(reader),
Fr.fromBuffer(reader),
Fr.fromBuffer(reader),
Fr.fromBuffer(reader),
);
}

/**
* Deserializes from a hex string.
* @param str - Hex string to read from.
* @returns The deserialized instance.
*/
static fromString(str: string): AvmContractInstanceHint {
return AvmContractInstanceHint.fromBuffer(Buffer.from(str, 'hex'));
}
}

export class AvmExecutionHints {
public readonly storageValues: Vector<AvmKeyValueHint>;
public readonly noteHashExists: Vector<AvmKeyValueHint>;
public readonly nullifierExists: Vector<AvmKeyValueHint>;
public readonly l1ToL2MessageExists: Vector<AvmKeyValueHint>;
public readonly externalCalls: Vector<AvmExternalCallHint>;
public readonly contractInstances: Vector<AvmContractInstanceHint>;

constructor(
storageValues: AvmKeyValueHint[],
noteHashExists: AvmKeyValueHint[],
nullifierExists: AvmKeyValueHint[],
l1ToL2MessageExists: AvmKeyValueHint[],
externalCalls: AvmExternalCallHint[],
contractInstances: AvmContractInstanceHint[],
) {
this.storageValues = new Vector(storageValues);
this.noteHashExists = new Vector(noteHashExists);
this.nullifierExists = new Vector(nullifierExists);
this.l1ToL2MessageExists = new Vector(l1ToL2MessageExists);
this.externalCalls = new Vector(externalCalls);
this.contractInstances = new Vector(contractInstances);
}

/**
Expand Down Expand Up @@ -194,7 +293,8 @@ export class AvmExecutionHints {
this.noteHashExists.items.length == 0 &&
this.nullifierExists.items.length == 0 &&
this.l1ToL2MessageExists.items.length == 0 &&
this.externalCalls.items.length == 0
this.externalCalls.items.length == 0 &&
this.contractInstances.items.length == 0
);
}

Expand All @@ -210,6 +310,7 @@ export class AvmExecutionHints {
fields.nullifierExists.items,
fields.l1ToL2MessageExists.items,
fields.externalCalls.items,
fields.contractInstances.items,
);
}

Expand All @@ -225,18 +326,10 @@ export class AvmExecutionHints {
fields.nullifierExists,
fields.l1ToL2MessageExists,
fields.externalCalls,
fields.contractInstances,
] as const;
}

flat() {
return [
...this.storageValues.items,
...this.noteHashExists.items,
...this.nullifierExists.items,
...this.l1ToL2MessageExists.items,
];
}

/**
* Deserializes from a buffer or reader.
* @param buffer - Buffer or reader to read from.
Expand All @@ -250,6 +343,7 @@ export class AvmExecutionHints {
reader.readVector(AvmKeyValueHint),
reader.readVector(AvmKeyValueHint),
reader.readVector(AvmExternalCallHint),
reader.readVector(AvmContractInstanceHint),
);
}

Expand All @@ -267,7 +361,7 @@ export class AvmExecutionHints {
* @returns The empty instance.
*/
static empty() {
return new AvmExecutionHints([], [], [], [], []);
return new AvmExecutionHints([], [], [], [], [], []);
}
}

Expand Down
21 changes: 20 additions & 1 deletion yarn-project/circuits.js/src/tests/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ARGS_LENGTH,
AppendOnlyTreeSnapshot,
AvmCircuitInputs,
AvmContractInstanceHint,
AvmExecutionHints,
AvmExternalCallHint,
AvmKeyValueHint,
Expand Down Expand Up @@ -1277,7 +1278,7 @@ export function makeAvmKeyValueHint(seed = 0): AvmKeyValueHint {
/**
* Makes arbitrary AvmExternalCallHint.
* @param seed - The seed to use for generating the state reference.
* @returns AvmKeyValueHint.
* @returns AvmExternalCallHint.
*/
export function makeAvmExternalCallHint(seed = 0): AvmExternalCallHint {
return new AvmExternalCallHint(
Expand All @@ -1287,6 +1288,23 @@ export function makeAvmExternalCallHint(seed = 0): AvmExternalCallHint {
);
}

/**
* Makes arbitrary AvmContractInstanceHint.
* @param seed - The seed to use for generating the state reference.
* @returns AvmContractInstanceHint.
*/
export function makeAvmContractInstanceHint(seed = 0): AvmContractInstanceHint {
return new AvmContractInstanceHint(
new Fr(seed),
new Fr(seed + 0x1),
new Fr(seed + 0x2),
new Fr(seed + 0x3),
new Fr(seed + 0x4),
new Fr(seed + 0x5),
new Fr(seed + 0x6),
);
}

/**
* Creates arbitrary AvmExecutionHints.
* @param seed - The seed to use for generating the hints.
Expand All @@ -1306,6 +1324,7 @@ export function makeAvmExecutionHints(
nullifierExists: makeVector(baseLength + 2, makeAvmKeyValueHint, seed + 0x4400),
l1ToL2MessageExists: makeVector(baseLength + 3, makeAvmKeyValueHint, seed + 0x4500),
externalCalls: makeVector(baseLength + 4, makeAvmExternalCallHint, seed + 0x4600),
contractInstances: makeVector(baseLength + 5, makeAvmContractInstanceHint, seed + 0x4700),
...overrides,
});
}
Expand Down
Loading