diff --git a/core/src/runtime/memory.rs b/core/src/runtime/memory.rs deleted file mode 100644 index 719903ab..00000000 --- a/core/src/runtime/memory.rs +++ /dev/null @@ -1,92 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// An record of a write to a memory address. -#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] -pub struct MemoryRecord { - /// The value at the memory address. - pub value: u32, - - /// The timestamp at which the memory address was last written to. - pub timestamp: u32, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum MemoryAccessPosition { - Memory = 0, - // Note that these AccessPositions mean that when when read/writing registers, they must be - // read/written in the following order: C, B, A. - C = 1, - B = 2, - A = 3, -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum MemoryRecordEnum { - Read(MemoryReadRecord), - Write(MemoryWriteRecord), -} - -#[allow(clippy::manual_non_exhaustive)] -#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] -pub struct MemoryReadRecord { - pub value: u32, - pub timestamp: u32, - pub prev_timestamp: u32, - _private: (), -} - -#[allow(clippy::manual_non_exhaustive)] -#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)] -pub struct MemoryWriteRecord { - pub value: u32, - pub timestamp: u32, - pub prev_value: u32, - pub prev_timestamp: u32, - _private: (), -} - -impl MemoryRecordEnum { - pub const fn value(&self) -> u32 { - match self { - MemoryRecordEnum::Read(record) => record.value, - MemoryRecordEnum::Write(record) => record.value, - } - } -} - -impl From for MemoryRecordEnum { - fn from(read_record: MemoryReadRecord) -> Self { - MemoryRecordEnum::Read(read_record) - } -} - -impl From for MemoryRecordEnum { - fn from(write_record: MemoryWriteRecord) -> Self { - MemoryRecordEnum::Write(write_record) - } -} - -impl MemoryReadRecord { - pub const fn new(value: u32, timestamp: u32, prev_timestamp: u32) -> Self { - assert!(timestamp > prev_timestamp); - Self { - value, - timestamp, - prev_timestamp, - _private: (), - } - } -} - -impl MemoryWriteRecord { - pub const fn new(value: u32, timestamp: u32, prev_value: u32, prev_timestamp: u32) -> Self { - assert!(timestamp > prev_timestamp); - Self { - value, - timestamp, - prev_value, - prev_timestamp, - _private: (), - } - } -} diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index 0339b61d..3cd81b13 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -1,6 +1,5 @@ mod instruction; mod io; -mod memory; mod opcode; mod program; mod register; @@ -10,7 +9,6 @@ mod syscall; mod utils; pub use instruction::*; -pub use memory::*; pub use opcode::*; pub use program::*; pub use register::*; @@ -69,9 +67,6 @@ pub struct Runtime { /// Max gas for the runtime. pub max_gas: Option, - /// The state of the runtime when in unconstrained mode. - pub(crate) unconstrained_state: ForkState, - /// The mapping between syscall codes and their implementations. pub syscall_map: HashMap>>, @@ -79,6 +74,17 @@ pub struct Runtime { pub max_syscall_cycles: u32, } +/// An record of a write to a memory address. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum MemoryAccessPosition { + Memory = 0, + // Note that these AccessPositions mean that when when read/writing registers, they must be + // read/written in the following order: C, B, A. + C = 1, + B = 2, + A = 3, +} + #[derive(Error, Debug)] pub enum ExecutionError { #[error("execution failed with exit code {0}")] @@ -137,7 +143,6 @@ where trace_buf, unconstrained: false, max_gas: opts.max_gas(), - unconstrained_state: ForkState::default(), syscall_map, max_syscall_cycles, } @@ -156,7 +161,7 @@ where for i in 0..32 { let addr = Register::from_u32(i as u32) as u32; registers[i] = match self.state.memory.get(&addr) { - Some(record) => record.value, + Some(record) => *record, None => 0, }; } @@ -167,7 +172,7 @@ where pub fn register(&self, register: Register) -> u32 { let addr = register as u32; match self.state.memory.get(&addr) { - Some(record) => record.value, + Some(record) => *record, None => 0, } } @@ -175,7 +180,7 @@ where /// Get the current value of a word. pub fn word(&self, addr: u32) -> u32 { match self.state.memory.get(&addr) { - Some(record) => record.value, + Some(record) => *record, None => 0, } } @@ -186,90 +191,37 @@ where (word >> ((addr % 4) * 8)) as u8 } - /// Get the current timestamp for a given memory access position. - pub const fn timestamp(&self, position: &MemoryAccessPosition) -> u32 { - self.state.clk + *position as u32 - } - - /// Read a word from memory and create an access record. - pub fn mr(&mut self, addr: u32, timestamp: u32) -> MemoryReadRecord { - // Get the memory record entry. + /// Read a word from memory. + pub fn mr(&mut self, addr: u32) -> u32 { + // Get the memory entry. let entry = self.state.memory.entry(addr); - // If we're in unconstrained mode, we don't want to modify state, so we'll save the - // original state if it's the first time modifying it. - if self.unconstrained { - let record = match entry { - Entry::Occupied(ref entry) => Some(entry.get()), - Entry::Vacant(_) => None, - }; - self - .unconstrained_state - .memory_diff - .entry(addr) - .or_insert(record.copied()); - } - // If it's the first time accessing this address, initialize previous values. - let record: &mut MemoryRecord = match entry { - Entry::Occupied(entry) => entry.into_mut(), + match entry { + Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { // If addr has a specific value to be initialized with, use that, otherwise 0. let value = self.state.uninitialized_memory.remove(&addr).unwrap_or(0); - - entry.insert(MemoryRecord { - value, - timestamp: 0, - }) + *entry.insert(value) } - }; - let value = record.value; - let prev_timestamp = record.timestamp; - record.timestamp = timestamp; - - // Construct the memory read record. - MemoryReadRecord::new(value, timestamp, prev_timestamp) + } } - /// Write a word to memory and create an access record. - pub fn mw(&mut self, addr: u32, value: u32, timestamp: u32) -> MemoryWriteRecord { + /// Write a word to memory. + pub fn mw(&mut self, addr: u32, value: u32) { // Get the memory record entry. let entry = self.state.memory.entry(addr); - // If we're in unconstrained mode, we don't want to modify state, so we'll save the - // original state if it's the first time modifying it. - if self.unconstrained { - let record = match entry { - Entry::Occupied(ref entry) => Some(entry.get()), - Entry::Vacant(_) => None, - }; - self - .unconstrained_state - .memory_diff - .entry(addr) - .or_insert(record.copied()); - } - - // If it's the first time accessing this address, initialize previous values. - let record: &mut MemoryRecord = match entry { - Entry::Occupied(entry) => entry.into_mut(), + match entry { + Entry::Occupied(mut entry) => { + entry.insert(value); + } Entry::Vacant(entry) => { // If addr has a specific value to be initialized with, use that, otherwise 0. - let value = self.state.uninitialized_memory.remove(&addr).unwrap_or(0); - - entry.insert(MemoryRecord { - value, - timestamp: 0, - }) + self.state.uninitialized_memory.remove(&addr); + entry.insert(value); } }; - let prev_value = record.value; - let prev_timestamp = record.timestamp; - record.value = value; - record.timestamp = timestamp; - - // Construct the memory write record. - MemoryWriteRecord::new(value, timestamp, prev_value, prev_timestamp) } /// Read from memory, assuming that all addresses are aligned. @@ -278,9 +230,7 @@ where assert_valid_memory_access!(addr, position); // Read the address from memory and create a memory read record. - let record = self.mr(addr, self.timestamp(&position)); - - record.value + self.mr(addr) } /// Write to memory. @@ -289,7 +239,7 @@ where assert_valid_memory_access!(addr, position); // Read the address from memory and create a memory read record. - let _ = self.mw(addr, value, self.timestamp(&position)); + self.mw(addr, value); } /// Read from a register. @@ -744,13 +694,7 @@ where tracing::info!("loading memory image"); for (addr, value) in self.program.memory_image.iter() { - self.state.memory.insert( - *addr, - MemoryRecord { - value: *value, - timestamp: 0, - }, - ); + self.state.memory.insert(*addr, *value); } tracing::info!("starting execution"); diff --git a/core/src/runtime/state.rs b/core/src/runtime/state.rs index 9b445e0e..bc7cec39 100644 --- a/core/src/runtime/state.rs +++ b/core/src/runtime/state.rs @@ -4,8 +4,6 @@ use nohash_hasher::BuildNoHashHasher; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use super::MemoryRecord; - /// Holds data describing the current state of a program's execution. #[serde_as] #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -21,7 +19,7 @@ pub struct ExecutionState { pub pc: u32, /// The memory which instructions operate over. - pub memory: HashMap>, + pub memory: HashMap>, /// Uninitialized memory addresses that have a specific value they should be initialized with. /// SyscallHintRead uses this to write hint data into uninitialized memory. @@ -55,10 +53,3 @@ impl ExecutionState { } } } - -/// Holds data to track changes made to the runtime since a fork point. -#[derive(Debug, Clone, Default)] -pub(crate) struct ForkState { - /// Only contains the original memory values for addresses that have been modified - pub(crate) memory_diff: HashMap, BuildNoHashHasher>, -} diff --git a/core/src/runtime/syscall.rs b/core/src/runtime/syscall.rs index db1b3012..017899c8 100644 --- a/core/src/runtime/syscall.rs +++ b/core/src/runtime/syscall.rs @@ -8,7 +8,6 @@ use crate::syscall::{ SyscallHalt, SyscallHintLen, SyscallHintRead, SyscallHostCall, SyscallHostGetBalance, SyscallHostRead, SyscallHostWrite, SyscallWrite, }; -use crate::{runtime::MemoryReadRecord, runtime::MemoryWriteRecord}; use athena_interface::HostInterface; @@ -109,53 +108,32 @@ where } } - pub fn mr(&mut self, addr: u32) -> (MemoryReadRecord, u32) { - let record = self.rt.mr(addr, self.clk); - (record, record.value) + pub fn mw(&mut self, addr: u32, value: u32) { + self.rt.mw(addr, value); } - pub fn mr_slice(&mut self, addr: u32, len: usize) -> (Vec, Vec) { - let mut records = Vec::new(); - let mut values = Vec::new(); - for i in 0..len { - let (record, value) = self.mr(addr + i as u32 * 4); - records.push(record); - values.push(value); - } - (records, values) - } - - pub fn mw(&mut self, addr: u32, value: u32) -> MemoryWriteRecord { - self.rt.mw(addr, value, self.clk) - } - - pub fn mw_slice(&mut self, addr: u32, values: &[u32]) -> Vec { - let mut records = Vec::new(); + pub fn mw_slice(&mut self, addr: u32, values: &[u32]) { for i in 0..values.len() { - let record = self.mw(addr + i as u32 * 4, values[i]); - records.push(record); + self.mw(addr + i as u32 * 4, values[i]); } - records } - /// Get the current value of a register, but doesn't use a memory record. - /// This is generally unconstrained, so you must be careful using it. - pub fn register_unsafe(&self, register: Register) -> u32 { + pub fn register(&self, register: Register) -> u32 { self.rt.register(register) } - pub fn byte_unsafe(&self, addr: u32) -> u8 { + pub fn byte(&self, addr: u32) -> u8 { self.rt.byte(addr) } - pub fn word_unsafe(&self, addr: u32) -> u32 { + pub fn word(&self, addr: u32) -> u32 { self.rt.word(addr) } - pub fn slice_unsafe(&self, addr: u32, len: usize) -> Vec { + pub fn slice(&self, addr: u32, len: usize) -> Vec { let mut values = Vec::new(); for i in 0..len { - values.push(self.rt.word(addr + i as u32 * 4)); + values.push(self.word(addr + i as u32 * 4)); } values } diff --git a/core/src/syscall/host.rs b/core/src/syscall/host.rs index b74c0006..547c50ae 100644 --- a/core/src/syscall/host.rs +++ b/core/src/syscall/host.rs @@ -24,7 +24,7 @@ where .expect("Missing Athena runtime context"); // marshal inputs - let key = ctx.slice_unsafe(arg1, BYTES32_LENGTH / 4); + let key = ctx.slice(arg1, BYTES32_LENGTH / 4); // read value from host let host = ctx.rt.host.as_mut().expect("Missing host interface"); @@ -59,8 +59,8 @@ where .expect("Missing Athena runtime context"); // marshal inputs - let key = ctx.slice_unsafe(arg1, BYTES32_LENGTH / 4); - let value = ctx.slice_unsafe(arg2, BYTES32_LENGTH / 4); + let key = ctx.slice(arg1, BYTES32_LENGTH / 4); + let value = ctx.slice(arg2, BYTES32_LENGTH / 4); // write value to host let host = ctx.rt.host.as_mut().expect("Missing host interface"); @@ -117,7 +117,7 @@ where // marshal inputs let address_words = ADDRESS_LENGTH / 4; - let address = ctx.slice_unsafe(arg1, address_words); + let address = ctx.slice(arg1, address_words); let address = AddressWrapper::from(address); // read the input length from the next register @@ -130,7 +130,7 @@ where // `len` is denominated in number of bytes; we read words in chunks of four bytes // then convert into a standard bytearray. let input = if len > 0 { - let input_slice = ctx.slice_unsafe(arg2, len / 4); + let input_slice = ctx.slice(arg2, len / 4); Some( input_slice .iter() @@ -144,7 +144,7 @@ where // read the amount pointer from the next register as little-endian let a3 = Register::X13; let amount_ptr = ctx.rt.register(a3); - let amount_slice = ctx.slice_unsafe(amount_ptr, 2); + let amount_slice = ctx.slice(amount_ptr, 2); let amount = u64::from(amount_slice[0]) | (u64::from(amount_slice[1]) << 32); // note: host is responsible for checking balance and stack depth diff --git a/examples/fibonacci/program/elf/fibonacci-program b/examples/fibonacci/program/elf/fibonacci-program index f90c9ff8..5a90ab1a 100755 Binary files a/examples/fibonacci/program/elf/fibonacci-program and b/examples/fibonacci/program/elf/fibonacci-program differ diff --git a/examples/hello_world/program/elf/hello-world-program b/examples/hello_world/program/elf/hello-world-program index 6cd90e12..160b0427 100755 Binary files a/examples/hello_world/program/elf/hello-world-program and b/examples/hello_world/program/elf/hello-world-program differ diff --git a/examples/io/program/elf/io-program b/examples/io/program/elf/io-program index 286123f7..b38364bb 100755 Binary files a/examples/io/program/elf/io-program and b/examples/io/program/elf/io-program differ