Skip to content

Commit

Permalink
feat(avm-simulator): implement AVM message opcodes (simulator/transpi…
Browse files Browse the repository at this point in the history
…ler/noir-test) (#4852)

Closes #4837 
Closes #4835 

Updated spec for both opcodes according to latest plan (and to more closely match existing Noir interface for private).

Misc cleanup.

[Latest discussion on `L1TOL2MSGEXISTS` opcode](https://aztecprotocol.slack.com/archives/C03P17YHVK8/p1709072769364739)
  • Loading branch information
dbanks12 committed Mar 4, 2024
1 parent a5e26e7 commit c98325d
Show file tree
Hide file tree
Showing 22 changed files with 632 additions and 173 deletions.
4 changes: 2 additions & 2 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub enum AvmOpcode {
EMITNOTEHASH, // Notes & Nullifiers
NULLIFIEREXISTS, // Notes & Nullifiers
EMITNULLIFIER, // Notes & Nullifiers
READL1TOL2MSG, // Messages
L1TOL2MSGEXISTS, // Messages
HEADERMEMBER, // Archive tree & Headers

// Accrued Substate
Expand Down Expand Up @@ -154,7 +154,7 @@ impl AvmOpcode {
AvmOpcode::EMITNOTEHASH => "EMITNOTEHASH", // Notes & Nullifiers
AvmOpcode::NULLIFIEREXISTS => "NULLIFIEREXISTS", // Notes & Nullifiers
AvmOpcode::EMITNULLIFIER => "EMITNULLIFIER", // Notes & Nullifiers
AvmOpcode::READL1TOL2MSG => "READL1TOL2MSG", // Messages
AvmOpcode::L1TOL2MSGEXISTS => "L1TOL2MSGEXISTS", // Messages
AvmOpcode::HEADERMEMBER => "HEADERMEMBER", // Archive tree & Headers

// Accrued Substate
Expand Down
95 changes: 95 additions & 0 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ fn handle_foreign_call(
inputs,
),
"nullifierExists" => handle_nullifier_exists(avm_instrs, destinations, inputs),
"l1ToL2MsgExists" => handle_l1_to_l2_msg_exists(avm_instrs, destinations, inputs),
"sendL2ToL1Msg" => handle_send_l2_to_l1_msg(avm_instrs, destinations, inputs),
"keccak256" | "sha256" => {
handle_2_field_hash_instruction(avm_instrs, function, destinations, inputs)
}
Expand Down Expand Up @@ -382,6 +384,99 @@ fn handle_nullifier_exists(
});
}

/// Handle an AVM L1TOL2MSGEXISTS instruction
/// (a l1ToL2MsgExists brillig foreign call was encountered)
/// Adds the new instruction to the avm instructions list.
fn handle_l1_to_l2_msg_exists(
avm_instrs: &mut Vec<AvmInstruction>,
destinations: &Vec<ValueOrArray>,
inputs: &Vec<ValueOrArray>,
) {
if destinations.len() != 1 || inputs.len() != 2 {
panic!(
"Transpiler expects ForeignCall::L1TOL2MSGEXISTS to have 1 destinations and 2 input, got {} and {}",
destinations.len(),
inputs.len()
);
}
let msg_hash_offset_operand = match &inputs[0] {
ValueOrArray::MemoryAddress(offset) => offset.to_usize() as u32,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::L1TOL2MSGEXISTS with HeapArray/Vector inputs",
),
};
let msg_leaf_index_offset_operand = match &inputs[1] {
ValueOrArray::MemoryAddress(offset) => offset.to_usize() as u32,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::L1TOL2MSGEXISTS with HeapArray/Vector inputs",
),
};
let exists_offset_operand = match &destinations[0] {
ValueOrArray::MemoryAddress(offset) => offset.to_usize() as u32,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::L1TOL2MSGEXISTS with HeapArray/Vector inputs",
),
};
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::L1TOL2MSGEXISTS,
indirect: Some(ALL_DIRECT),
operands: vec![
AvmOperand::U32 {
value: msg_hash_offset_operand,
},
AvmOperand::U32 {
value: msg_leaf_index_offset_operand,
},
AvmOperand::U32 {
value: exists_offset_operand,
},
],
..Default::default()
});
}

/// Handle an AVM SENDL2TOL1MSG
/// (a sendL2ToL1Msg brillig foreign call was encountered)
/// Adds the new instruction to the avm instructions list.
fn handle_send_l2_to_l1_msg(
avm_instrs: &mut Vec<AvmInstruction>,
destinations: &Vec<ValueOrArray>,
inputs: &Vec<ValueOrArray>,
) {
if destinations.len() != 0 || inputs.len() != 2 {
panic!(
"Transpiler expects ForeignCall::SENDL2TOL1MSG to have 0 destinations and 2 inputs, got {} and {}",
destinations.len(),
inputs.len()
);
}
let recipient_offset_operand = match &inputs[0] {
ValueOrArray::MemoryAddress(offset) => offset.to_usize() as u32,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::SENDL2TOL1MSG with HeapArray/Vector inputs",
),
};
let content_offset_operand = match &inputs[1] {
ValueOrArray::MemoryAddress(offset) => offset.to_usize() as u32,
_ => panic!(
"Transpiler does not know how to handle ForeignCall::SENDL2TOL1MSG with HeapArray/Vector inputs",
),
};
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::SENDL2TOL1MSG,
indirect: Some(ALL_DIRECT),
operands: vec![
AvmOperand::U32 {
value: recipient_offset_operand,
},
AvmOperand::U32 {
value: content_offset_operand,
},
],
..Default::default()
});
}

/// Two field hash instructions represent instruction's that's outputs are larger than a field element
///
/// This includes:
Expand Down
4 changes: 2 additions & 2 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const std::unordered_map<OpCode, size_t> Bytecode::OPERANDS_NUM = {
//{ OpCode::EMITNOTEHASH, }, // Notes & Nullifiers
//{ OpCode::NULLIFIEREXISTS, }, // Notes & Nullifiers
//{ OpCode::EMITNULLIFIER, }, // Notes & Nullifiers
//{ OpCode::READL1TOL2MSG, }, // Messages
//{ OpCode::L1TOL2MSGEXISTS, }, // Messages
//{ OpCode::HEADERMEMBER, },

//// Accrued Substate
Expand Down Expand Up @@ -146,7 +146,7 @@ bool Bytecode::has_in_tag(OpCode const op_code)
case OpCode::EMITNOTEHASH:
case OpCode::NULLIFIEREXISTS:
case OpCode::EMITNULLIFIER:
case OpCode::READL1TOL2MSG:
case OpCode::L1TOL2MSGEXISTS:
case OpCode::HEADERMEMBER:
case OpCode::EMITUNENCRYPTEDLOG:
case OpCode::SENDL2TOL1MSG:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ enum class OpCode : uint8_t {
EMITNOTEHASH, // Notes & Nullifiers
NULLIFIEREXISTS, // Notes & Nullifiers
EMITNULLIFIER, // Notes & Nullifiers
READL1TOL2MSG, // Messages
L1TOL2MSGEXISTS, // Messages
HEADERMEMBER, // Archive tree & Headers

// Accrued Substate
Expand Down
35 changes: 30 additions & 5 deletions noir-projects/aztec-nr/aztec/src/context/avm.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use dep::protocol_types::address::{AztecAddress, EthAddress};
use dep::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH};

// Getters that will be converted by the transpiler into their
// own opcodes
Expand All @@ -10,6 +10,7 @@ impl AVMContext {
Self {}
}

// OPCODES
#[oracle(address)]
pub fn address(self) -> AztecAddress {}

Expand Down Expand Up @@ -56,16 +57,40 @@ impl AVMContext {
pub fn emit_note_hash(self, note_hash: Field) {}

#[oracle(nullifierExists)]
pub fn check_nullifier_exists(self, nullifier: Field) -> u8 {}
pub fn nullifier_exists(self, nullifier: Field) -> u8 {}

#[oracle(emitNullifier)]
pub fn emit_nullifier(self, nullifier: Field) {}

#[oracle(l1ToL2MsgExists)]
pub fn l1_to_l2_msg_exists(self, msg_hash: Field, msg_leaf_index: Field) -> u8 {}

#[oracle(sendL2ToL1Msg)]
pub fn send_l2_to_l1_msg(self, recipient: EthAddress, content: Field) {}

///////////////////////////////////////////////////////////////////////////
// The functions below allow interface-equivalence with PrivateContext
// for emitting note hashes and nullifiers
pub fn push_new_note_hash(self: &mut Self, note_hash: Field) {
self.emit_note_hash(note_hash);
///////////////////////////////////////////////////////////////////////////
pub fn this_address(self) -> AztecAddress {
self.address()
}

#[oracle(sendL2ToL1Msg)]
pub fn message_portal(&mut self, recipient: EthAddress, content: Field) {}

pub fn consume_l1_to_l2_message(
&mut self,
_msg_key: Field,
_content: Field,
_secret: Field,
_sender: EthAddress
) {
assert(false, "Not implemented!");
}

#[oracle(emitNoteHash)]
pub fn push_new_note_hash(self: &mut Self, note_hash: Field) {}

pub fn push_new_nullifier(self: &mut Self, nullifier: Field, _nullified_commitment: Field) {
// Cannot nullify pending commitments in AVM, so `nullified_commitment` is not used
self.emit_nullifier(nullifier);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
contract AvmTest {
// Libs
use dep::aztec::protocol_types::address::{AztecAddress, EthAddress};
use dep::aztec::protocol_types::{address::{AztecAddress, EthAddress}, constants::L1_TO_L2_MESSAGE_LENGTH};

// avm lib
use dep::aztec::avm::hash::{keccak256, poseidon, sha256};
Expand Down Expand Up @@ -159,15 +159,15 @@ contract AvmTest {

// Use the standard context interface to emit a new nullifier
#[aztec(public-vm)]
fn check_nullifier_exists(nullifier: Field) -> pub u8 {
context.check_nullifier_exists(nullifier)
fn nullifier_exists(nullifier: Field) -> pub u8 {
context.nullifier_exists(nullifier)
}

// Use the standard context interface to emit a new nullifier
#[aztec(public-vm)]
fn emit_nullifier_and_check(nullifier: Field) {
context.emit_nullifier(nullifier);
let exists = context.check_nullifier_exists(nullifier);
let exists = context.nullifier_exists(nullifier);
assert(exists == 1, "Nullifier was just created, but its existence wasn't detected!");
}

Expand All @@ -178,4 +178,14 @@ contract AvmTest {
// Can't do this twice!
context.push_new_nullifier(nullifier, 0);
}

#[aztec(public-vm)]
fn l1_to_l2_msg_exists(msg_hash: Field, msg_leaf_index: Field) -> pub u8 {
context.l1_to_l2_msg_exists(msg_hash, msg_leaf_index)
}

#[aztec(public-vm)]
fn send_l2_to_l1_msg(recipient: EthAddress, content: Field) {
context.message_portal(recipient, content)
}
}
Loading

0 comments on commit c98325d

Please sign in to comment.