cannon_io/oracle/oracle_reader.rs
1use crate::syscalls::{self, SyscallError};
2use alloc::vec::Vec;
3
4pub use super::PreimageKey;
5
6#[derive(Debug)]
7pub enum OracleError {
8 NoKeySet,
9 EndOfData,
10 SyscallError(syscalls::SyscallError),
11}
12
13impl From<SyscallError> for OracleError {
14 fn from(e: SyscallError) -> Self {
15 OracleError::SyscallError(e)
16 }
17}
18
19pub struct OracleReader {
20 key: Option<PreimageKey>,
21 length: u64,
22 cursor: u64,
23}
24
25// the only way to access an oracle reader is through this singleton.
26// This is to ensure there cannot be more than one at a time which would have
27// unpredictable results
28static mut ORACLE_READER: Option<OracleReader> = Some(OracleReader {
29 key: None,
30 length: 0,
31 cursor: 0,
32});
33
34/// Get the global oracle reader
35///
36/// # Panics
37/// This will panic if called more than once. This is to ensure there is only one oracle reader at once
38/// as it encapsulates host global state.
39pub fn oracle_reader() -> OracleReader {
40 unsafe {
41 let reader = core::ptr::replace(&mut ORACLE_READER, None);
42 reader.expect("oracle_reader` has already been called. Can only call once per program")
43 }
44}
45
46impl OracleReader {
47 /// Set the preimage key for the global oracle reader. This will overwrite any existing key
48 ///
49 /// Internally this sends the 32 bytes of the key to the host by writing into the WritePreimage file descriptor.
50 /// This may require several writes as the host may only accept a few bytes at a time. Once 32 bytes have been written
51 /// successfully the key is considered set. If it fails to write 32 bytes it will return an error.
52 /// Once it has written the key it will read the first 8 bytes of the ReadPreimage file descriptor which is the length
53 /// encoded as a big endian u64. This is stored in the oracle reader along with the read cursor position.
54 ///
55 /// # Examples
56 /// ```
57 /// use cannon_io::prelude::*;
58 ///
59 /// let mut oracle = oracle_reader();
60 /// oracle.set_key(PreimageKey::new_local(&[0xff;31]));
61 /// ```
62 pub fn set_key(&mut self, key: PreimageKey) -> Result<(), OracleError> {
63 self.key = Some(key);
64 let key_bytes: [u8; 32] = key.into();
65 let mut written = 0;
66 // need to loop and write the bytes a chunk at a time until all are written
67 loop {
68 match syscalls::write_preimage(&key_bytes[written..]) {
69 Ok(0) => break,
70 Ok(n) => {
71 written += n as usize;
72 continue;
73 }
74 Err(e) => return Err(e.into()),
75 }
76 }
77
78 // first read the length prefix, cache and reset the cursor
79 let mut length_buffer = [0_u8; 8];
80 self.read_exact(&mut length_buffer)?;
81 self.length = u64::from_be_bytes(length_buffer);
82 self.cursor = 0;
83 Ok(())
84 }
85
86 /// Return the current key stored in the global oracle reader
87 pub fn key(&self) -> Option<PreimageKey> {
88 self.key
89 }
90
91 /// length of the current pre-image
92 pub fn length(&self) -> u64 {
93 self.length
94 }
95
96 /// Current position of the read cursor within the current pre-image
97 pub fn cursor(&self) -> u64 {
98 self.cursor
99 }
100
101 /// Get the data corresponding to the currently set key from the host. Return the data in a new heap allocated `Vec<u8>`
102 ///
103 /// Internally this reads self.length bytes from the ReadPreimage file descriptor into a new heap allocated `Vec<u8>` and returns it.
104 /// This is a high level way to interact with the preimage oracle but may not be the best way if heap allocations are not desirable.
105 ///
106 /// # Examples
107 /// ```
108 /// use cannon_io::prelude::*;
109 ///
110 /// let mut oracle = oracle_reader();
111 /// let key = PreimageKey::new_local(&[0xff;31]);
112 /// let data = oracle.get(key).unwrap();
113 /// ```
114 pub fn get(&mut self, key: PreimageKey) -> Result<Vec<u8>, OracleError> {
115 self.set_key(key)?;
116 let mut data_buffer = Vec::with_capacity(self.length as usize);
117 data_buffer.resize(self.length as usize, 0);
118 self.read_exact(&mut data_buffer)?;
119 Ok(data_buffer)
120 }
121
122 /// Get the data corresponding to the currently set key from the host. Write the data into the provided buffer
123 ///
124 /// # Panics
125 /// This will panic if the size of the buffer is not equal to the size of the preimage as reported by the host
126 ///
127 /// # Examples
128 /// ```
129 /// use cannon_io::prelude::*;
130 ///
131 /// let mut oracle = oracle_reader();
132 /// let key = PreimageKey::new_local(&[0xff;31]);
133 /// let mut buffer = [0_u8; 100];
134 /// oracle.get_exact(key, &mut buffer).unwrap();
135 /// ```
136 pub fn get_exact(&mut self, key: PreimageKey, buf: &mut [u8]) -> Result<(), OracleError> {
137 self.set_key(key)?;
138 assert!(self.length as usize == buf.len(), "Buffer not correct size for preimage data. Preimage size: {} bytes, buffer size: {} bytes", self.length, buf.len());
139 self.read_exact(buf)?;
140 Ok(())
141 }
142}
143
144// Since the Rust Error trait cannot be used in no_std, we define our own
145// trait that is very similar and should feel familiar
146pub trait Read {
147 type Error;
148
149 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
150
151 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Self::Error>;
152
153 fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>;
154}
155
156impl Read for OracleReader {
157 type Error = OracleError;
158
159 /// Read up to buf.len() bytes from the ReadPreimage file descriptor into buf
160 ///
161 /// This returns the number of bytes read. If the end of the data is reached it will return 0.
162 /// Subsequent calls can be used to write the rest of the data as the host may only accept arbitrary small
163 /// chunks at a time.
164 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
165 self.key.ok_or(Self::Error::NoKeySet)?;
166 let read = syscalls::read_preimage(buf)?;
167 self.cursor += read as u64;
168 Ok(read as usize)
169 }
170
171 /// Read all the data from the ReadPreimage file descriptor into buf
172 ///
173 /// This will read all the data from the ReadPreimage file descriptor into buf. This is implemented by calling
174 /// read() in a loop until it returns 0. This will read at most 32 bytes per call to read.
175 /// New space will be allocated into the return buffer if it fills up.
176 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Self::Error> {
177 let mut chunk = [0; 32];
178 loop {
179 let read = self.read(&mut chunk)?;
180 if read == 0 {
181 break;
182 }
183 buf.extend_from_slice(&chunk[..read]);
184 }
185 Ok(buf.len())
186 }
187
188 /// Read exactly buf.len() bytes from the ReadPreimage file descriptor into buf
189 ///
190 /// This will read exactly buf.len() bytes from the ReadPreimage file descriptor into buf. This is implemented by calling
191 /// read() in a loop until it returns 0. This will read at most 32 bytes per call to read.
192 /// If the end of the data is reached before buf.len() bytes have been read it will return an error.
193 /// After this function returns there may be more bytes in the stream to be read.
194 fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
195 let mut chunk = [0; 32];
196 let mut read = 0;
197 while read < buf.len() {
198 let chunk_read = self.read(&mut chunk)?;
199 if chunk_read == 0 {
200 return Err(OracleError::EndOfData);
201 }
202 buf[read..read + chunk_read].copy_from_slice(&chunk[..chunk_read]);
203 read += chunk_read;
204 }
205 Ok(())
206 }
207}