sov_state/witness.rs
1use std::sync::atomic::AtomicUsize;
2use std::sync::Mutex;
3
4use borsh::{BorshDeserialize, BorshSerialize};
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7
8/// A witness is a value produced during native execution that is then used by
9/// the zkVM circuit to produce proofs.
10///
11/// Witnesses are typically used to abstract away storage access from inside the
12/// zkVM. For every read operation performed by the native code, a hint can be
13/// added and the zkVM circuit can then read the same hint. Hints are replayed
14/// to [`Witness::get_hint`] in the same order
15/// they were added via [`Witness::add_hint`].
16// TODO: Refactor witness trait so it only require Serialize / Deserialize
17// https://github.com/Sovereign-Labs/sovereign-sdk/issues/263
18pub trait Witness: Default + Serialize + DeserializeOwned {
19 /// Adds a serializable "hint" to the witness value, which can be later
20 /// read by the zkVM circuit.
21 ///
22 /// This method **SHOULD** only be called from the native execution
23 /// environment.
24 fn add_hint<T: BorshSerialize>(&self, hint: T);
25
26 /// Retrieves a "hint" from the witness value.
27 fn get_hint<T: BorshDeserialize>(&self) -> T;
28
29 /// Adds all hints from `rhs` to `self`.
30 fn merge(&self, rhs: &Self);
31}
32
33/// A [`Vec`]-based implementation of [`Witness`] with no special logic.
34///
35/// # Example
36///
37/// ```
38/// use sov_state::{ArrayWitness, Witness};
39///
40/// let witness = ArrayWitness::default();
41///
42/// witness.add_hint(1u64);
43/// witness.add_hint(2u64);
44///
45/// assert_eq!(witness.get_hint::<u64>(), 1u64);
46/// assert_eq!(witness.get_hint::<u64>(), 2u64);
47/// ```
48#[derive(Default, Debug, Serialize, Deserialize)]
49pub struct ArrayWitness {
50 next_idx: AtomicUsize,
51 hints: Mutex<Vec<Vec<u8>>>,
52}
53
54impl Witness for ArrayWitness {
55 fn add_hint<T: BorshSerialize>(&self, hint: T) {
56 self.hints.lock().unwrap().push(hint.try_to_vec().unwrap())
57 }
58
59 fn get_hint<T: BorshDeserialize>(&self) -> T {
60 let idx = self
61 .next_idx
62 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
63 let hints_lock = self.hints.lock().unwrap();
64 T::deserialize_reader(&mut std::io::Cursor::new(&hints_lock[idx]))
65 .expect("Hint deserialization should never fail")
66 }
67
68 fn merge(&self, rhs: &Self) {
69 let rhs_next_idx = rhs.next_idx.load(std::sync::atomic::Ordering::SeqCst);
70 let mut lhs_hints_lock = self.hints.lock().unwrap();
71 let mut rhs_hints_lock = rhs.hints.lock().unwrap();
72 lhs_hints_lock.extend(rhs_hints_lock.drain(rhs_next_idx..))
73 }
74}