pub mod in_memory;
pub mod lmdb;
pub mod scratch;
use std::{collections::HashMap, hash::BuildHasher};
use tracing::error;
use casper_hashing::Digest;
use casper_types::{bytesrepr, Key, StoredValue};
use crate::{
shared::{
additive_map::AdditiveMap,
newtypes::CorrelationId,
transform::{self, Transform},
},
storage::{
transaction_source::{Transaction, TransactionSource},
trie::{merkle_proof::TrieMerkleProof, Trie, TrieRaw},
trie_store::{
operations::{read, write, ReadResult, WriteResult},
TrieStore,
},
},
};
use super::trie_store::operations::DeleteResult;
pub trait StateReader<K, V> {
type Error;
fn read(&self, correlation_id: CorrelationId, key: &K) -> Result<Option<V>, Self::Error>;
fn read_with_proof(
&self,
correlation_id: CorrelationId,
key: &K,
) -> Result<Option<TrieMerkleProof<K, V>>, Self::Error>;
fn keys_with_prefix(
&self,
correlation_id: CorrelationId,
prefix: &[u8],
) -> Result<Vec<K>, Self::Error>;
}
#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
pub enum CommitError {
#[error("Root not found: {0:?}")]
RootNotFound(Digest),
#[error("Root not found while attempting to read: {0:?}")]
ReadRootNotFound(Digest),
#[error("Root not found while writing: {0:?}")]
WriteRootNotFound(Digest),
#[error("Key not found: {0}")]
KeyNotFound(Key),
#[error(transparent)]
TransformError(transform::Error),
#[error("Trie not found in cache {0}")]
TrieNotFoundInCache(Digest),
}
pub trait CommitProvider: StateProvider {
fn commit(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
effects: AdditiveMap<Key, Transform>,
) -> Result<Digest, Self::Error>;
}
pub trait StateProvider {
type Error;
type Reader: StateReader<Key, StoredValue, Error = Self::Error>;
fn checkout(&self, state_hash: Digest) -> Result<Option<Self::Reader>, Self::Error>;
fn empty_root(&self) -> Digest;
fn get_trie_full(
&self,
correlation_id: CorrelationId,
trie_key: &Digest,
) -> Result<Option<TrieRaw>, Self::Error>;
fn put_trie(&self, correlation_id: CorrelationId, trie: &[u8]) -> Result<Digest, Self::Error>;
fn missing_children(
&self,
correlation_id: CorrelationId,
trie_raw: &[u8],
) -> Result<Vec<Digest>, Self::Error>;
fn delete_keys(
&self,
correlation_id: CorrelationId,
root: Digest,
keys_to_delete: &[Key],
) -> Result<DeleteResult, Self::Error>;
}
pub fn put_stored_values<'a, R, S, E>(
environment: &'a R,
store: &S,
correlation_id: CorrelationId,
prestate_hash: Digest,
stored_values: HashMap<Key, StoredValue>,
) -> Result<Digest, E>
where
R: TransactionSource<'a, Handle = S::Handle>,
S: TrieStore<Key, StoredValue>,
S::Error: From<R::Error>,
E: From<R::Error> + From<S::Error> + From<bytesrepr::Error> + From<CommitError>,
{
let mut txn = environment.create_read_write_txn()?;
let mut state_root = prestate_hash;
let maybe_root: Option<Trie<Key, StoredValue>> = store.get(&txn, &state_root)?;
if maybe_root.is_none() {
return Err(CommitError::RootNotFound(prestate_hash).into());
};
for (key, value) in stored_values.iter() {
let write_result =
write::<_, _, _, _, E>(correlation_id, &mut txn, store, &state_root, key, value)?;
match write_result {
WriteResult::Written(root_hash) => {
state_root = root_hash;
}
WriteResult::AlreadyExists => (),
WriteResult::RootNotFound => {
error!(?state_root, ?key, ?value, "Error writing new value");
return Err(CommitError::WriteRootNotFound(state_root).into());
}
}
}
txn.commit()?;
Ok(state_root)
}
pub fn commit<'a, R, S, H, E>(
environment: &'a R,
store: &S,
correlation_id: CorrelationId,
prestate_hash: Digest,
effects: AdditiveMap<Key, Transform, H>,
) -> Result<Digest, E>
where
R: TransactionSource<'a, Handle = S::Handle>,
S: TrieStore<Key, StoredValue>,
S::Error: From<R::Error>,
E: From<R::Error> + From<S::Error> + From<bytesrepr::Error> + From<CommitError>,
H: BuildHasher,
{
let mut txn = environment.create_read_write_txn()?;
let mut state_root = prestate_hash;
let maybe_root: Option<Trie<Key, StoredValue>> = store.get(&txn, &state_root)?;
if maybe_root.is_none() {
return Err(CommitError::RootNotFound(prestate_hash).into());
};
for (key, transform) in effects.into_iter() {
let read_result = read::<_, _, _, _, E>(correlation_id, &txn, store, &state_root, &key)?;
let value = match (read_result, transform) {
(ReadResult::NotFound, Transform::Write(new_value)) => new_value,
(ReadResult::NotFound, transform) => {
error!(
?state_root,
?key,
?transform,
"Key not found while attempting to apply transform"
);
return Err(CommitError::KeyNotFound(key).into());
}
(ReadResult::Found(current_value), transform) => match transform.apply(current_value) {
Ok(updated_value) => updated_value,
Err(err) => {
error!(
?state_root,
?key,
?err,
"Key found, but could not apply transform"
);
return Err(CommitError::TransformError(err).into());
}
},
(ReadResult::RootNotFound, transform) => {
error!(
?state_root,
?key,
?transform,
"Failed to read state root while processing transform"
);
return Err(CommitError::ReadRootNotFound(state_root).into());
}
};
let write_result =
write::<_, _, _, _, E>(correlation_id, &mut txn, store, &state_root, &key, &value)?;
match write_result {
WriteResult::Written(root_hash) => {
state_root = root_hash;
}
WriteResult::AlreadyExists => (),
WriteResult::RootNotFound => {
error!(?state_root, ?key, ?value, "Error writing new value");
return Err(CommitError::WriteRootNotFound(state_root).into());
}
}
}
txn.commit()?;
Ok(state_root)
}