use std::{collections::BTreeMap, future::Future, pin::Pin, sync::Arc};
use essential_constraint_vm::{Access, SolutionAccess, StateSlots};
use essential_lock::StdLock;
use essential_server_types::{
QueryStateReads, QueryStateReadsOutput, Slots, SlotsRequest, StateReadRequestType,
};
use essential_state_read_vm::{asm::Op, GasLimit, StateRead};
use essential_storage::{next_key, QueryState, StateStorage};
use essential_transaction_storage::TransactionStorage;
use essential_types::{ContentAddress, Key, Value};
use futures::FutureExt;
use crate::solution::create_post_state;
#[cfg(test)]
mod tests;
#[derive(Clone)]
struct Recorder<S> {
inner: Arc<Inner<S>>,
enabled: bool,
}
struct Inner<S> {
storage: TransactionStorage<S>,
record: StdLock<Recording>,
}
struct Recording(BTreeMap<ContentAddress, BTreeMap<Key, Value>>);
impl<S> Recorder<S> {
fn new(storage: TransactionStorage<S>, enable: bool) -> Self {
let i = Inner {
storage,
record: StdLock::new(Recording(BTreeMap::new())),
};
Self {
inner: Arc::new(i),
enabled: enable,
}
}
fn record(&self, contract: ContentAddress, f: impl FnOnce(&mut BTreeMap<Key, Value>)) {
self.inner.record.apply(|r| {
f(r.0.entry(contract).or_default());
});
}
fn into_recording(self) -> Recording {
self.inner
.record
.apply(|r| Recording(std::mem::take(&mut r.0)))
}
}
pub(crate) async fn query_state_reads<S>(
storage: TransactionStorage<S>,
query: QueryStateReads,
) -> anyhow::Result<QueryStateReadsOutput>
where
S: StateStorage + Clone + Send + Sync + 'static,
{
let QueryStateReads {
state_read,
solution,
index,
request_type,
} = query;
let transient_data = essential_constraint_vm::transient_data(&solution);
let mutable_keys = essential_constraint_vm::mut_keys_set(&solution, index);
let mut slots = Slots::default();
let post_state = create_post_state(&storage, &solution)?;
let pre_state = Recorder::new(
storage,
!matches!(request_type, StateReadRequestType::Slots(_)),
);
let post_state = post_state.view();
for read in state_read {
let solution = SolutionAccess::new(&solution, index, &mutable_keys, &transient_data);
slots
.pre
.extend(read_state(&pre_state, solution, &slots, read.clone()).await?);
slots
.post
.extend(read_state(&post_state, solution, &slots, read).await?);
}
let read_kvs = pre_state.into_recording().0;
let out = match request_type {
StateReadRequestType::All(SlotsRequest::All) => QueryStateReadsOutput::All(read_kvs, slots),
StateReadRequestType::All(SlotsRequest::Pre) => {
slots.post.clear();
QueryStateReadsOutput::All(read_kvs, slots)
}
StateReadRequestType::All(SlotsRequest::Post) => {
slots.pre.clear();
QueryStateReadsOutput::All(read_kvs, slots)
}
StateReadRequestType::Reads => QueryStateReadsOutput::Reads(read_kvs),
StateReadRequestType::Slots(SlotsRequest::All) => QueryStateReadsOutput::Slots(slots),
StateReadRequestType::Slots(SlotsRequest::Pre) => {
slots.post.clear();
QueryStateReadsOutput::Slots(slots)
}
StateReadRequestType::Slots(SlotsRequest::Post) => {
slots.pre.clear();
QueryStateReadsOutput::Slots(slots)
}
};
Ok(out)
}
async fn read_state<S>(
state: &S,
solution: SolutionAccess<'_>,
slots: &Slots,
read: Vec<u8>,
) -> anyhow::Result<Vec<Value>>
where
S: StateRead,
<S as StateRead>::Error: Send + Sync + 'static,
{
let access = Access {
solution,
state_slots: StateSlots {
pre: &slots.pre,
post: &slots.post,
},
};
let mut vm = essential_state_read_vm::Vm::default();
vm.exec_bytecode_iter(read, access, state, &|_: &Op| 1, GasLimit::UNLIMITED)
.await?;
Ok(vm.into_state_slots())
}
impl<S> StateRead for Recorder<S>
where
S: StateStorage + Clone + Send + Sync + 'static,
{
type Error = anyhow::Error;
type Future = Pin<Box<dyn Future<Output = Result<Vec<Value>, Self::Error>> + Send>>;
fn key_range(
&self,
contract_addr: essential_types::ContentAddress,
key: essential_types::Key,
num_values: usize,
) -> Self::Future {
let s = self.clone();
let mut out = Vec::with_capacity(num_values);
async move {
let values =
key_range(&s.inner.storage, contract_addr.clone(), key, num_values).await?;
if s.enabled {
s.record(contract_addr, |r| {
for (k, v) in values {
out.push(v.clone());
r.insert(k, v);
}
});
} else {
out = values.into_iter().map(|(_, v)| v).collect();
}
Ok(out)
}
.boxed()
}
}
async fn key_range<S>(
storage: &TransactionStorage<S>,
contract_addr: ContentAddress,
mut key: Key,
num_words: usize,
) -> anyhow::Result<Vec<(Key, Value)>>
where
S: QueryState + Send,
{
let mut words = Vec::with_capacity(num_words);
for _ in 0..num_words {
let slot = storage.query_state(&contract_addr, &key).await?;
words.push((key.clone(), slot));
key = next_key(key).ok_or_else(|| anyhow::anyhow!("Failed to find next key"))?
}
Ok(words)
}