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: decommit opcode #44

Merged
merged 12 commits into from
Jun 18, 2024
Merged
3 changes: 3 additions & 0 deletions afl-fuzz/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ fn main() {
if vm.is_in_valid_state() && vm.instruction_is_not_precompile_call() {
// Tests that running one instruction and converting to zk_evm produces the same result as
// first converting to zk_evm and then running one instruction.
vm.state
montekki marked this conversation as resolved.
Show resolved Hide resolved
.heaps
.set_heap_id(vm.state.current_frame.heap.to_u32());

let mut zk_evm = vm2_to_zk_evm(
&vm,
Expand Down
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
31 changes: 29 additions & 2 deletions src/decommit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ impl WorldDiff {
Some((UnpaidDecommit { cost, code_key }, is_evm))
}

pub(crate) fn decommit_opcode(&mut self, code_hash: U256) -> Option<UnpaidDecommit> {
let mut code_info_bytes = [0; 32];
code_hash.to_big_endian(&mut code_info_bytes);

let cost = if self.decommitted_hashes.as_ref().contains_key(&code_hash) {
0
} else {
let code_length_in_words = u16::from_be_bytes([code_info_bytes[2], code_info_bytes[3]]);
code_length_in_words as u32 * zkevm_opcode_defs::ERGS_PER_CODE_WORD_DECOMMITTMENT
};

Some(UnpaidDecommit {
cost,
code_key: code_hash,
})
}

pub(crate) fn pay_for_decommit(
&mut self,
world: &mut dyn World,
Expand All @@ -91,11 +108,21 @@ impl WorldDiff {
self.decommitted_hashes.insert(decommit.code_key, ());
Some(world.decommit(decommit.code_key))
}

pub(crate) fn unpaid_decommit(
montekki marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
world: &mut dyn World,
decommit: UnpaidDecommit,
) -> Option<Program> {
self.decommitted_hashes.insert(decommit.code_key, ());
Some(world.decommit(decommit.code_key))
}
}

#[derive(Debug)]
pub(crate) struct UnpaidDecommit {
cost: u32,
code_key: U256,
pub cost: u32,
joonazan marked this conversation as resolved.
Show resolved Hide resolved
pub code_key: U256,
}

/// May be used to load code when the VM first starts up.
Expand Down
1 change: 1 addition & 0 deletions src/fat_pointer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::heap::HeapId;
use u256::U256;

#[derive(Debug)]
#[repr(C)]
pub struct FatPointer {
pub offset: u32,
Expand Down
5 changes: 5 additions & 0 deletions src/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ impl HeapInterface for Heap {
}
result
}
fn memset(&mut self, src: &[U256]) {
for (i, word) in src.iter().enumerate() {
self.write_u256((i * 32) as u32, *word);
}
}
}

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

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

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

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

let mut decommit_preimage_format_is_invalid = false;
let mut buffer = [0u8; 32];
code_hash.to_big_endian(&mut buffer);
let mut _decommit_preimage_normalized = VersionedHashNormalizedPreimage::default();
montekki marked this conversation as resolved.
Show resolved Hide resolved

let mut preimage_len_in_bytes =
zkevm_opcode_defs::system_params::NEW_KERNEL_FRAME_MEMORY_STIPEND;
let mut _decommit_header = VersionedHashHeader::default();
joonazan marked this conversation as resolved.
Show resolved Hide resolved

if ContractCodeSha256Format::is_valid(&buffer) {
let (header, normalized_preimage) =
ContractCodeSha256Format::normalize_for_decommitment(&buffer);
_decommit_header = header;
_decommit_preimage_normalized = normalized_preimage;
} else if BlobSha256Format::is_valid(&buffer) {
let (header, normalized_preimage) =
BlobSha256Format::normalize_for_decommitment(&buffer);
_decommit_header = header;
_decommit_preimage_normalized = normalized_preimage;
} else {
preimage_len_in_bytes = 0;
decommit_preimage_format_is_invalid = true;
};

if vm.state.use_gas(extra_cost).is_err() {
montekki marked this conversation as resolved.
Show resolved Hide resolved
Register1::set(args, &mut vm.state, U256::zero());
return continue_normally;
}

if decommit_preimage_format_is_invalid {
let value = U256::zero();
Register1::set(args, &mut vm.state, value);
return continue_normally;
}

let Some(unpaid_decommit) = vm.world_diff.decommit_opcode(code_hash) else {
Register1::set(args, &mut vm.state, U256::zero());
return continue_normally;
montekki marked this conversation as resolved.
Show resolved Hide resolved
};

let decommit_result = vm.world_diff.unpaid_decommit(world, unpaid_decommit);

let heap = vm.state.heaps.allocate();
montekki marked this conversation as resolved.
Show resolved Hide resolved
let program = &decommit_result.unwrap();
let decommited_memory = program.code_page().as_ref();
let mut _length: u32 = decommited_memory.len().try_into().unwrap();
_length *= 32;

vm.state.heaps[heap].memset(decommited_memory);

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);

continue_normally
},
)
}
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: &[U256]);
}

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
13 changes: 12 additions & 1 deletion src/single_instruction_test/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ 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: &[U256]) {
for (i, word) in src.iter().enumerate() {
self.write_u256((i * 32) as u32, *word);
montekki marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

impl<'a> Arbitrary<'a> for Heap {
Expand All @@ -47,6 +53,7 @@ impl<'a> Arbitrary<'a> for Heap {

#[derive(Debug, Clone, Arbitrary)]
joonazan marked this conversation as resolved.
Show resolved Hide resolved
pub struct Heaps {
heap_id: u32,
pub(crate) read: MockRead<HeapId, Heap>,
}

Expand All @@ -60,10 +67,14 @@ impl Heaps {
}

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

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

pub fn set_heap_id(&mut self, heap_id: u32) {
self.heap_id = heap_id
}
}

impl Index<HeapId> for Heaps {
Expand Down
22 changes: 10 additions & 12 deletions src/single_instruction_test/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,17 @@ 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;
}
self.current_opcode() == 1093
montekki marked this conversation as resolved.
Show resolved Hide resolved
/*
// TODO PLA-972 implement StaticMemoryRead/Write
if (1096..=1103).contains(&self.current_opcode()) {
return false;
}

// Precompilecall is not allowed because it accesses memory multiple times
// and only needs to work as used by trusted code
self.current_opcode() != 1056u64
// Precompilecall is not allowed because it accesses memory multiple times
// and only needs to work as used by trusted code
self.current_opcode() != 1056u64
*/
}

pub fn instruction_is_far_call(&self) -> bool {
Expand Down
Loading