Skip to content

Commit

Permalink
feat: Stable tracer interface (#46)
Browse files Browse the repository at this point in the history
Co-authored-by: Fedor Sakharov <[email protected]>
  • Loading branch information
joonazan and montekki authored Aug 28, 2024
1 parent 2276b7b commit dc73bb4
Show file tree
Hide file tree
Showing 54 changed files with 2,144 additions and 963 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[package]
name = "vm2"
version = "0.1.0"
edition = "2021"
edition.workspace = true
homepage = "https://zksync.io/"
license = "MIT OR Apache-2.0"
authors = ["The Matter Labs Team <[email protected]>"]
license.workspace = true
authors.workspace = true

[dependencies]
eravm-stable-interface = { path = "./eravm-stable-interface" }
zkevm_opcode_defs = "0.150.0"
zk_evm_abstractions = "0.150.0"
u256 = { package = "primitive-types", version = "0.12.1" }
Expand All @@ -28,8 +29,12 @@ harness = false

[features]
default = []
trace = []
single_instruction_test = ["arbitrary", "u256/arbitrary", "zk_evm", "anyhow"]

[workspace]
members = [".", "afl-fuzz"]
members = [".", "afl-fuzz", "eravm-stable-interface"]

[workspace.package]
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["The Matter Labs Team <[email protected]>"]
5 changes: 4 additions & 1 deletion afl-fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "differential_fuzzing"
version = "0.1.0"
edition = "2021"
edition.workspace = true
publish = false

[dependencies]
Expand All @@ -13,6 +13,9 @@ pretty_assertions = "1.4.0"
path = ".."
features = ["single_instruction_test"]

[dependencies.eravm-stable-interface]
path = "../eravm-stable-interface"

[[bin]]
name = "show_testcase"
path = "src/show_testcase.rs"
Expand Down
2 changes: 1 addition & 1 deletion afl-fuzz/src/check_input_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ fn main() {
const BYTES_GIVEN: usize = 10000;
let data = [0xFF; BYTES_GIVEN];
let mut u = arbitrary::Unstructured::new(&data);
let _: VmAndWorld = u.arbitrary().unwrap();
let _: VmAndWorld<()> = u.arbitrary().unwrap();
println!("{:?}", BYTES_GIVEN - u.len());
}
5 changes: 3 additions & 2 deletions afl-fuzz/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use arbitrary::Arbitrary;
use eravm_stable_interface::Tracer;
use vm2::{single_instruction_test::MockWorld, VirtualMachine};

#[derive(Arbitrary, Debug)]
pub struct VmAndWorld {
pub vm: VirtualMachine,
pub struct VmAndWorld<T: Tracer> {
pub vm: VirtualMachine<T, MockWorld>,
pub world: MockWorld,
}
10 changes: 3 additions & 7 deletions afl-fuzz/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ 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(),
vm.state.current_frame.pc_from_u16(0).unwrap(),
);
let mut zk_evm = vm2_to_zk_evm(&vm, world.clone());

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

add_heap_to_zk_evm(&mut zk_evm, &vm);
Expand All @@ -29,7 +25,7 @@ fn main() {

assert_eq!(
UniversalVmState::from(zk_evm),
vm2_to_zk_evm(&vm, world.clone(), pc).into()
vm2_to_zk_evm(&vm, world.clone()).into()
);
}
}
Expand Down
10 changes: 3 additions & 7 deletions afl-fuzz/src/show_testcase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,13 @@ fn main() {
println!("{:?}", vm.state);
assert!(vm.is_in_valid_state());

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

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

println!("Mocks that have been touched:");
vm.print_mock_info();
Expand All @@ -49,6 +45,6 @@ fn main() {

assert_eq!(
UniversalVmState::from(zk_evm),
vm2_to_zk_evm(&vm, world.clone(), pc).into()
vm2_to_zk_evm(&vm, world.clone()).into()
);
}
4 changes: 2 additions & 2 deletions benches/nested_near_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn nested_near_call(bencher: Bencher) {
},
);

vm.run(black_box(&mut world));
vm.run(black_box(&mut world), &mut ());
});
}

Expand Down Expand Up @@ -87,7 +87,7 @@ fn nested_near_call_with_storage_write(bencher: Bencher) {
},
);

vm.run(black_box(&mut world));
vm.run(black_box(&mut world), &mut ());
});
}

Expand Down
9 changes: 9 additions & 0 deletions eravm-stable-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "eravm-stable-interface"
version = "0.1.0"
edition.workspace = true
license.workspace = true
authors.workspace = true

[dependencies]
primitive-types = "0.12.1"
88 changes: 88 additions & 0 deletions eravm-stable-interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! # EraVM Stable Interface
//!
//! This crate defines an interface for tracers that will never change but may be extended.
//! To be precise, a tracer using this interface will work in any VM written against that
//! version or a newer one. Updating the tracer to depend on a newer interface version is
//! not necessary. In fact, tracers should depend on the oldest version that has the required
//! features.
//!
//! A struct implementing [Tracer] may read and mutate the VM's state via [StateInterface]
//! when particular opcodes are executed.
//!
//! ## Why is extreme backwards compatibility required here?
//!
//! Suppose VM1 uses stable interface version 1 and VM2 uses stable interface version 2.
//! With any sane design it would be trivial to take a tracer written for version 1 and
//! update it to work with version 2. However, then it can no longer be used with VM1.
//!
//! This exact thing caused us a lot of trouble when we put many versions of zk_evm in multivm.
//!
//! ## How do I add a new feature to the interface?
//!
//! Do not change the existing traits. In fact, you should delete existing code in the new
//! version that you publish and import it from the previous version instead.
//!
//! This is how you would add a new method to StateInterface and a new opcode.
//! ```
//! # use eravm_stable_interface as eravm_stable_interface_v1;
//! use eravm_stable_interface_v1::{StateInterface as StateInterfaceV1, Tracer as TracerV1, opcodes::NearCall};
//!
//! trait StateInterface: StateInterfaceV1 {
//! fn get_some_new_field(&self) -> u32;
//! }
//!
//! pub struct NewOpcode;
//!
//! #[derive(PartialEq, Eq)]
//! enum Opcode {
//! NewOpcode,
//! NearCall,
//! // ...
//! }
//!
//! trait OpcodeType {
//! const VALUE: Opcode;
//! }
//!
//! impl OpcodeType for NewOpcode {
//! const VALUE: Opcode = Opcode::NewOpcode;
//! }
//!
//! // Do this for every old opcode
//! impl OpcodeType for NearCall {
//! const VALUE: Opcode = Opcode::NearCall;
//! }
//!
//! trait Tracer {
//! fn before_instruction<OP: OpcodeType, S: StateInterface>(&mut self, _state: &mut S) {}
//! fn after_instruction<OP: OpcodeType, S: StateInterface>(&mut self, _state: &mut S) {}
//! }
//!
//! impl<T: TracerV1> Tracer for T {
//! fn before_instruction<OP: OpcodeType, S: StateInterface>(&mut self, state: &mut S) {
//! match OP::VALUE {
//! Opcode::NewOpcode => {}
//! // Do this for every old opcode
//! Opcode::NearCall => {
//! <Self as TracerV1>::before_instruction::<NearCall, _>(self, state)
//! }
//! }
//! }
//! fn after_instruction<OP: OpcodeType, S: StateInterface>(&mut self, _state: &mut S) {}
//! }
//!
//! // Now you can use the new features by implementing TracerV2
//! struct MyTracer;
//! impl Tracer for MyTracer {
//! fn before_instruction<OP: OpcodeType, S: StateInterface>(&mut self, state: &mut S) {
//! if OP::VALUE == Opcode::NewOpcode {
//! state.get_some_new_field();
//! }
//! }
//! }
//! ```
mod state_interface;
mod tracer_interface;
pub use state_interface::*;
pub use tracer_interface::*;
Loading

0 comments on commit dc73bb4

Please sign in to comment.