diff --git a/src/decommit.rs b/src/decommit.rs index d1a36191..ff3d4354 100644 --- a/src/decommit.rs +++ b/src/decommit.rs @@ -91,12 +91,15 @@ impl WorldDiff { decommit: UnpaidDecommit, gas: &mut u32, ) -> Option { + // We intentionally record a decommitment event even if actual decommitment never happens because of an out-of-gas error. + // This is how the old VM behaves. + self.decommitted_hashes.insert(decommit.code_key, ()); + if decommit.cost > *gas { // Unlike all other gas costs, this one is not paid if low on gas. return None; } *gas -= decommit.cost; - self.decommitted_hashes.insert(decommit.code_key, ()); Some(world.decommit(decommit.code_key)) } } diff --git a/src/heap.rs b/src/heap.rs index 23813cee..0859b04e 100644 --- a/src/heap.rs +++ b/src/heap.rs @@ -1,6 +1,6 @@ use crate::instruction_handlers::HeapInterface; -use std::mem; use std::ops::{Index, Range}; +use std::{fmt, mem}; use u256::U256; #[derive(Copy, Clone, PartialEq, Debug)] @@ -31,11 +31,36 @@ impl Default for HeapPage { } } -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, Default)] pub struct Heap { pages: Vec>, } +// We never remove `HeapPage`s (even after rollbacks – although we do zero all added pages in this case), +// we allow additional pages to be present if they are zeroed. +impl PartialEq for Heap { + fn eq(&self, other: &Self) -> bool { + for i in 0..self.pages.len().max(other.pages.len()) { + let this_page = self.pages.get(i).and_then(Option::as_ref); + let other_page = other.pages.get(i).and_then(Option::as_ref); + match (this_page, other_page) { + (Some(this_page), Some(other_page)) => { + if this_page != other_page { + return false; + } + } + (Some(page), None) | (None, Some(page)) => { + if page.0.iter().any(|&byte| byte != 0) { + return false; + } + } + (None, None) => { /* do nothing */ } + } + } + true + } +} + impl Heap { fn from_bytes(bytes: &[u8], pagepool: &mut PagePool) -> Self { let pages = bytes @@ -272,9 +297,18 @@ impl PartialEq for Heaps { } } -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone)] struct PagePool(Vec); +impl fmt::Debug for PagePool { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("PagePool") + .field("len", &self.0.len()) + .finish_non_exhaustive() + } +} + impl PagePool { fn allocate_page(&mut self) -> HeapPage { self.get_dirty_page()