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: implement kernel mode #42

Merged
merged 70 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
50964da
only kernel addresses may use certain instructions
joonazan Jun 4, 2024
d678af9
pubdata can be read only in kernel mode
joonazan Jun 4, 2024
1a3f586
implement pointer erasure
joonazan Jun 4, 2024
af250b5
comment explaining heap growth vs backing DS
joonazan Jun 4, 2024
606efa0
fix a few finish_frame related mistakes in zk_evm fuzzing
joonazan Jun 4, 2024
45be3f2
avoid edge case where final frame is popped
joonazan Jun 4, 2024
3ac1055
set is_kernel in a zk_evm compatible way
joonazan Jun 4, 2024
e2369f4
save a few bytes by not generating r0
joonazan Jun 4, 2024
29e7597
implement aux_mutating
joonazan Jun 4, 2024
5314813
translate near calls to zk_evm
joonazan Jun 4, 2024
88d0eeb
do not compare the current frame's sp in case of panic
joonazan Jun 5, 2024
66af4bc
implement heap for zk_evm
joonazan Jun 5, 2024
81b97b5
deal with different far call panic handling
joonazan Jun 5, 2024
11fdacd
pay for return from far call as appropriate
joonazan Jun 6, 2024
e557934
lint
joonazan Jun 6, 2024
700f91f
check all free panics before even running the instruction
joonazan Jun 6, 2024
29ef86f
implement return value of jump
joonazan Jun 6, 2024
ebbd8b8
implement enough decommit for far call to go through
joonazan Jun 6, 2024
de441c1
fix initial sp
joonazan Jun 6, 2024
c9dcf74
grow the heap to u32::MAX instead of paying u32::MAX
joonazan Jun 6, 2024
48a2b3d
filter out unimplemented instructions
joonazan Jun 6, 2024
9ca0994
implement zk_evm heap writes
joonazan Jun 6, 2024
b8dbb29
replicate nonsensical behaviour in zk_evm
joonazan Jun 6, 2024
f32a21b
fix heap bounds comparison
joonazan Jun 6, 2024
b9cf12d
add comments to remember to make vm sane again as soon as zk_evm is
joonazan Jun 7, 2024
508453d
support aux heap, too
joonazan Jun 7, 2024
cca6fb5
more accurate far call heap growth
joonazan Jun 7, 2024
4816074
fix possible overflow
joonazan Jun 7, 2024
b28b39a
restrict system calls and constructor calls
joonazan Jun 7, 2024
d2bf214
call default aa on constructor call mismatch, too
joonazan Jun 7, 2024
5b9a3e3
far call: stop doing things immediately on failure
joonazan Jun 7, 2024
360ec54
copy heap read bug to heap write, too
joonazan Jun 7, 2024
6d84774
get second param of ptr as non-pointer like defined in opcode_defs
joonazan Jun 7, 2024
4b0c04b
heap accesses don't take pointers
joonazan Jun 7, 2024
6f5f922
grow heap on out of bounds access regardless of other errors
joonazan Jun 10, 2024
00730ee
fix bug in heap mock
joonazan Jun 10, 2024
20246db
mock storage write
joonazan Jun 10, 2024
fb7f017
replicate transient storage behaviour of vm2
joonazan Jun 10, 2024
5992e9c
allow fat pointer read in mock
joonazan Jun 10, 2024
095801c
validate that calldata heap is different from the current heap
joonazan Jun 10, 2024
f2578c8
never make evm interpreter static
joonazan Jun 10, 2024
69fe5ea
perfectly replicates far call heap growth
joonazan Jun 10, 2024
2570290
panic on constructor call mismatch to kernel address
joonazan Jun 10, 2024
5a876a9
remove old fuzzer
joonazan Jun 10, 2024
f215bb5
support shard calls to panic when one is attempted
joonazan Jun 10, 2024
60b326e
add comments explaining already_failed situation
joonazan Jun 10, 2024
a4a871d
generate small enough gas to fit EVM stipend
joonazan Jun 10, 2024
c09a558
set static call to EVM flag correctly
joonazan Jun 10, 2024
281a123
correct mistake in default aa logic
joonazan Jun 10, 2024
a3c301d
temporarily remove assertion
joonazan Jun 11, 2024
c9acc0f
make calldata come from smaller heap
joonazan Jun 11, 2024
edccc37
do not mask pointers going into load and store
joonazan Jun 11, 2024
e5bd627
generate heap numbers that make sense for vm2 and zk_evm
joonazan Jun 12, 2024
4a28421
make kernel addresses much more likely
joonazan Jun 12, 2024
f3b4116
remove todo! from start_new_tx
joonazan Jun 12, 2024
cddf041
check pointer flags on ptr instruction's second argument
joonazan Jun 12, 2024
ff0ac10
craft kernel to kernel far call as seed
joonazan Jun 12, 2024
66f8bc0
only system calls can have extra cost
joonazan Jun 12, 2024
eb7db89
fix case where mandated gas was not paid but was given back
joonazan Jun 12, 2024
8314001
the top half of generated pointer is no longer zero
joonazan Jun 12, 2024
4b9f044
allow kernel contracts to return their calldata
joonazan Jun 12, 2024
ad745a4
add missing already failed check
joonazan Jun 12, 2024
8c3f1ee
craft really intricate inputs
joonazan Jun 12, 2024
4f01eb4
clippy lints
joonazan Jun 12, 2024
526a065
burn all gas if there is not enough left for the mandated gas
joonazan Jun 13, 2024
be18683
remove superfluous is_static checks
joonazan Jun 14, 2024
c79a2fb
enable tracing with trace feature
joonazan Jun 14, 2024
32d4268
update tracing code
joonazan Jun 14, 2024
cbed0c6
feat: decommit opcode (#44)
montekki Jun 18, 2024
4931b48
chore: stop storing tx number (#45)
joonazan Jun 20, 2024
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ zk_evm_abstractions = {git = "https://github.com/matter-labs/era-zk_evm_abstract
u256 = { package = "primitive-types", version = "0.12.1" }
enum_dispatch = "0.3"
arbitrary = { version = "1", features = ["derive"], optional = true }
zk_evm = { git = "https://github.com/matter-labs/era-zk_evm.git", branch = "v1.5.0", optional = true }
zk_evm = { git = "https://github.com/matter-labs/era-zk_evm.git", branch = "jms-remove-assert", optional = true }
anyhow = { version = "1", optional = true }

[dev-dependencies]
Expand All @@ -26,3 +26,4 @@ harness = false

[features]
single_instruction_test = ["arbitrary", "u256/arbitrary", "zk_evm", "anyhow"]
trace = []
1 change: 0 additions & 1 deletion afl-fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
in
out
Binary file not shown.
Binary file added afl-fuzz/in/return_calldata
Binary file not shown.
21 changes: 16 additions & 5 deletions afl-fuzz/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use differential_fuzzing::VmAndWorld;
use vm2::single_instruction_test::{vm2_to_zk_evm, NoTracer, UniversalVmState};
use vm2::single_instruction_test::{add_heap_to_zk_evm, vm2_to_zk_evm, NoTracer, UniversalVmState};

fn main() {
afl::fuzz!(|data: &[u8]| {
Expand All @@ -9,17 +9,28 @@ fn main() {
// 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.

let mut zk_evm = vm2_to_zk_evm(&vm, world.clone());
let mut zk_evm = vm2_to_zk_evm(
&vm,
world.clone(),
vm.state.current_frame.pc_from_u16(0).unwrap(),
);

let _ = vm.run_single_instruction(&mut world);
let pc = vm.run_single_instruction(&mut world).unwrap();
assert!(vm.is_in_valid_state());

add_heap_to_zk_evm(&mut zk_evm, &vm);
let _ = zk_evm.cycle(&mut NoTracer);

// vm2 does not build a frame for a failed far call, so we need to run the panic
// to get a meaningful comparison.
if vm.instruction_is_far_call() && zk_evm.local_state.pending_exception {
let _ = zk_evm.cycle(&mut NoTracer);
}

assert_eq!(
UniversalVmState::from(zk_evm),
vm2_to_zk_evm(&vm, world.clone()).into()
vm2_to_zk_evm(&vm, world.clone(), pc).into()
);
// TODO compare emitted events, storage changes and pubdata
}
}
});
Expand Down
19 changes: 16 additions & 3 deletions afl-fuzz/src/show_testcase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use differential_fuzzing::VmAndWorld;
use pretty_assertions::assert_eq;
use std::env;
use std::fs;
use vm2::single_instruction_test::add_heap_to_zk_evm;
use vm2::single_instruction_test::vm2_to_zk_evm;
use vm2::single_instruction_test::NoTracer;
use vm2::single_instruction_test::UniversalVmState;
Expand All @@ -20,22 +21,34 @@ fn main() {
println!("{:?}", vm.state);
assert!(vm.is_in_valid_state());

let mut zk_evm = vm2_to_zk_evm(&vm, world.clone());
let mut zk_evm = vm2_to_zk_evm(
&vm,
world.clone(),
vm.state.current_frame.pc_from_u16(0).unwrap(),
);

let (parsed, _) = EncodingModeProduction::parse_preliminary_variant_and_absolute_number(
vm.state.current_frame.raw_first_instruction(),
);
println!("{}", parsed);
let _ = vm.run_single_instruction(&mut world);
let pc = vm.run_single_instruction(&mut world).unwrap();

println!("Mocks that have been touched:");
vm.print_mock_info();

assert!(vm.is_in_valid_state());

add_heap_to_zk_evm(&mut zk_evm, &vm);
let _ = zk_evm.cycle(&mut NoTracer);

// vm2 does not build a frame for a failed far call, so we need to run the panic
// to get a meaningful comparison.
if vm.instruction_is_far_call() && zk_evm.local_state.pending_exception {
let _ = zk_evm.cycle(&mut NoTracer);
}

assert_eq!(
UniversalVmState::from(zk_evm),
vm2_to_zk_evm(&vm, world.clone()).into()
vm2_to_zk_evm(&vm, world.clone(), pc).into()
);
}
13 changes: 8 additions & 5 deletions benches/nested_near_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use vm2::{
addressing_modes::{Arguments, Immediate1, Immediate2, Register, Register1, Register2},
initial_decommit,
testworld::TestWorld,
Instruction,
Instruction, ModeRequirements,
Predicate::Always,
Program,
};
Expand All @@ -17,7 +17,7 @@ fn nested_near_call(bencher: Bencher) {
Register1(Register::new(0)),
Immediate1(0),
Immediate2(0),
Arguments::new(Always, 10),
Arguments::new(Always, 10, ModeRequirements::none()),
)],
vec![],
);
Expand Down Expand Up @@ -48,19 +48,22 @@ fn nested_near_call(bencher: Bencher) {
fn nested_near_call_with_storage_write(bencher: Bencher) {
let program = Program::new(
vec![
Instruction::from_ergs_left(Register1(Register::new(1)), Arguments::new(Always, 5)),
Instruction::from_ergs_left(
Register1(Register::new(1)),
Arguments::new(Always, 5, ModeRequirements::none()),
),
Instruction::from_sstore(
// always use same storage slot to get a warm write discount
Register1(Register::new(0)),
Register2(Register::new(1)),
Arguments::new(Always, 5511), // need to use actual cost to not create free gas from refunds
Arguments::new(Always, 5511, ModeRequirements::none()), // need to use actual cost to not create free gas from refunds
),
Instruction::from_near_call(
// zero means pass all gas
Register1(Register::new(0)),
Immediate1(0),
Immediate2(0),
Arguments::new(Always, 25),
Arguments::new(Always, 25, ModeRequirements::none()),
),
],
vec![],
Expand Down
4 changes: 0 additions & 4 deletions fuzz/.gitignore

This file was deleted.

30 changes: 0 additions & 30 deletions fuzz/Cargo.toml

This file was deleted.

1 change: 0 additions & 1 deletion fuzz/fuzz.sh

This file was deleted.

38 changes: 0 additions & 38 deletions fuzz/fuzz_targets/fuzz_target_1.rs

This file was deleted.

50 changes: 44 additions & 6 deletions src/addressing_modes.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
use crate::predication::Predicate;
use crate::{mode_requirements::ModeRequirements, predication::Predicate};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use enum_dispatch::enum_dispatch;
use u256::U256;
use zkevm_opcode_defs::erase_fat_pointer_metadata;

pub(crate) trait Source {
/// Get a word's value for non-pointer operations. (Pointers are erased.)
fn get(args: &Arguments, state: &mut impl Addressable) -> U256 {
Self::get_with_pointer_flag(args, state).0
Self::get_with_pointer_flag_and_erasing(args, state).0
}

/// Get a word's value and pointer flag.
fn get_with_pointer_flag(args: &Arguments, state: &mut impl Addressable) -> (U256, bool) {
(Self::get(args, state), false)
}

/// Get a word's value, erasing pointers but also returning the pointer flag.
/// The flag will always be false unless in kernel mode.
/// Necessary for pointer operations, which for some reason erase their second argument
/// but also panic when it was a pointer.
fn get_with_pointer_flag_and_erasing(
args: &Arguments,
state: &mut impl Addressable,
) -> (U256, bool) {
let (mut value, is_pointer) = Self::get_with_pointer_flag(args, state);
if is_pointer && !state.in_kernel_mode() {
erase_fat_pointer_metadata(&mut value)
}
(value, is_pointer && state.in_kernel_mode())
}
}

pub(crate) trait Destination {
Expand All @@ -35,6 +54,8 @@ pub trait Addressable {
fn clear_stack_pointer_flag(&mut self, slot: u16);

fn code_page(&self) -> &[U256];

fn in_kernel_mode(&self) -> bool;
}

#[enum_dispatch]
Expand Down Expand Up @@ -63,13 +84,14 @@ impl<T: DestinationWriter> DestinationWriter for Option<T> {
}
}

#[derive(Hash, Debug)]
// It is important for performance that this fits into 8 bytes.
#[derive(Debug)]
pub struct Arguments {
source_registers: PackedRegisters,
destination_registers: PackedRegisters,
immediate1: u16,
immediate2: u16,
pub predicate: Predicate,
predicate_and_mode_requirements: u8,
static_gas_cost: u8,
}

Expand All @@ -79,13 +101,21 @@ pub(crate) const SLOAD_COST: u32 = 2008;
pub(crate) const INVALID_INSTRUCTION_COST: u32 = 4294967295;

impl Arguments {
pub const fn new(predicate: Predicate, gas_cost: u32) -> Self {
pub const fn new(
predicate: Predicate,
gas_cost: u32,
mode_requirements: ModeRequirements,
) -> Self {
// Make sure that these two can be packed into 8 bits without overlapping
assert!(predicate as u8 & (0b11 << 6) == 0);
assert!(mode_requirements.0 & !(0b11) == 0);

Self {
source_registers: PackedRegisters(0),
destination_registers: PackedRegisters(0),
immediate1: 0,
immediate2: 0,
predicate,
predicate_and_mode_requirements: (predicate as u8) << 2 | mode_requirements.0,
static_gas_cost: Self::encode_static_gas_cost(gas_cost),
}
}
Expand Down Expand Up @@ -117,6 +147,14 @@ impl Arguments {
}
}

pub(crate) fn predicate(&self) -> Predicate {
unsafe { std::mem::transmute(self.predicate_and_mode_requirements >> 2) }
}

pub(crate) fn mode_requirements(&self) -> ModeRequirements {
ModeRequirements(self.predicate_and_mode_requirements & 0b11)
}

pub(crate) fn write_source(mut self, sw: &impl SourceWriter) -> Self {
sw.write_source(&mut self);
self
Expand Down
Loading
Loading