aurora_engine_sdk/
io.rs

1use crate::error;
2use crate::prelude::{vec, Vec};
3use aurora_engine_types::borsh::{BorshDeserialize, BorshSerialize};
4use aurora_engine_types::{borsh, U256};
5
6/// The purpose of this trait is to represent a reference to a value that
7/// could be obtained by IO, but without eagerly loading it into memory.
8/// For example, the NEAR runtime registers API allows querying the length
9/// of some bytes read from input or storage without loading them into the
10/// wasm memory.
11pub trait StorageIntermediate: Sized {
12    fn len(&self) -> usize;
13    fn is_empty(&self) -> bool;
14    fn copy_to_slice(&self, buffer: &mut [u8]);
15
16    fn to_vec(&self) -> Vec<u8> {
17        let size = self.len();
18        let mut buf = vec![0u8; size];
19        self.copy_to_slice(&mut buf);
20        buf
21    }
22
23    fn to_value<T: BorshDeserialize>(&self) -> Result<T, error::BorshDeserializeError> {
24        let bytes = self.to_vec();
25        T::try_from_slice(&bytes).map_err(|_| error::BorshDeserializeError)
26    }
27}
28
29/// Trait for reading/writing values from storage and a generalized `stdin`/`stdout`.
30pub trait IO {
31    /// A type giving a reference to a value obtained by IO without loading it
32    /// into memory. For example, in the case of a wasm contract on NEAR this
33    /// will correspond to a register index.
34    type StorageValue: StorageIntermediate;
35
36    /// Read bytes that were passed as input to the process. This can be thought of as a
37    /// generalization of `stdin` or command-line arguments. In the case of wasm contracts
38    /// on NEAR these would be the arguments to the method.
39    fn read_input(&self) -> Self::StorageValue;
40
41    /// Return a value to an external process. In the case of wasm contracts on NEAR
42    /// this corresponds to the return value from the contract method.
43    fn return_output(&mut self, value: &[u8]);
44
45    /// Read the value in storage at the given key, if any.
46    fn read_storage(&self, key: &[u8]) -> Option<Self::StorageValue>;
47
48    /// Check if there is a value in storage at the given key, but do not read the value.
49    /// Equivalent to `self.read_storage(key).is_some()` but more efficient.
50    fn storage_has_key(&self, key: &[u8]) -> bool;
51
52    /// Write the given value to storage under the given key. Returns a reference to the old
53    /// value stored at that key (if any).
54    fn write_storage(&mut self, key: &[u8], value: &[u8]) -> Option<Self::StorageValue>;
55
56    /// Write a `StorageIntermediate` to storage directly under the given key
57    /// (without ever needing to load the value into memory).Returns a reference
58    /// to the old value stored at that key (if any).
59    fn write_storage_direct(
60        &mut self,
61        key: &[u8],
62        value: Self::StorageValue,
63    ) -> Option<Self::StorageValue>;
64
65    /// Remove entry from storage and capture the value present at the given key (if any)
66    fn remove_storage(&mut self, key: &[u8]) -> Option<Self::StorageValue>;
67
68    /// Read the length of the bytes stored at the given key.
69    fn read_storage_len(&self, key: &[u8]) -> Option<usize> {
70        self.read_storage(key).map(|s| s.len())
71    }
72
73    /// Convenience function to read the input and deserialize the bytes using borsh.
74    fn read_input_borsh<U: BorshDeserialize>(&self) -> Result<U, error::BorshDeserializeError> {
75        self.read_input().to_value()
76    }
77
78    /// Convenience function to read the input into a 20-byte array.
79    fn read_input_arr20(&self) -> Result<[u8; 20], error::IncorrectInputLength> {
80        let value = self.read_input();
81
82        if value.len() != 20 {
83            return Err(error::IncorrectInputLength);
84        }
85
86        let mut buf = [0u8; 20];
87        value.copy_to_slice(&mut buf);
88        Ok(buf)
89    }
90
91    /// Convenience function to read the input into a 32-byte array.
92    fn read_input_arr32(&self) -> Result<[u8; 32], error::IncorrectInputLength> {
93        let value = self.read_input();
94
95        if value.len() != 32 {
96            return Err(error::IncorrectInputLength);
97        }
98
99        let mut buf = [0u8; 32];
100        value.copy_to_slice(&mut buf);
101        Ok(buf)
102    }
103
104    /// Convenience function to store the input directly in storage under the
105    /// given key (without ever loading it into memory).
106    fn read_input_and_store(&mut self, key: &[u8]) {
107        let value = self.read_input();
108        self.write_storage_direct(key, value);
109    }
110
111    /// Convenience function to read a 32-bit unsigned integer from storage
112    /// (assumes little-endian encoding).
113    fn read_u32(&self, key: &[u8]) -> Result<u32, error::ReadU32Error> {
114        let value = self
115            .read_storage(key)
116            .ok_or(error::ReadU32Error::MissingValue)?;
117
118        if value.len() != 4 {
119            return Err(error::ReadU32Error::InvalidU32);
120        }
121
122        let mut result = [0u8; 4];
123        value.copy_to_slice(&mut result);
124        Ok(u32::from_le_bytes(result))
125    }
126
127    /// Convenience function to read a 64-bit unsigned integer from storage
128    /// (assumes little-endian encoding).
129    fn read_u64(&self, key: &[u8]) -> Result<u64, error::ReadU64Error> {
130        let value = self
131            .read_storage(key)
132            .ok_or(error::ReadU64Error::MissingValue)?;
133
134        if value.len() != 8 {
135            return Err(error::ReadU64Error::InvalidU64);
136        }
137
138        let mut result = [0u8; 8];
139        value.copy_to_slice(&mut result);
140        Ok(u64::from_le_bytes(result))
141    }
142
143    /// Convenience function to read a 256-bit unsigned integer from storage
144    /// (assumes big-endian encoding).
145    fn read_u256(&self, key: &[u8]) -> Result<U256, error::ReadU256Error> {
146        let value = self
147            .read_storage(key)
148            .ok_or(error::ReadU256Error::MissingValue)?;
149
150        if value.len() != 32 {
151            return Err(error::ReadU256Error::InvalidU256);
152        }
153
154        let mut result = [0u8; 32];
155        value.copy_to_slice(&mut result);
156        Ok(U256::from_big_endian(&result))
157    }
158
159    fn write_borsh<T: BorshSerialize>(
160        &mut self,
161        key: &[u8],
162        value: &T,
163    ) -> Option<Self::StorageValue> {
164        let bytes = borsh::to_vec(&value).ok()?;
165        self.write_storage(key, &bytes)
166    }
167}