Skip to content

Commit

Permalink
feat(avm): get contract instance now works e2e with avm proving (#6911)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbanks12 authored Jun 7, 2024
1 parent d35ee2e commit 662187d
Show file tree
Hide file tree
Showing 16 changed files with 311 additions and 62 deletions.
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

1 comment on commit 662187d

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: 662187d Previous: d35ee2e Ratio
Goblin::merge(t) 211429476 ns/iter 197876654 ns/iter 1.07

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.