Skip to content

Commit

Permalink
feat: decommit opcode (#44)
Browse files Browse the repository at this point in the history
# What ❔

<!-- What are the changes this PR brings about? -->
<!-- Example: This PR adds a PR template to the repo. -->
<!-- (For bigger PRs adding more context is appreciated) -->

## Why ❔

<!-- Why are these changes done? What goal do they contribute to? What
are the principles behind them? -->
<!-- Example: PR templates ensure PR reviewers, observers, and future
iterators are in context about the evolution of repos. -->

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk fmt` and `zk lint`.
  • Loading branch information
montekki authored Jun 18, 2024
1 parent 32d4268 commit cbed0c6
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 11 deletions.
7 changes: 6 additions & 1 deletion src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,12 @@ pub(crate) fn decode(raw: u64, is_bootloader: bool) -> Instruction {
out.try_into().unwrap(),
arguments,
),
x => unimplemented_instruction(zkevm_opcode_defs::Opcode::Log(x)),
zkevm_opcode_defs::LogOpcode::Decommit => Instruction::from_decommit(
src1.try_into().unwrap(),
src2,
out.try_into().unwrap(),
arguments,
),
},
zkevm_opcode_defs::Opcode::UMA(x) => {
let increment = parsed.variant.flags[UMA_INCREMENT_FLAG_IDX];
Expand Down
7 changes: 7 additions & 0 deletions src/decommit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ impl WorldDiff {
Some((UnpaidDecommit { cost, code_key }, is_evm))
}

pub(crate) fn decommit_opcode(&mut self, world: &mut dyn World, code_hash: U256) -> Vec<u8> {
let mut code_info_bytes = [0; 32];
code_hash.to_big_endian(&mut code_info_bytes);
self.decommitted_hashes.insert(code_hash, ());
world.decommit_code(code_hash)
}

pub(crate) fn pay_for_decommit(
&mut self,
world: &mut dyn World,
Expand Down
3 changes: 3 additions & 0 deletions src/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ impl HeapInterface for Heap {
}
result
}
fn memset(&mut self, src: &[u8]) {
self.0 = src.to_vec();
}
}

#[derive(Debug, Clone)]
Expand Down
67 changes: 67 additions & 0 deletions src/instruction_handlers/decommit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use u256::U256;
use zkevm_opcode_defs::{BlobSha256Format, ContractCodeSha256Format, VersionedHashLen32};

use crate::{
addressing_modes::{Arguments, Destination, Register1, Register2, Source},
fat_pointer::FatPointer,
instruction::InstructionResult,
Instruction, VirtualMachine, World,
};

use super::{common::instruction_boilerplate, HeapInterface};

fn decommit(
vm: &mut VirtualMachine,
instruction: *const Instruction,
world: &mut dyn World,
) -> InstructionResult {
instruction_boilerplate(vm, instruction, world, |vm, args, world| {
let code_hash = Register1::get(args, &mut vm.state);
let extra_cost = Register2::get(args, &mut vm.state).low_u32();

let mut buffer = [0u8; 32];
code_hash.to_big_endian(&mut buffer);

let preimage_len_in_bytes =
zkevm_opcode_defs::system_params::NEW_KERNEL_FRAME_MEMORY_STIPEND;

if vm.state.use_gas(extra_cost).is_err()
|| (!ContractCodeSha256Format::is_valid(&buffer)
&& !BlobSha256Format::is_valid(&buffer))
{
Register1::set(args, &mut vm.state, U256::zero());
return;
}

let program = vm.world_diff.decommit_opcode(world, code_hash);

let heap = vm.state.heaps.allocate();
vm.state.current_frame.heaps_i_am_keeping_alive.push(heap);
vm.state.heaps[heap].memset(program.as_ref());

let value = FatPointer {
offset: 0,
memory_page: heap,
start: 0,
length: preimage_len_in_bytes,
};
let value = value.into_u256();
Register1::set_fat_ptr(args, &mut vm.state, value);
})
}
impl Instruction {
pub fn from_decommit(
abi: Register1,
burn: Register2,
out: Register1,
arguments: Arguments,
) -> Self {
Self {
arguments: arguments
.write_source(&abi)
.write_source(&burn)
.write_destination(&out),
handler: decommit,
}
}
}
1 change: 1 addition & 0 deletions src/instruction_handlers/heap_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub trait HeapInterface {
fn read_u256_partially(&self, range: Range<u32>) -> U256;
fn write_u256(&mut self, start_address: u32, value: U256);
fn read_range_big_endian(&self, range: Range<u32>) -> Vec<u8>;
fn memset(&mut self, memory: &[u8]);
}

pub trait HeapFromState {
Expand Down
1 change: 1 addition & 0 deletions src/instruction_handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(crate) use ret::{free_panic, PANIC};
mod binop;
mod common;
mod context;
mod decommit;
mod event;
mod far_call;
mod heap_access;
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub trait World {
/// the world implementor's job.
fn decommit(&mut self, hash: U256) -> Program;

fn decommit_code(&mut self, hash: U256) -> Vec<u8>;

/// There is no write_storage; [WorldDiff::get_storage_changes] gives a list of all storage changes.
fn read_storage(&mut self, contract: H160, key: U256) -> Option<U256>;

Expand Down
20 changes: 18 additions & 2 deletions src/single_instruction_test/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ impl HeapInterface for Heap {
// This is wrong, but this method is only used to get the final return value.
vec![]
}

fn memset(&mut self, src: &[u8]) {
let u = U256::from_big_endian(src);
self.write_u256(0, u);
}
}

impl<'a> Arbitrary<'a> for Heap {
Expand All @@ -45,8 +50,9 @@ impl<'a> Arbitrary<'a> for Heap {
}
}

#[derive(Debug, Clone, Arbitrary)]
#[derive(Debug, Clone)]
pub struct Heaps {
heap_id: HeapId,
pub(crate) read: MockRead<HeapId, Heap>,
}

Expand All @@ -60,10 +66,20 @@ impl Heaps {
}

pub(crate) fn allocate(&mut self) -> HeapId {
HeapId(3)
self.heap_id
}

pub(crate) fn deallocate(&mut self, _: HeapId) {}

pub(crate) fn from_id(
heap_id: HeapId,
u: &mut arbitrary::Unstructured<'_>,
) -> arbitrary::Result<Heaps> {
Ok(Heaps {
heap_id,
read: u.arbitrary()?,
})
}
}

impl Index<HeapId> for Heaps {
Expand Down
11 changes: 4 additions & 7 deletions src/single_instruction_test/vm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::stack::StackPool;
use super::{heap::Heaps, stack::StackPool};
use crate::{
callframe::Callframe, fat_pointer::FatPointer, instruction::InstructionResult,
instruction_handlers::free_panic, HeapId, Instruction, Settings, State, VirtualMachine, World,
Expand Down Expand Up @@ -40,11 +40,6 @@ impl VirtualMachine {
}

pub fn instruction_is_not_precompile_call(&self) -> bool {
// TODO PLA-934 implement Decommit
if self.current_opcode() == 1093 {
return false;
}

// TODO PLA-972 implement StaticMemoryRead/Write
if (1096..=1103).contains(&self.current_opcode()) {
return false;
Expand Down Expand Up @@ -81,6 +76,8 @@ impl<'a> Arbitrary<'a> for VirtualMachine {
register_pointer_flags |= (is_pointer as u16) << i;
}

let heaps = Heaps::from_id(current_frame.heap, u)?;

Ok(Self {
state: State {
registers,
Expand All @@ -90,7 +87,7 @@ impl<'a> Arbitrary<'a> for VirtualMachine {
// Exiting the final frame is different in vm2 on purpose,
// so always generate two frames to avoid that.
previous_frames: vec![(0, Callframe::dummy())],
heaps: u.arbitrary()?,
heaps,
transaction_number: u.arbitrary()?,
context_u128: u.arbitrary()?,
},
Expand Down
4 changes: 4 additions & 0 deletions src/single_instruction_test/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ impl World for MockWorld {
Program::for_decommit()
}

fn decommit_code(&mut self, _hash: U256) -> Vec<u8> {
vec![0; 32]
}

fn read_storage(&mut self, contract: H160, key: U256) -> Option<U256> {
*self.storage_slot.get((contract, key))
}
Expand Down
12 changes: 12 additions & 0 deletions src/testworld.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ impl World for TestWorld {
}
}

fn decommit_code(&mut self, hash: U256) -> Vec<u8> {
self.decommit(hash)
.code_page()
.iter()
.flat_map(|u256| {
let mut buffer = [0u8; 32];
u256.to_big_endian(&mut buffer);
buffer
})
.collect()
}

fn read_storage(&mut self, contract: u256::H160, key: u256::U256) -> Option<U256> {
let deployer_system_contract_address =
Address::from_low_u64_be(DEPLOYER_SYSTEM_CONTRACT_ADDRESS_LOW as u64);
Expand Down
6 changes: 5 additions & 1 deletion src/world_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ mod tests {
.map(|(key, value)| (
*key,
StorageChange {
before: first_changes.get(key).or(initial_values.get(&key)).copied(),
before: first_changes.get(key).or(initial_values.get(key)).copied(),
after: *value,
is_initial: initial_values.get(key).is_none(),
tx_number: 1,
Expand Down Expand Up @@ -432,5 +432,9 @@ mod tests {
fn is_free_storage_slot(&self, _: &H160, _: &U256) -> bool {
false
}

fn decommit_code(&mut self, _: U256) -> Vec<u8> {
unimplemented!()
}
}
}

0 comments on commit cbed0c6

Please sign in to comment.