diff --git a/src/riscv/lib/src/machine_state/block_cache.rs b/src/riscv/lib/src/machine_state/block_cache.rs index 093ad9823c2c1f76afcd253440c5755530f74bf7..782022d30f46ebbf8ccc68ee1a345a06f9caf5e7 100644 --- a/src/riscv/lib/src/machine_state/block_cache.rs +++ b/src/riscv/lib/src/machine_state/block_cache.rs @@ -80,9 +80,9 @@ use super::{bus::Address, ProgramCounterUpdate}; use crate::cache_utils::FenceCounter; use crate::cache_utils::Sizes; use crate::default::ConstDefault; -use crate::state_backend::{self, ManagerAlloc, ManagerClone}; use crate::state_backend::{ - AllocatedOf, Atom, Cell, ManagerBase, ManagerRead, ManagerReadWrite, ManagerWrite, Ref, + self, AllocatedOf, Atom, Cell, ManagerAlloc, ManagerBase, ManagerClone, ManagerRead, + ManagerReadWrite, ManagerWrite, Ref, }; use crate::traps::{EnvironException, Exception}; use std::marker::PhantomData; diff --git a/src/riscv/lib/src/state_backend.rs b/src/riscv/lib/src/state_backend.rs index c8e33ddebfbfbfca5baa45df8c191c68c68eb139..8634ea7d183b16a66fd461bf7c3562a7d10a94a4 100644 --- a/src/riscv/lib/src/state_backend.rs +++ b/src/riscv/lib/src/state_backend.rs @@ -68,6 +68,35 @@ pub use elems::*; pub use layout::*; pub use region::*; +/// An enriched value may be stored in a [`ManagerBase::EnrichedCell`]. +/// +/// This allows a value to have an additional, derived, value attached - that may be expensive +/// to derive lazily. +/// +/// This derived value does not form part of any stored state/commitments. +pub trait EnrichedValue: 'static { + type E: 'static; + type D: Sized; +} + +/// Specifies that there exists a path to derive `V::D` from `&V::E`, +/// for a given manager. +pub trait EnrichedValueLinked: EnrichedValue { + /// Construct the derived value from the stored value, maps to + /// the `From` trait by default. + fn derive(v: &Self::E) -> Self::D; +} + +impl EnrichedValueLinked for Value +where + Value: EnrichedValue, + Value::D: for<'a> From<&'a Value::E>, +{ + fn derive(v: &Self::E) -> Self::D { + v.into() + } +} + /// Manager of the state backend storage pub trait ManagerBase { /// Region that has been allocated in the state storage @@ -75,6 +104,20 @@ pub trait ManagerBase { /// Dynamic region represents a fixed-sized byte vector that has been allocated in the state storage type DynRegion; + + /// An [enriched] value may have a derived value attached. + /// + /// [enriched]: EnrichedValue + type EnrichedCell; + + /// The root manager may either be itself, or occassionally the manager that this manager + /// wraps. + /// + /// For example, the [`Ref`] backend is often use to wrap the [`Owned`] backend to gain access + /// to its regions. In this case, the root manager would be the owned backend. + /// + /// [`Owned`]: owned_backend::Owned + type ManagerRoot: ManagerBase; } /// Manager with allocation capabilities @@ -90,6 +133,11 @@ pub trait ManagerAlloc: 'static + ManagerReadWrite { /// Allocate a dynamic region in the state storage. fn allocate_dyn_region(&mut self) -> Self::DynRegion; + + /// Allocate an enriched cell. + fn allocate_enriched_cell(&mut self, init_value: V::E) -> Self::EnrichedCell + where + V: EnrichedValueLinked; } /// Manager with read capabilities @@ -115,10 +163,28 @@ pub trait ManagerRead: ManagerBase { address: usize, values: &mut [E], ); + + /// Read the value, and derived value, contained in the enriched cell. + fn enriched_cell_read(cell: &Self::EnrichedCell) -> (V::E, V::D) + where + V: EnrichedValueLinked, + V::E: Copy, + V::D: Copy; + + /// Obtain a refernce to the value, and derived value, contained in the enriched cell. + fn enriched_cell_ref(cell: &Self::EnrichedCell) -> (&V::E, &V::D) + where + V: EnrichedValueLinked; + + /// Obtain a refernce to the value, and derived value, contained in the enriched cell. + fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E + where + V: EnrichedValue, + V::E: Copy; } /// Manager with write capabilities -pub trait ManagerWrite: ManagerBase { +pub trait ManagerWrite: ManagerBase { /// Update an element in the region. fn region_write( region: &mut Self::Region, @@ -142,6 +208,11 @@ pub trait ManagerWrite: ManagerBase { address: usize, values: &[E], ); + + /// Update the value contained in an enriched cell. The derived value will be recalculated. + fn enriched_cell_write(cell: &mut Self::EnrichedCell, value: V::E) + where + V: EnrichedValueLinked; } /// Manager with capabilities that require both read and write @@ -167,6 +238,16 @@ pub trait ManagerSerialise: ManagerBase { region: &Self::DynRegion, serializer: S, ) -> Result; + + /// Serialise the contents of the enriched cell. + fn serialise_enriched_cell( + cell: &Self::EnrichedCell, + serializer: S, + ) -> Result + where + V: EnrichedValue, + V::E: serde::Serialize, + S: serde::Serializer; } /// Manager with the ability to deserialise regions @@ -185,6 +266,14 @@ pub trait ManagerDeserialise: ManagerBase { fn deserialise_dyn_region<'de, const LEN: usize, D: serde::Deserializer<'de>>( deserializer: D, ) -> Result, D::Error>; + + /// Deserialise an enriched cell. + fn deserialise_enriched_cell<'de, V, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> + where + V: EnrichedValueLinked, + V::E: serde::Deserialize<'de>; } /// Manager with the ability to clone regions @@ -196,15 +285,26 @@ pub trait ManagerClone: ManagerBase { /// Clone the dynamic region. fn clone_dyn_region(region: &Self::DynRegion) -> Self::DynRegion; + + /// Clone the enriched cell. + fn clone_enriched_cell(cell: &Self::EnrichedCell) -> Self::EnrichedCell + where + V: EnrichedValue, + V::E: Copy, + V::D: Copy; } /// Manager wrapper around `M` whose regions are immutable references to regions of `M` -pub struct Ref<'backend, M>(std::marker::PhantomData<&'backend M>); +pub struct Ref<'backend, M>(std::marker::PhantomData); impl<'backend, M: ManagerBase> ManagerBase for Ref<'backend, M> { type Region = &'backend M::Region; type DynRegion = &'backend M::DynRegion; + + type EnrichedCell = &'backend M::EnrichedCell; + + type ManagerRoot = M::ManagerRoot; } impl ManagerSerialise for Ref<'_, M> { @@ -221,6 +321,16 @@ impl ManagerSerialise for Ref<'_, M> { ) -> Result { M::serialise_dyn_region(region, serializer) } + + fn serialise_enriched_cell( + cell: &Self::EnrichedCell, + serializer: S, + ) -> Result + where + V::E: serde::Serialize, + { + M::serialise_enriched_cell(cell, serializer) + } } impl ManagerRead for Ref<'_, M> { @@ -250,6 +360,31 @@ impl ManagerRead for Ref<'_, M> { ) { M::dyn_region_read_all(region, address, values) } + + fn enriched_cell_read>( + cell: &Self::EnrichedCell, + ) -> (V::E, V::D) + where + V::D: Copy, + V::E: Copy, + { + M::enriched_cell_read(cell) + } + + fn enriched_cell_ref>( + cell: &Self::EnrichedCell, + ) -> (&V::E, &V::D) +where { + M::enriched_cell_ref(cell) + } + + fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E + where + V: EnrichedValue, + V::E: Copy, + { + M::enriched_cell_read_stored(cell) + } } #[cfg(test)] diff --git a/src/riscv/lib/src/state_backend/owned_backend.rs b/src/riscv/lib/src/state_backend/owned_backend.rs index 2aa2ac32cf1b29374662e7c5e40a69bc2f2ac7ce..be955101c19c0d44665d2110cd0c5a9e46f81c03 100644 --- a/src/riscv/lib/src/state_backend/owned_backend.rs +++ b/src/riscv/lib/src/state_backend/owned_backend.rs @@ -3,8 +3,9 @@ // SPDX-License-Identifier: MIT use super::{ - AllocatedOf, Elem, Layout, ManagerAlloc, ManagerBase, ManagerClone, ManagerDeserialise, - ManagerRead, ManagerReadWrite, ManagerSerialise, ManagerWrite, StaticCopy, + AllocatedOf, Elem, EnrichedValue, EnrichedValueLinked, Layout, ManagerAlloc, ManagerBase, + ManagerClone, ManagerDeserialise, ManagerRead, ManagerReadWrite, ManagerSerialise, + ManagerWrite, StaticCopy, }; use serde::ser::SerializeTuple; use std::{ @@ -28,6 +29,10 @@ impl ManagerBase for Owned { type Region = [E; LEN]; type DynRegion = Box<[u8; LEN]>; + + type EnrichedCell = (V::E, V::D); + + type ManagerRoot = Self; } impl ManagerAlloc for Owned { @@ -47,6 +52,14 @@ impl ManagerAlloc for Owned { Box::from_raw(alloc.cast()) } } + + fn allocate_enriched_cell>( + &mut self, + value: V::E, + ) -> Self::EnrichedCell { + let derived = V::derive(&value); + (value, derived) + } } impl ManagerRead for Owned { @@ -98,6 +111,26 @@ impl ManagerRead for Owned { elem.from_stored_in_place(); } } + + fn enriched_cell_read(cell: &Self::EnrichedCell) -> (V::E, V::D) + where + V::E: Copy, + V::D: Copy, + { + *cell + } + + fn enriched_cell_ref(cell: &Self::EnrichedCell) -> (&V::E, &V::D) { + (&cell.0, &cell.1) + } + + fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E + where + V: EnrichedValue, + V::E: Copy, + { + cell.0 + } } impl ManagerWrite for Owned { @@ -150,6 +183,16 @@ impl ManagerWrite for Owned { } } } + + fn enriched_cell_write(cell: &mut Self::EnrichedCell, value: V::E) + where + V: EnrichedValueLinked, + { + let derived = V::derive(&value); + + cell.0 = value; + cell.1 = derived; + } } impl ManagerReadWrite for Owned { @@ -191,6 +234,17 @@ impl ManagerSerialise for Owned { ) -> Result { serializer.serialize_bytes(region.as_slice()) } + + fn serialise_enriched_cell( + cell: &Self::EnrichedCell, + serializer: S, + ) -> Result + where + V::E: serde::Serialize, + { + use serde::Serialize; + cell.0.serialize(serializer) + } } impl ManagerDeserialise for Owned { @@ -261,6 +315,19 @@ impl ManagerDeserialise for Owned { vec.try_into() .map_err(|_err| serde::de::Error::custom("Dynamic region of mismatching length")) } + + fn deserialise_enriched_cell<'de, V, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> + where + V: EnrichedValueLinked, + V::E: serde::Deserialize<'de>, + { + use serde::Deserialize; + let value = V::E::deserialize(deserializer)?; + let derived = V::derive(&value); + Ok((value, derived)) + } } impl ManagerClone for Owned { @@ -274,12 +341,23 @@ impl ManagerClone for Owned { fn clone_dyn_region(region: &Self::DynRegion) -> Self::DynRegion { region.clone() } + + fn clone_enriched_cell(cell: &Self::EnrichedCell) -> Self::EnrichedCell + where + V::E: Copy, + V::D: Copy, + { + #[allow(clippy::clone_on_copy)] + cell.clone() + } } #[cfg(test)] pub mod test_helpers { use super::*; - use crate::state_backend::{test_helpers::TestBackendFactory, Cell, Cells, DynCells}; + use crate::state_backend::{ + test_helpers::TestBackendFactory, Cell, Cells, DynCells, EnrichedCell, + }; /// Test backend factory for the owned state manager pub struct OwnedTestBackendFactory; @@ -345,6 +423,45 @@ pub mod test_helpers { }); } + /// Ensure [`EnrichedCell`] can be serialised and deserialised in a consistent way. + #[test] + fn enriched_cell_serialise() { + pub struct Enriching; + pub struct Fun(Box u64>); + + impl EnrichedValue for Enriching { + type E = u64; + type D = Fun; + } + + impl<'a> From<&'a u64> for Fun { + fn from(value: &'a u64) -> Self { + let value = *value; + Self(Box::new(move |x| value.wrapping_add(x))) + } + } + + proptest::proptest!(|(value: u64)| { + let mut cell: EnrichedCell = EnrichedCell::bind((0u64, Fun::from(&0))); + cell.write(value); + + assert_eq!(value, *cell.read_ref().0); + let bytes = bincode::serialize(&cell).unwrap(); + + let cell_after: EnrichedCell = bincode::deserialize(&bytes).unwrap(); + + for i in 0..5 { + assert_eq!(cell.read_ref().0, cell_after.read_ref().0); + + let fun = cell.read_ref().1; + let fun_after = cell_after.read_ref().1; + + assert_eq!(value.wrapping_add(i), (fun.0)(i)); + assert_eq!((fun.0)(i), (fun_after.0)(i)); + } + }); + } + /// Ensure that [`Cell`] serialises in a way that represents the underlying element /// directly instead of wrapping it into an array (as it is an array under the hood). #[test] diff --git a/src/riscv/lib/src/state_backend/region.rs b/src/riscv/lib/src/state_backend/region.rs index c0f07ad397e4f5ec25c1f653c9b64796deaa87d5..b7bc015dd29b94b8db3ad412b0706b4742ac470c 100644 --- a/src/riscv/lib/src/state_backend/region.rs +++ b/src/riscv/lib/src/state_backend/region.rs @@ -5,12 +5,99 @@ use super::{ hash::{self, Hash, HashError, HashWriter, RootHashable}, - Elem, ManagerBase, ManagerClone, ManagerDeserialise, ManagerRead, ManagerReadWrite, - ManagerSerialise, ManagerWrite, Ref, + Elem, EnrichedValue, EnrichedValueLinked, ManagerBase, ManagerClone, ManagerDeserialise, + ManagerRead, ManagerReadWrite, ManagerSerialise, ManagerWrite, Ref, }; use crate::storage::binary; +use std::borrow::Borrow; use std::num::NonZeroUsize; +/// Link a stored value directly with a derived value - +/// that would either be expensive to compute each time, or cannot +/// itself be stored. +/// +/// Only the value of `V::E` forms part of the 'state' for the purposes of commitments etc. +pub struct EnrichedCell { + cell: M::EnrichedCell, +} + +impl EnrichedCell { + /// Bind this state to the enriched cell. + pub fn bind(cell: M::EnrichedCell) -> Self { + Self { cell } + } + + /// Obtain a structure with references to the bound enriched cell of this type. + pub fn struct_ref(&self) -> EnrichedCell> { + EnrichedCell { + cell: self.cell.borrow(), + } + } + + /// Write the value back to the enriched cell. + /// + /// Reading the new value will produce the new derived value also. + pub fn write(&mut self, value: V::E) + where + M: ManagerWrite, + V: EnrichedValueLinked, + { + M::enriched_cell_write(&mut self.cell, value) + } + + /// Read the value & derived value from the enriched cell. + pub fn read(&self) -> (V::E, V::D) + where + M: ManagerRead, + V: EnrichedValueLinked, + V::E: Copy, + V::D: Copy, + { + M::enriched_cell_read(&self.cell) + } + + /// Obtain references to the value & derived value contained + /// within the cell. + pub fn read_ref(&self) -> (&V::E, &V::D) + where + M: ManagerRead, + V: EnrichedValueLinked, + { + M::enriched_cell_ref(&self.cell) + } + + /// Read only the stored value from the enriched cell. + pub fn read_stored(&self) -> V::E + where + M: ManagerRead, + V::E: Copy, + { + M::enriched_cell_read_stored(&self.cell) + } +} + +impl Clone for EnrichedCell +where + V::E: Copy, + V::D: Copy, +{ + fn clone(&self) -> Self { + Self { + cell: M::clone_enriched_cell(&self.cell), + } + } +} + +impl PartialEq for EnrichedCell +where + V: EnrichedValueLinked, + V::E: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + M::enriched_cell_ref(&self.cell).0 == M::enriched_cell_ref(&other.cell).0 + } +} + /// Single element of type `E` #[repr(transparent)] pub struct Cell { @@ -274,6 +361,27 @@ impl serde::Serializ } } +impl serde::Serialize for EnrichedCell +where + V::E: serde::Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + M::serialise_enriched_cell(&self.cell, serializer) + } +} + +impl RootHashable for EnrichedCell +where + V::E: serde::Serialize, +{ + fn hash(&self) -> Result { + Hash::blake2b_hash(self) + } +} + impl<'de, E: serde::Deserialize<'de>, const LEN: usize, M: ManagerDeserialise> serde::Deserialize<'de> for Cells { @@ -286,6 +394,20 @@ impl<'de, E: serde::Deserialize<'de>, const LEN: usize, M: ManagerDeserialise> } } +impl<'de, V, M: ManagerDeserialise> serde::Deserialize<'de> for EnrichedCell +where + V: EnrichedValueLinked, + V::E: serde::Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let cell = M::deserialise_enriched_cell(deserializer)?; + Ok(Self { cell }) + } +} + impl RootHashable for Cells { fn hash(&self) -> Result { Hash::blake2b_hash(self)