essential_vm/
state_read.rs

1//! State read operation implementations.
2
3use crate::{
4    error::{MemoryError, OpAsyncError, OpAsyncResult, StackError},
5    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/// Read-only access to state required by the 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 memory address at which this should start writing into.
49    mem_addr: 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<S> Future for StateReadFuture<'_, 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 mem_addr = self.mem_addr;
66                let res = res
67                    .map_err(OpAsyncError::StateRead)
68                    .and_then(|words| write_values_to_memory(mem_addr, words, self.vm));
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 mem_addr = vm.stack.pop()?;
85    let mem_addr = usize::try_from(mem_addr).map_err(|_| MemoryError::IndexOutOfBounds)?;
86    let future = read_key_range(state_read, contract_addr, vm)?;
87    Ok(StateReadFuture {
88        future,
89        mem_addr,
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 mem_addr = vm.stack.pop()?;
103    let mem_addr = usize::try_from(mem_addr).map_err(|_| MemoryError::IndexOutOfBounds)?;
104    let future = read_key_range_ext(state_read, vm)?;
105    Ok(StateReadFuture {
106        future,
107        mem_addr,
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 memory.
145fn write_values_to_memory<E>(
146    mem_addr: usize,
147    values: Vec<Vec<Word>>,
148    vm: &mut Vm,
149) -> OpAsyncResult<(), E> {
150    let values_len = Word::try_from(values.len()).map_err(|_| MemoryError::Overflow)?;
151    let index_len_pairs_len = values_len.checked_mul(2).ok_or(MemoryError::Overflow)?;
152    let mut mem_addr = Word::try_from(mem_addr).map_err(|_| MemoryError::IndexOutOfBounds)?;
153    let mut value_addr = mem_addr
154        .checked_add(index_len_pairs_len)
155        .ok_or(MemoryError::Overflow)?;
156    for value in values {
157        let value_len = Word::try_from(value.len()).map_err(|_| MemoryError::Overflow)?;
158        // Write the [index, len] pair.
159        vm.memory.store_range(mem_addr, &[value_addr, value_len])?;
160        // Write the value.
161        vm.memory.store_range(value_addr, &value)?;
162        // No need to check addition here as `store_range` would have failed.
163        value_addr += value_len;
164        mem_addr += 2;
165    }
166    Ok(())
167}