Skip to content

Commit

Permalink
feat(avm): get_args_hash in user-space
Browse files Browse the repository at this point in the history
  • Loading branch information
fcarreiro committed Sep 9, 2024
1 parent eab944c commit 22202cb
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 11 deletions.
42 changes: 42 additions & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ fn handle_foreign_call(
"avmOpcodeGetContractInstance" => {
handle_get_contract_instance(avm_instrs, destinations, inputs);
}
"avmOpcodeCalldataCopy" => handle_calldata_copy(avm_instrs, destinations, inputs),
"avmOpcodeStorageRead" => handle_storage_read(avm_instrs, destinations, inputs),
"avmOpcodeStorageWrite" => handle_storage_write(avm_instrs, destinations, inputs),
"debugLog" => handle_debug_log(avm_instrs, destinations, inputs),
Expand Down Expand Up @@ -974,6 +975,47 @@ fn handle_debug_log(
});
}

// #[oracle(avmOpcodeCalldataCopy)]
// unconstrained fn calldata_copy_opcode<let N: u32>(cdoffset: Field) -> [Field; N] {}
fn handle_calldata_copy(
avm_instrs: &mut Vec<AvmInstruction>,
destinations: &Vec<ValueOrArray>,
inputs: &Vec<ValueOrArray>,
) {
assert!(inputs.len() == 2);
assert!(destinations.len() == 1);

let cd_offset = match inputs[0] {
ValueOrArray::MemoryAddress(address) => address.0,
_ => panic!("CalldataCopy offset should be a memory address"),
};

let copy_size_offset = match inputs[1] {
ValueOrArray::MemoryAddress(address) => address.0,
_ => panic!("CalldataCopy size should be a memory address"),
};

let (dest_offset, ..) = match destinations[0] {
ValueOrArray::HeapArray(HeapArray { pointer, size }) => (pointer.0, size),
_ => panic!("CalldataCopy destination should be an array"),
};

avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::CALLDATACOPY,
indirect: Some(SECOND_OPERAND_INDIRECT),
operands: vec![
AvmOperand::U32 {
value: cd_offset as u32, // cdOffset (calldata offset)
},
AvmOperand::U32 { value: copy_size_offset as u32 }, // copy size
AvmOperand::U32 {
value: dest_offset as u32, // dstOffset
},
],
..Default::default()
});
}

/// Emit a storage write opcode
/// The current implementation writes an array of values into storage ( contiguous slots in memory )
fn handle_storage_write(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use dep::protocol_types::traits::Empty;

// These inputs will likely go away once the AVM processes 1 public kernel per enqueued call.
struct PublicContextInputs {
args_hash: Field,
// TODO: Remove this structure and get calldata size at compile time.
calldata_length: Field,
is_static_call: bool
}

impl Empty for PublicContextInputs {
fn empty() -> Self {
PublicContextInputs {
args_hash: 0,
calldata_length: 0,
is_static_call: false
}
}
Expand Down
27 changes: 24 additions & 3 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ use dep::protocol_types::traits::{Serialize, Deserialize, Empty};
use dep::protocol_types::abis::function_selector::FunctionSelector;
use crate::context::inputs::public_context_inputs::PublicContextInputs;
use crate::context::gas::GasOpts;
use crate::hash::ArgsHasher;

struct PublicContext {
inputs: PublicContextInputs,
args_hash: Option<Field>
}

impl PublicContext {
pub fn new(inputs: PublicContextInputs) -> Self {
PublicContext { inputs }
PublicContext { inputs, args_hash: Option::none() }
}

pub fn emit_unencrypted_log<T, let N: u32>(_self: &mut Self, log: T) where T: Serialize<N> {
Expand Down Expand Up @@ -130,8 +132,20 @@ impl PublicContext {
fn selector(_self: Self) -> FunctionSelector {
FunctionSelector::from_u32(function_selector())
}
fn get_args_hash(self) -> Field {
self.inputs.args_hash
fn get_args_hash(mut self) -> Field {
if !self.args_hash.is_some() {
let mut hasher = ArgsHasher::new();

// TODO: this should be replaced with the compile-time calldata size.
for i in 0..self.inputs.calldata_length as u32 {
let argn: [Field; 1] = calldata_copy((2 + i) as u32, 1);
hasher.add(argn[0]);
}

self.args_hash = Option::some(hasher.hash());
}

self.args_hash.unwrap()
}
fn transaction_fee(_self: Self) -> Field {
transaction_fee()
Expand Down Expand Up @@ -278,6 +292,10 @@ unconstrained fn call_static<let RET_SIZE: u32>(
call_static_opcode(gas, address, args, function_selector)
}

unconstrained fn calldata_copy<let N: u32>(cdoffset: u32, copy_size: u32) -> [Field; N] {
calldata_copy_opcode(cdoffset, copy_size)
}

unconstrained fn storage_read(storage_slot: Field) -> Field {
storage_read_opcode(storage_slot)
}
Expand Down Expand Up @@ -356,6 +374,9 @@ unconstrained fn l1_to_l2_msg_exists_opcode(msg_hash: Field, msg_leaf_index: Fie
#[oracle(avmOpcodeSendL2ToL1Msg)]
unconstrained fn send_l2_to_l1_msg_opcode(recipient: EthAddress, content: Field) {}

#[oracle(avmOpcodeCalldataCopy)]
unconstrained fn calldata_copy_opcode<let N: u32>(cdoffset: u32, copy_size: u32) -> [Field; N] {}

#[oracle(avmOpcodeCall)]
unconstrained fn call_opcode<let RET_SIZE: u32>(
gas: [Field; 2], // gas allocation: [l2_gas, da_gas]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl TestEnvironment {
cheatcodes::set_contract_address(target_address);
cheatcodes::set_msg_sender(original_contract_address);
let mut inputs = cheatcodes::get_public_context_inputs();
inputs.args_hash = hash_args(call_interface.get_args());
inputs.calldata_length = call_interface.get_args().len() as Field;
inputs.is_static_call = call_interface.get_is_static();
let result = original_fn(inputs);

Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/test/helpers/utils.nr
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<let N: u32, let M: u32> Deployer<N, M> {
cheatcodes::set_contract_address(instance.to_address());
cheatcodes::set_msg_sender(original_contract_address);
let mut inputs = cheatcodes::get_public_context_inputs();
inputs.args_hash = hash_args(call_interface.get_args());
inputs.calldata_length = call_interface.get_args().len() as Field;
let _result: T = original_fn(inputs);

cheatcodes::set_fn_selector(original_fn_selector);
Expand Down
7 changes: 3 additions & 4 deletions yarn-project/simulator/src/avm/avm_execution_environment.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { FunctionSelector, type GlobalVariables, type Header } from '@aztec/circuits.js';
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
import { type AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';

export class AvmContextInputs {
static readonly SIZE = 2;

constructor(private argsHash: Fr, private isStaticCall: boolean) {}
constructor(private calldataSize: Fr, private isStaticCall: boolean) {}

public toFields(): Fr[] {
return [this.argsHash, new Fr(this.isStaticCall)];
return [this.calldataSize, new Fr(this.isStaticCall)];
}
}

Expand All @@ -33,7 +32,7 @@ export class AvmExecutionEnvironment {
) {
// We encode some extra inputs (AvmContextInputs) in calldata.
// This will have to go once we move away from one proof per call.
const inputs = new AvmContextInputs(computeVarArgsHash(calldata), isStaticCall).toFields();
const inputs = new AvmContextInputs(new Fr(calldata.length), isStaticCall).toFields();
this.calldata = [...inputs, ...calldata];
}

Expand Down

0 comments on commit 22202cb

Please sign in to comment.