Skip to main content

freenet_stdlib/contract_interface/
encoding.rs

1//! Helper types for interaction between wasm and host boundaries.
2use std::{
3    collections::{HashMap, HashSet},
4    marker::PhantomData,
5};
6
7use serde::{de::DeserializeOwned, Serialize};
8
9use super::*;
10use crate::parameters::Parameters;
11
12pub enum MergeResult {
13    Success,
14    RequestRelated(RelatedContractsContainer),
15    Error(ContractError),
16}
17
18#[derive(Default)]
19pub struct RelatedContractsContainer {
20    contracts: HashMap<ContractInstanceId, State<'static>>,
21    pending: HashSet<ContractInstanceId>,
22    not_found: HashSet<ContractInstanceId>,
23}
24
25impl From<RelatedContracts<'static>> for RelatedContractsContainer {
26    fn from(found: RelatedContracts<'static>) -> Self {
27        let mut not_found = HashSet::new();
28        let mut contracts = HashMap::new();
29        for (id, state) in found.states() {
30            match state {
31                Some(state) => {
32                    contracts.insert(*id, state.clone());
33                }
34                None => {
35                    not_found.insert(*id);
36                }
37            }
38        }
39        RelatedContractsContainer {
40            contracts,
41            pending: HashSet::new(),
42            not_found,
43        }
44    }
45}
46
47impl From<RelatedContractsContainer> for Vec<crate::contract_interface::RelatedContract> {
48    fn from(related: RelatedContractsContainer) -> Self {
49        related
50            .pending
51            .into_iter()
52            .map(|id| RelatedContract {
53                contract_instance_id: id,
54                mode: RelatedMode::StateOnce,
55            })
56            .collect()
57    }
58}
59
60impl From<Vec<UpdateData<'static>>> for RelatedContractsContainer {
61    fn from(updates: Vec<UpdateData<'static>>) -> Self {
62        let mut this = RelatedContractsContainer::default();
63        for update in updates {
64            match update {
65                UpdateData::RelatedState { related_to, state } => {
66                    this.contracts.insert(related_to, state);
67                }
68                UpdateData::RelatedStateAndDelta {
69                    related_to, state, ..
70                } => {
71                    this.contracts.insert(related_to, state);
72                }
73                _ => {}
74            }
75        }
76        this
77    }
78}
79
80impl RelatedContractsContainer {
81    pub fn get<C: TypedContract>(
82        &self,
83        params: &C::Parameters,
84    ) -> Result<Related<C>, <<C as EncodingAdapter>::SelfEncoder as Encoder<C>>::Error> {
85        let id = <C as TypedContract>::instance_id(params);
86        if let Some(res) = self.contracts.get(&id) {
87            let state = <<C as EncodingAdapter>::SelfEncoder>::deserialize(res.as_ref())?;
88            return Ok(Related::Found { state });
89        }
90        if self.pending.contains(&id) {
91            return Ok(Related::RequestPending);
92        }
93        if self.not_found.contains(&id) {
94            return Ok(Related::NotFound);
95        }
96        Ok(Related::NotRequested)
97    }
98
99    pub fn request<C: TypedContract>(&mut self, id: ContractInstanceId) {
100        self.pending.insert(id);
101    }
102
103    pub fn merge(&mut self, other: Self) {
104        let Self {
105            contracts,
106            pending,
107            not_found,
108        } = other;
109        self.pending.extend(pending);
110        self.not_found.extend(not_found);
111        self.contracts.extend(contracts);
112    }
113}
114
115pub enum Related<C: TypedContract> {
116    /// The state was previously requested and found
117    Found { state: C },
118    /// The state was previously requested but not found
119    NotFound,
120    /// The state was previously requested but request is still in flight
121    RequestPending,
122    /// The state was not previously requested, this enum can be included
123    /// in the MergeResult return value which will request it
124    NotRequested,
125}
126
127/// A contract state and it's associated types which can be encoded and decoded
128/// via an specific encoder.
129pub trait EncodingAdapter
130where
131    Self: Sized,
132{
133    type Parameters;
134    type Delta;
135    type Summary;
136
137    type SelfEncoder: Encoder<Self>;
138    type ParametersEncoder: Encoder<Self::Parameters>;
139    type DeltaEncoder: Encoder<Self::Delta>;
140    type SummaryEncoder: Encoder<Self::Summary>;
141}
142
143pub enum TypedUpdateData<T: EncodingAdapter> {
144    RelatedState { state: T },
145    RelatedDelta { delta: T::Delta },
146    RelatedStateAndDelta { state: T, delta: T::Delta },
147}
148
149impl<T: EncodingAdapter> TypedUpdateData<T> {
150    pub fn from_other<Parent>(value: &TypedUpdateData<Parent>) -> Self
151    where
152        Parent: EncodingAdapter,
153        T: for<'x> From<&'x Parent>,
154        T::Delta: for<'x> From<&'x Parent::Delta>,
155    {
156        match value {
157            TypedUpdateData::RelatedState { state } => {
158                let state = T::from(state);
159                TypedUpdateData::RelatedState { state }
160            }
161            TypedUpdateData::RelatedDelta { delta } => {
162                let delta: T::Delta = <T as EncodingAdapter>::Delta::from(delta);
163                TypedUpdateData::RelatedDelta { delta }
164            }
165            TypedUpdateData::RelatedStateAndDelta { state, delta } => {
166                let state = T::from(state);
167                let delta: T::Delta = <T as EncodingAdapter>::Delta::from(delta);
168                TypedUpdateData::RelatedStateAndDelta { state, delta }
169            }
170        }
171    }
172}
173
174impl<T: EncodingAdapter> TryFrom<(Option<T>, Option<T::Delta>)> for TypedUpdateData<T> {
175    type Error = ContractError;
176    fn try_from((state, delta): (Option<T>, Option<T::Delta>)) -> Result<Self, Self::Error> {
177        match (state, delta) {
178            (None, None) => Err(ContractError::InvalidState),
179            (None, Some(delta)) => Ok(Self::RelatedDelta { delta }),
180            (Some(state), None) => Ok(Self::RelatedState { state }),
181            (Some(state), Some(delta)) => Ok(Self::RelatedStateAndDelta { state, delta }),
182        }
183    }
184}
185
186pub trait TypedContract: EncodingAdapter {
187    fn instance_id(params: &Self::Parameters) -> ContractInstanceId;
188
189    fn verify(
190        &self,
191        parameters: Self::Parameters,
192        related: RelatedContractsContainer,
193    ) -> Result<ValidateResult, ContractError>;
194
195    fn merge(
196        &mut self,
197        parameters: &Self::Parameters,
198        update: TypedUpdateData<Self>,
199        related: &RelatedContractsContainer,
200    ) -> MergeResult;
201
202    fn summarize(&self, parameters: Self::Parameters) -> Result<Self::Summary, ContractError>;
203
204    fn delta(
205        &self,
206        parameters: Self::Parameters,
207        summary: Self::Summary,
208    ) -> Result<Self::Delta, ContractError>;
209}
210
211pub trait Encoder<T> {
212    type Error: Into<ContractError>;
213    fn deserialize(bytes: &[u8]) -> Result<T, Self::Error>;
214    fn serialize(value: &T) -> Result<Vec<u8>, Self::Error>;
215}
216
217pub struct JsonEncoder<T>(PhantomData<T>);
218
219impl<T> Encoder<T> for JsonEncoder<T>
220where
221    T: DeserializeOwned + Serialize,
222{
223    type Error = serde_json::Error;
224
225    fn deserialize(bytes: &[u8]) -> Result<T, Self::Error> {
226        serde_json::from_slice(bytes)
227    }
228
229    fn serialize(value: &T) -> Result<Vec<u8>, Self::Error> {
230        serde_json::to_vec(value)
231    }
232}
233
234impl From<serde_json::Error> for ContractError {
235    fn from(value: serde_json::Error) -> Self {
236        ContractError::Deser(format!("{value}"))
237    }
238}
239
240pub struct BincodeEncoder<T>(PhantomData<T>);
241
242impl<T> Encoder<T> for BincodeEncoder<T>
243where
244    T: DeserializeOwned + Serialize,
245{
246    type Error = bincode::Error;
247
248    fn deserialize(bytes: &[u8]) -> Result<T, Self::Error> {
249        bincode::deserialize(bytes)
250    }
251
252    fn serialize(value: &T) -> Result<Vec<u8>, Self::Error> {
253        bincode::serialize(value)
254    }
255}
256
257impl From<bincode::Error> for ContractError {
258    fn from(value: bincode::Error) -> Self {
259        ContractError::Deser(format!("{value}"))
260    }
261}
262
263pub fn inner_validate_state<T>(
264    parameters: Parameters<'static>,
265    state: State<'static>,
266    related: RelatedContracts<'static>,
267) -> Result<ValidateResult, ContractError>
268where
269    T: EncodingAdapter + TypedContract,
270    ContractError: From<
271        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
272            <T as EncodingAdapter>::Parameters,
273        >>::Error,
274    >,
275    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
276{
277    let typed_params =
278        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
279    let typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
280    let related_container = RelatedContractsContainer::from(related);
281    typed_state.verify(typed_params, related_container)
282}
283
284pub fn inner_update_state<T>(
285    parameters: Parameters<'static>,
286    state: State<'static>,
287    data: Vec<UpdateData<'static>>,
288) -> Result<UpdateModification<'static>, ContractError>
289where
290    T: EncodingAdapter + TypedContract,
291    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
292    ContractError: From<
293        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
294            <T as EncodingAdapter>::Parameters,
295        >>::Error,
296    >,
297    ContractError: From<
298        <<T as EncodingAdapter>::DeltaEncoder as Encoder<<T as EncodingAdapter>::Delta>>::Error,
299    >,
300{
301    let typed_params =
302        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
303    let mut typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
304    let self_updates = UpdateData::get_self_states(&data);
305    let related_container = RelatedContractsContainer::from(data);
306    for (state, delta) in self_updates {
307        let state = state
308            .map(|s| <<T as EncodingAdapter>::SelfEncoder>::deserialize(s.as_ref()))
309            .transpose()?;
310        let delta = delta
311            .map(|d| <<T as EncodingAdapter>::DeltaEncoder>::deserialize(d.as_ref()))
312            .transpose()?;
313        let typed_update = TypedUpdateData::try_from((state, delta))?;
314        match typed_state.merge(&typed_params, typed_update, &related_container) {
315            MergeResult::Success => {}
316            MergeResult::RequestRelated(req) => {
317                return UpdateModification::requires(req.into());
318            }
319            MergeResult::Error(err) => return Err(err),
320        }
321    }
322    let encoded = <<T as EncodingAdapter>::SelfEncoder>::serialize(&typed_state)?;
323    Ok(UpdateModification::valid(encoded.into()))
324}
325
326pub fn inner_summarize_state<T>(
327    parameters: Parameters<'static>,
328    state: State<'static>,
329) -> Result<StateSummary<'static>, ContractError>
330where
331    T: EncodingAdapter + TypedContract,
332    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
333    ContractError: From<
334        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
335            <T as EncodingAdapter>::Parameters,
336        >>::Error,
337    >,
338    ContractError: From<
339        <<T as EncodingAdapter>::SummaryEncoder as Encoder<<T as EncodingAdapter>::Summary>>::Error,
340    >,
341{
342    let typed_params =
343        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
344    let typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
345    let summary = typed_state.summarize(typed_params)?;
346    let encoded = <<T as EncodingAdapter>::SummaryEncoder>::serialize(&summary)?;
347    Ok(encoded.into())
348}
349
350pub fn inner_state_delta<T>(
351    parameters: Parameters<'static>,
352    state: State<'static>,
353    summary: StateSummary<'static>,
354) -> Result<StateDelta<'static>, ContractError>
355where
356    T: EncodingAdapter + TypedContract,
357    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
358    ContractError: From<
359        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
360            <T as EncodingAdapter>::Parameters,
361        >>::Error,
362    >,
363    ContractError: From<
364        <<T as EncodingAdapter>::SummaryEncoder as Encoder<<T as EncodingAdapter>::Summary>>::Error,
365    >,
366    ContractError: From<
367        <<T as EncodingAdapter>::DeltaEncoder as Encoder<<T as EncodingAdapter>::Delta>>::Error,
368    >,
369{
370    let typed_params =
371        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
372    let typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
373    let typed_summary = <<T as EncodingAdapter>::SummaryEncoder>::deserialize(summary.as_ref())?;
374    let summary = typed_state.delta(typed_params, typed_summary)?;
375    let encoded = <<T as EncodingAdapter>::DeltaEncoder>::serialize(&summary)?;
376    Ok(encoded.into())
377}