essential_state_read_vm/
state_read.rs

1//! State read operation implementations.
2
3use crate::{
4    error::{OpAsyncError, StackError},
5    OpAsyncResult, Vm,
6};
7use core::{
8    future::Future,
9    pin::Pin,
10    task::{Context, Poll},
11};
12use essential_types::{convert::u8_32_from_word_4, ContentAddress, Key, Word};
13
14/// Access to state required by the state read VM.
15pub trait StateRead {
16    /// An error type describing any cases that might occur during state reading.
17    type Error: core::fmt::Debug + core::fmt::Display;
18    /// The future type returned from the `key_range` method.
19    ///
20    /// ## Unpin
21    ///
22    /// This `Future` must be `Unpin` in order for the `Vm`'s `ExecFuture`
23    /// to remain zero-allocation by default. Implementers may decide on
24    /// whether they require dynamic allocation as a part of their `StateRead`
25    /// implementation.
26    ///
27    /// It is likely that in-memory implementations may be `Unpin` by default
28    /// using `std::future::Ready`, however more involved implementations that
29    /// require calling `async` functions with anonymised return types may
30    /// require using a `Box` in order to name the anonymised type.
31    type Future: Future<Output = Result<Vec<Vec<Word>>, Self::Error>> + Unpin;
32
33    /// Read the given number of values from state at the given key associated
34    /// with the given contract address.
35    fn key_range(&self, contract_addr: ContentAddress, key: Key, num_values: usize)
36        -> Self::Future;
37}
38
39/// A future representing the asynchronous `StateRead` (or `StateReadExtern`) operation.
40///
41/// Performs the state read and then writes the result to memory.
42pub(crate) struct StateReadFuture<'vm, S>
43where
44    S: StateRead,
45{
46    /// The future produced by the `StateRead::key_range` implementation.
47    future: S::Future,
48    /// The index of the slot that this should start writing into.
49    slot_index: usize,
50    /// Access to the `Vm` so that the result of the future can be written to memory.
51    pub(crate) vm: &'vm mut Vm,
52}
53
54impl<'vm, S> Future for StateReadFuture<'vm, S>
55where
56    S: StateRead,
57{
58    /// Returns a `Result` representing whether or not the state was read and
59    /// written to memory successfully.
60    type Output = OpAsyncResult<(), S::Error>;
61    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
62        match Pin::new(&mut self.future).poll(cx) {
63            Poll::Pending => Poll::Pending,
64            Poll::Ready(res) => {
65                let slot_index = self.slot_index;
66                let res = res
67                    .map_err(OpAsyncError::StateRead)
68                    .and_then(|words| write_values_to_state_slots(self.vm, slot_index, words));
69                Poll::Ready(res)
70            }
71        }
72    }
73}
74
75/// `StateRead::KeyRange` operation.
76pub fn key_range<'vm, S>(
77    state_read: &S,
78    contract_addr: &ContentAddress,
79    vm: &'vm mut Vm,
80) -> OpAsyncResult<StateReadFuture<'vm, S>, S::Error>
81where
82    S: StateRead,
83{
84    let slot_index = vm.stack.pop()?;
85    let slot_index = usize::try_from(slot_index).map_err(|_| StackError::IndexOutOfBounds)?;
86    let future = read_key_range(state_read, contract_addr, vm)?;
87    Ok(StateReadFuture {
88        future,
89        slot_index,
90        vm,
91    })
92}
93
94/// `StateRead::KeyRangeExtern` operation.
95pub fn key_range_ext<'vm, S>(
96    state_read: &S,
97    vm: &'vm mut Vm,
98) -> OpAsyncResult<StateReadFuture<'vm, S>, S::Error>
99where
100    S: StateRead,
101{
102    let slot_index = vm.stack.pop()?;
103    let slot_index = usize::try_from(slot_index).map_err(|_| StackError::IndexOutOfBounds)?;
104    let future = read_key_range_ext(state_read, vm)?;
105    Ok(StateReadFuture {
106        future,
107        slot_index,
108        vm,
109    })
110}
111
112/// Read the length and key from the top of the stack and read the associated words from state.
113fn read_key_range<S>(
114    state_read: &S,
115    contract_addr: &ContentAddress,
116    vm: &mut Vm,
117) -> OpAsyncResult<S::Future, S::Error>
118where
119    S: StateRead,
120{
121    let num_keys = vm.stack.pop()?;
122    let num_keys = usize::try_from(num_keys).map_err(|_| StackError::IndexOutOfBounds)?;
123    let key = vm
124        .stack
125        .pop_len_words::<_, _, StackError>(|words| Ok(words.to_vec()))?;
126    Ok(state_read.key_range(contract_addr.clone(), key, num_keys))
127}
128
129/// Read the length, key and external contract address from the top of the stack and
130/// read the associated words from state.
131fn read_key_range_ext<S>(state_read: &S, vm: &mut Vm) -> OpAsyncResult<S::Future, S::Error>
132where
133    S: StateRead,
134{
135    let num_keys = vm.stack.pop()?;
136    let num_keys = usize::try_from(num_keys).map_err(|_| StackError::IndexOutOfBounds)?;
137    let key = vm
138        .stack
139        .pop_len_words::<_, _, StackError>(|words| Ok(words.to_vec()))?;
140    let contract_addr = ContentAddress(u8_32_from_word_4(vm.stack.pop4()?));
141    Ok(state_read.key_range(contract_addr, key, num_keys))
142}
143
144/// Write the given values to mutable state slots.
145fn write_values_to_state_slots<E>(
146    vm: &mut Vm,
147    slot_index: usize,
148    values: Vec<Vec<Word>>,
149) -> OpAsyncResult<(), E> {
150    vm.state_memory.store_slots_range(slot_index, values)?;
151    Ok(())
152}