Skip to content

Commit

Permalink
feat: implement L2 to L1 message opcode (#8)
Browse files Browse the repository at this point in the history
* implement L2 to L1 message opcode

* export L2ToL1Log
  • Loading branch information
joonazan authored May 2, 2024
1 parent 07959ae commit 9cc6eb4
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
16 changes: 5 additions & 11 deletions src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,17 +236,11 @@ fn decode(raw: u64, is_bootloader: bool) -> Instruction {
zkevm_opcode_defs::LogOpcode::StorageWrite => {
Instruction::from_sstore(src1.try_into().unwrap(), src2, predicate)
}
// TODO: This is obviously wrong but I want to nop instead of crashing
zkevm_opcode_defs::LogOpcode::ToL1Message => Instruction::from_nop(
AdvanceStackPointer(RegisterAndImmediate {
immediate: 0,
register: Register::new(0),
}),
AdvanceStackPointer(RegisterAndImmediate {
immediate: 0,
register: Register::new(0),
}),
Predicate::Always,
zkevm_opcode_defs::LogOpcode::ToL1Message => Instruction::from_l2_to_l1_message(
src1.try_into().unwrap(),
src2,
parsed.variant.flags[FIRST_MESSAGE_FLAG_IDX],
predicate,
),
zkevm_opcode_defs::LogOpcode::Event => Instruction::from_event(
src1.try_into().unwrap(),
Expand Down
39 changes: 38 additions & 1 deletion src/instruction_handlers/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{common::instruction_boilerplate_with_panic, free_panic};
use crate::{
addressing_modes::{Arguments, Immediate1, Register1, Register2, Source},
instruction::InstructionResult,
modified_world::Event,
modified_world::{Event, L2ToL1Log},
Instruction, Predicate, VirtualMachine,
};
use u256::H160;
Expand Down Expand Up @@ -31,6 +31,28 @@ fn event(vm: &mut VirtualMachine, instruction: *const Instruction) -> Instructio
})
}

fn l2_to_l1(vm: &mut VirtualMachine, instruction: *const Instruction) -> InstructionResult {
instruction_boilerplate_with_panic(vm, instruction, |vm, args, continue_normally| {
if vm.state.current_frame.is_static {
return free_panic(vm);
}

let key = Register1::get(args, &mut vm.state);
let value = Register2::get(args, &mut vm.state);
let is_service = Immediate1::get(args, &mut vm.state).low_u32() == 1;
vm.world.record_l2_to_l1_log(L2ToL1Log {
key,
value,
is_service,
address: vm.state.current_frame.address,
shard_id: 0,
tx_number: vm.state.transaction_number,
});

continue_normally
})
}

impl Instruction {
pub fn from_event(
key: Register1,
Expand All @@ -46,4 +68,19 @@ impl Instruction {
.write_source(&Immediate1(is_first.into())),
}
}

pub fn from_l2_to_l1_message(
key: Register1,
value: Register2,
is_service: bool,
predicate: Predicate,
) -> Self {
Self {
handler: l2_to_l1,
arguments: Arguments::new(predicate, 34)
.write_source(&key)
.write_source(&value)
.write_source(&Immediate1(is_service.into())),
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use u256::{H160, U256};
pub use decommit::address_into_u256;
pub use decommit::initial_decommit;
pub use instruction::{jump_to_beginning, ExecutionEnd, Instruction};
pub use modified_world::{Event, ExternalSnapshot, ModifiedWorld};
pub use modified_world::{Event, ExternalSnapshot, L2ToL1Log, ModifiedWorld};
pub use predication::Predicate;
pub use program::Program;
pub use state::{State, FIRST_HEAP};
Expand Down
34 changes: 31 additions & 3 deletions src/modified_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct ModifiedWorld {
// These are rolled back on revert or panic (and when the whole VM is rolled back).
storage_changes: RollbackableMap<(H160, U256), U256>,
events: RollbackableLog<Event>,
l2_to_l1_logs: RollbackableLog<L2ToL1Log>,

// The field below are only rolled back when the whole VM is rolled back.
pub(crate) decommitted_hashes: RollbackableSet<U256>,
Expand All @@ -28,6 +29,7 @@ pub struct ModifiedWorld {
pub struct ExternalSnapshot {
storage_changes: <RollbackableMap<(H160, U256), U256> as Rollback>::Snapshot,
events: <RollbackableLog<Event> as Rollback>::Snapshot,
l2_to_l1_logs: <RollbackableLog<L2ToL1Log> as Rollback>::Snapshot,

// The field below are only rolled back when the whole VM is rolled back.
pub(crate) decommitted_hashes: <RollbackableMap<U256, ()> as Rollback>::Snapshot,
Expand All @@ -46,12 +48,22 @@ pub struct Event {
pub tx_number: u16,
}

pub struct L2ToL1Log {
pub key: U256,
pub value: U256,
pub is_service: bool,
pub address: H160,
pub shard_id: u8,
pub tx_number: u16,
}

impl ModifiedWorld {
pub fn new(world: Box<dyn World>) -> Self {
Self {
world,
storage_changes: Default::default(),
events: Default::default(),
l2_to_l1_logs: Default::default(),
decommitted_hashes: Default::default(),
read_storage_slots: Default::default(),
written_storage_slots: Default::default(),
Expand Down Expand Up @@ -103,27 +115,41 @@ impl ModifiedWorld {
self.storage_changes.as_ref()
}

pub fn record_event(&mut self, event: Event) {
pub(crate) fn record_event(&mut self, event: Event) {
self.events.push(event);
}

pub fn events(&self) -> &[Event] {
self.events.as_ref()
}

pub(crate) fn record_l2_to_l1_log(&mut self, log: L2ToL1Log) {
self.l2_to_l1_logs.push(log);
}

pub fn l2_to_l1_logs(&self) -> &[L2ToL1Log] {
self.l2_to_l1_logs.as_ref()
}

pub(crate) fn snapshot(&self) -> Snapshot {
(self.storage_changes.snapshot(), self.events.snapshot())
(
self.storage_changes.snapshot(),
self.events.snapshot(),
self.l2_to_l1_logs.snapshot(),
)
}

pub(crate) fn rollback(&mut self, (storage, events): Snapshot) {
pub(crate) fn rollback(&mut self, (storage, events, l2_to_l1_logs): Snapshot) {
self.storage_changes.rollback(storage);
self.events.rollback(events);
self.l2_to_l1_logs.rollback(l2_to_l1_logs);
}

pub fn external_snapshot(&mut self) -> ExternalSnapshot {
ExternalSnapshot {
storage_changes: self.storage_changes.snapshot(),
events: self.events.snapshot(),
l2_to_l1_logs: self.l2_to_l1_logs.snapshot(),
decommitted_hashes: self.decommitted_hashes.snapshot(),
read_storage_slots: self.read_storage_slots.snapshot(),
written_storage_slots: self.written_storage_slots.snapshot(),
Expand All @@ -133,6 +159,7 @@ impl ModifiedWorld {
pub fn external_rollback(&mut self, snapshot: ExternalSnapshot) {
self.storage_changes.rollback(snapshot.storage_changes);
self.events.rollback(snapshot.events);
self.l2_to_l1_logs.rollback(snapshot.l2_to_l1_logs);
self.decommitted_hashes
.rollback(snapshot.decommitted_hashes);
self.read_storage_slots
Expand All @@ -157,6 +184,7 @@ impl ModifiedWorld {
pub(crate) type Snapshot = (
<RollbackableMap<(H160, U256), U256> as Rollback>::Snapshot,
<RollbackableLog<Event> as Rollback>::Snapshot,
<RollbackableLog<L2ToL1Log> as Rollback>::Snapshot,
);

const WARM_READ_REFUND: u32 = STORAGE_ACCESS_COLD_READ_COST - STORAGE_ACCESS_WARM_READ_COST;
Expand Down

0 comments on commit 9cc6eb4

Please sign in to comment.