Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(avm): basic AVM-ACVM interoperability #5593

Closed
Closed
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
1 change: 0 additions & 1 deletion avm-transpiler/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ pub const ALL_DIRECT: u8 = 0b00000000;
pub const ZEROTH_OPERAND_INDIRECT: u8 = 0b00000001;
pub const FIRST_OPERAND_INDIRECT: u8 = 0b00000010;
pub const SECOND_OPERAND_INDIRECT: u8 = 0b00000100;
pub const ZEROTH_FIRST_OPERANDS_INDIRECT: u8 = ZEROTH_OPERAND_INDIRECT | FIRST_OPERAND_INDIRECT;

/// A simple representation of an AVM instruction for the purpose
/// of generating an AVM bytecode from Brillig.
Expand Down
3 changes: 0 additions & 3 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
BinaryIntOp::Xor => AvmOpcode::XOR,
BinaryIntOp::Shl => AvmOpcode::SHL,
BinaryIntOp::Shr => AvmOpcode::SHR,
_ => panic!(
"Transpiler doesn't know how to process {:?}", brillig_instr
),
};
avm_instrs.push(AvmInstruction {
opcode: avm_opcode,
Expand Down
1 change: 0 additions & 1 deletion avm-transpiler/src/transpile_contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use base64::Engine;
use log::info;
use regex::Regex;
use serde::{Deserialize, Serialize};

use acvm::acir::circuit::Program;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ contract AvmTest {
// Libs
use dep::aztec::prelude::Map;
use dep::aztec::state_vars::PublicMutable;
use dep::aztec::history::nullifier_inclusion::prove_nullifier_inclusion;
use dep::aztec::protocol_types::{
address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH,
contract_instance::ContractInstance
contract_instance::ContractInstance,
hash::silo_nullifier,
};
use dep::aztec::oracle::get_contract_instance::{get_contract_instance_avm, get_contract_instance_internal_avm};
use dep::aztec::protocol_types::abis::function_selector::FunctionSelector;
Expand Down Expand Up @@ -174,6 +176,60 @@ contract AvmTest {
dep::std::hash::pedersen_hash_with_separator(data, 20)
}

/************************************************************************
* ACVM interoperability
************************************************************************/
#[aztec(public)]
fn constant_field_acvm() -> pub Field {
123456
}

#[aztec(public-vm)]
fn constant_field_avm() -> pub Field {
123456
}

#[aztec(public)]
fn new_nullifier_acvm(nullifier: Field) -> pub Field {
context.push_new_nullifier(nullifier, 0);
}

#[aztec(public)]
fn assert_unsiloed_nullifier_acvm(nullifier: Field) {
// ACVM requires siloed nullifier.
let siloed_nullifier = silo_nullifier(context.this_address(), nullifier);
prove_nullifier_inclusion(siloed_nullifier, context);
}

#[aztec(public-vm)]
fn call_acvm_from_avm() -> pub Field {
let data_to_return: [Field; RETURN_VALUES_LENGTH] = context.call_public_function(
context.this_address(),
FunctionSelector::from_signature("constant_field_acvm()"),
[]
);
data_to_return[0]
}

#[aztec(public)]
fn call_avm_from_acvm() -> pub Field {
let data_to_return: [Field; RETURN_VALUES_LENGTH] = context.call_public_function(
context.this_address(),
FunctionSelector::from_signature("constant_field_avm()"),
[]
);
data_to_return[0]
}

#[aztec(public-vm)]
fn avm_to_acvm_call(selector: FunctionSelector, args: Field) {
context.call_public_function(
context.this_address(),
selector,
[args]
);
}

/************************************************************************
* Contract instance
************************************************************************/
Expand Down
36 changes: 35 additions & 1 deletion yarn-project/end-to-end/src/e2e_avm_simulator.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AztecAddress, Fr, TxStatus, type Wallet } from '@aztec/aztec.js';
import { AztecAddress, Fr, FunctionSelector, TxStatus, type Wallet } from '@aztec/aztec.js';
import { AvmInitializerTestContract, AvmTestContract } from '@aztec/noir-contracts.js';

import { jest } from '@jest/globals';
Expand Down Expand Up @@ -64,6 +64,40 @@ describe('e2e_avm_simulator', () => {
expect(tx.status).toEqual(TxStatus.MINED);
});
});

describe('ACVM interoperability', () => {
it('Can execute ACVM function among AVM functions', async () => {
expect(await avmContract.methods.constant_field_acvm().simulate()).toEqual([123456n, 0n, 0n, 0n]);
});

it('Can call AVM function from ACVM', async () => {
expect(await avmContract.methods.call_avm_from_acvm().simulate()).toEqual([123456n, 0n, 0n, 0n]);
});

it('Can call ACVM function from AVM', async () => {
expect(await avmContract.methods.call_acvm_from_avm().simulate()).toEqual([123456n, 0n, 0n, 0n]);
});

// Cannot work because ACVM does not support pending nullifiers.
// it('AVM->ACVM nullifiers work (pending)', async () => {
// await avmContract.methods.avm_to_acvm_nullifier().send().wait();
// });

it('AVM sees settled nullifiers by ACVM', async () => {
const nullifier = new Fr(123456);
await avmContract.methods.new_nullifier(nullifier).send().wait();
await avmContract.methods.assert_unsiloed_nullifier_acvm(nullifier).send().wait();
});

it('AVM nested call to ACVM sees settled nullifiers', async () => {
const nullifier = new Fr(123456);
await avmContract.methods.new_nullifier(nullifier).send().wait();
await avmContract.methods
.avm_to_acvm_call(FunctionSelector.fromSignature('assert_unsiloed_nullifier_acvm(Field)'), nullifier)
.send()
.wait();
});
});
});

describe('AvmInitializerTestContract', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ export abstract class AbstractPhaseManager {
while (executionStack.length) {
const current = executionStack.pop()!;
const isExecutionRequest = !isPublicExecutionResult(current);

const sideEffectCounter = lastSideEffectCounter(tx) + 1;

const result = isExecutionRequest
? await this.publicExecutor.simulate(current, this.globalVariables, sideEffectCounter)
: current;
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/simulator/src/avm/avm_execution_environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ export class AvmExecutionEnvironment {
}

public deriveEnvironmentForNestedCall(
address: AztecAddress,
targetAddress: AztecAddress,
calldata: Fr[],
temporaryFunctionSelector: FunctionSelector = FunctionSelector.empty(),
): AvmExecutionEnvironment {
return new AvmExecutionEnvironment(
address,
/*storageAddress=*/ address,
targetAddress,
/*storageAddress=*/ targetAddress,
this.origin,
this.sender,
this.address,
this.portal,
this.feePerL1Gas,
this.feePerL2Gas,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AvmTestContractArtifact } from '@aztec/noir-contracts.js';
import { jest } from '@jest/globals';
import { strict as assert } from 'assert';

import { isAvmBytecode } from '../public/transitional_adaptors.js';
import { AvmMachineState } from './avm_machine_state.js';
import { TypeTag } from './avm_memory_types.js';
import { AvmSimulator } from './avm_simulator.js';
Expand All @@ -23,7 +24,6 @@ import {
} from './fixtures/index.js';
import { Add, CalldataCopy, Return } from './opcodes/index.js';
import { encodeToBytecode } from './serialization/bytecode_serialization.js';
import { isAvmBytecode } from './temporary_executor_migration.js';

describe('AVM simulator: injected bytecode', () => {
let calldata: Fr[];
Expand Down
11 changes: 6 additions & 5 deletions yarn-project/simulator/src/avm/journal/journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,19 @@ export class AvmPersistableStateManager {
/** Reference to node storage */
public readonly hostStorage: HostStorage;

// TODO: make members private once this is not used in transitional_adaptors.ts.
/** World State */
/** Public storage, including cached writes */
private publicStorage: PublicStorage;
public publicStorage: PublicStorage;
/** Nullifier set, including cached/recently-emitted nullifiers */
private nullifiers: Nullifiers;
public nullifiers: Nullifiers;

/** World State Access Trace */
private trace: WorldStateAccessTrace;
public trace: WorldStateAccessTrace;

/** Accrued Substate **/
private newL1Messages: L2ToL1Message[] = [];
private newLogs: UnencryptedL2Log[] = [];
public newL1Messages: L2ToL1Message[] = [];
public newLogs: UnencryptedL2Log[] = [];

constructor(hostStorage: HostStorage, parent?: AvmPersistableStateManager) {
this.hostStorage = hostStorage;
Expand Down
26 changes: 22 additions & 4 deletions yarn-project/simulator/src/avm/opcodes/external_calls.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { FunctionSelector } from '@aztec/circuits.js';
import { padArrayEnd } from '@aztec/foundation/collection';

import { executePublicFunction } from '../../public/executor.js';
import {
adjustAvmContextFromPublicExecutionResult,
convertPublicExecutionResult,
createPublicExecutionContext,
} from '../../public/transitional_adaptors.js';
import type { AvmContext } from '../avm_context.js';
import { gasLeftToGas, sumGas } from '../avm_gas.js';
import { Field, Uint8 } from '../avm_memory_types.js';
import { AvmSimulator } from '../avm_simulator.js';
import { type AvmContractCallResults } from '../avm_message_call_result.js';
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
import { Addressing } from './addressing_mode.js';
import { Instruction } from './instruction.js';
Expand Down Expand Up @@ -61,15 +67,27 @@ abstract class ExternalCall extends Instruction {
const totalGas = sumGas(this.gasCost(memoryOperations), allocatedGas);
context.machineState.consumeGas(totalGas);

// TRANSITIONAL: This should be removed once the AVM is fully operational and the public executor is gone.
const nestedContext = context.createNestedContractCallContext(
callAddress.toFr(),
calldata,
allocatedGas,
this.type,
FunctionSelector.fromField(functionSelector),
);
const pxContext = createPublicExecutionContext(nestedContext, calldata);
const pxResults = await executePublicFunction(pxContext, /*nested=*/ true);
const nestedCallResults: AvmContractCallResults = convertPublicExecutionResult(pxResults);
adjustAvmContextFromPublicExecutionResult(nestedContext, pxResults);
const nestedPersistableState = nestedContext.persistableState;
// const nestedContext = context.createNestedContractCallContext(
// callAddress.toFr(),
// calldata,
// FunctionSelector.fromField(functionSelector),
// );
// const nestedCallResults: AvmContractCallResults = await new AvmSimulator(nestedContext).execute();
// const nestedPersistableState = nestedContext.persistableState;

const nestedCallResults = await new AvmSimulator(nestedContext).execute();
const success = !nestedCallResults.reverted;

// We only take as much data as was specified in the return size and pad with zeroes if the return data is smaller
Expand All @@ -90,9 +108,9 @@ abstract class ExternalCall extends Instruction {

// TODO: Should we merge the changes from a nested call in the case of a STATIC call?
if (success) {
context.persistableState.acceptNestedCallState(nestedContext.persistableState);
context.persistableState.acceptNestedCallState(nestedPersistableState);
} else {
context.persistableState.rejectNestedCallState(nestedContext.persistableState);
context.persistableState.rejectNestedCallState(nestedPersistableState);
}

memory.assert(memoryOperations);
Expand Down
Loading
Loading