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-simulator): implement AVM message opcodes (simulator/transpiler/noir-test) #4852

Merged
merged 1 commit into from
Mar 4, 2024
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
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
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()
Maddiaa0 marked this conversation as resolved.
Show resolved Hide resolved
}

#[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
Loading