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            match <<C as EncodingAdapter>::SelfEncoder>::deserialize(res.as_ref()) {
88                Ok(state) => return Ok(Related::Found { state }),
89                Err(err) => return Err(err),
90            }
91        }
92        if self.pending.contains(&id) {
93            return Ok(Related::RequestPending);
94        }
95        if self.not_found.contains(&id) {
96            return Ok(Related::NotFound);
97        }
98        Ok(Related::NotRequested)
99    }
100
101    pub fn request<C: TypedContract>(&mut self, id: ContractInstanceId) {
102        self.pending.insert(id);
103    }
104
105    pub fn merge(&mut self, other: Self) {
106        let Self {
107            contracts,
108            pending,
109            not_found,
110        } = other;
111        self.pending.extend(pending);
112        self.not_found.extend(not_found);
113        self.contracts.extend(contracts);
114    }
115}
116
117pub enum Related<C: TypedContract> {
118    /// The state was previously requested and found
119    Found { state: C },
120    /// The state was previously requested but not found
121    NotFound,
122    /// The state was previously requested but request is still in flight
123    RequestPending,
124    /// The state was not previously requested, this enum can be included
125    /// in the MergeResult return value which will request it
126    NotRequested,
127}
128
129/// A contract state and it's associated types which can be encoded and decoded
130/// via an specific encoder.
131pub trait EncodingAdapter
132where
133    Self: Sized,
134{
135    type Parameters;
136    type Delta;
137    type Summary;
138
139    type SelfEncoder: Encoder<Self>;
140    type ParametersEncoder: Encoder<Self::Parameters>;
141    type DeltaEncoder: Encoder<Self::Delta>;
142    type SummaryEncoder: Encoder<Self::Summary>;
143}
144
145pub enum TypedUpdateData<T: EncodingAdapter> {
146    RelatedState { state: T },
147    RelatedDelta { delta: T::Delta },
148    RelatedStateAndDelta { state: T, delta: T::Delta },
149}
150
151impl<T: EncodingAdapter> TypedUpdateData<T> {
152    pub fn from_other<Parent>(value: &TypedUpdateData<Parent>) -> Self
153    where
154        Parent: EncodingAdapter,
155        T: for<'x> From<&'x Parent>,
156        T::Delta: for<'x> From<&'x Parent::Delta>,
157    {
158        match value {
159            TypedUpdateData::RelatedState { state } => {
160                let state = T::from(state);
161                TypedUpdateData::RelatedState { state }
162            }
163            TypedUpdateData::RelatedDelta { delta } => {
164                let delta: T::Delta = <T as EncodingAdapter>::Delta::from(delta);
165                TypedUpdateData::RelatedDelta { delta }
166            }
167            TypedUpdateData::RelatedStateAndDelta { state, delta } => {
168                let state = T::from(state);
169                let delta: T::Delta = <T as EncodingAdapter>::Delta::from(delta);
170                TypedUpdateData::RelatedStateAndDelta { state, delta }
171            }
172        }
173    }
174}
175
176impl<T: EncodingAdapter> TryFrom<(Option<T>, Option<T::Delta>)> for TypedUpdateData<T> {
177    type Error = ContractError;
178    fn try_from((state, delta): (Option<T>, Option<T::Delta>)) -> Result<Self, Self::Error> {
179        match (state, delta) {
180            (None, None) => Err(ContractError::InvalidState),
181            (None, Some(delta)) => Ok(Self::RelatedDelta { delta }),
182            (Some(state), None) => Ok(Self::RelatedState { state }),
183            (Some(state), Some(delta)) => Ok(Self::RelatedStateAndDelta { state, delta }),
184        }
185    }
186}
187
188pub trait TypedContract: EncodingAdapter {
189    fn instance_id(params: &Self::Parameters) -> ContractInstanceId;
190
191    fn verify(
192        &self,
193        parameters: Self::Parameters,
194        related: RelatedContractsContainer,
195    ) -> Result<ValidateResult, ContractError>;
196
197    fn merge(
198        &mut self,
199        parameters: &Self::Parameters,
200        update: TypedUpdateData<Self>,
201        related: &RelatedContractsContainer,
202    ) -> MergeResult;
203
204    fn summarize(&self, parameters: Self::Parameters) -> Result<Self::Summary, ContractError>;
205
206    fn delta(
207        &self,
208        parameters: Self::Parameters,
209        summary: Self::Summary,
210    ) -> Result<Self::Delta, ContractError>;
211}
212
213pub trait Encoder<T> {
214    type Error: Into<ContractError>;
215    fn deserialize(bytes: &[u8]) -> Result<T, Self::Error>;
216    fn serialize(value: &T) -> Result<Vec<u8>, Self::Error>;
217}
218
219pub struct JsonEncoder<T>(PhantomData<T>);
220
221impl<T> Encoder<T> for JsonEncoder<T>
222where
223    T: DeserializeOwned + Serialize,
224{
225    type Error = serde_json::Error;
226
227    fn deserialize(bytes: &[u8]) -> Result<T, Self::Error> {
228        serde_json::from_slice(bytes)
229    }
230
231    fn serialize(value: &T) -> Result<Vec<u8>, Self::Error> {
232        serde_json::to_vec(value)
233    }
234}
235
236impl From<serde_json::Error> for ContractError {
237    fn from(value: serde_json::Error) -> Self {
238        ContractError::Deser(format!("{value}"))
239    }
240}
241
242pub struct BincodeEncoder<T>(PhantomData<T>);
243
244impl<T> Encoder<T> for BincodeEncoder<T>
245where
246    T: DeserializeOwned + Serialize,
247{
248    type Error = bincode::Error;
249
250    fn deserialize(bytes: &[u8]) -> Result<T, Self::Error> {
251        bincode::deserialize(bytes)
252    }
253
254    fn serialize(value: &T) -> Result<Vec<u8>, Self::Error> {
255        bincode::serialize(value)
256    }
257}
258
259impl From<bincode::Error> for ContractError {
260    fn from(value: bincode::Error) -> Self {
261        ContractError::Deser(format!("{value}"))
262    }
263}
264
265pub fn inner_validate_state<T>(
266    parameters: Parameters<'static>,
267    state: State<'static>,
268    related: RelatedContracts<'static>,
269) -> Result<ValidateResult, ContractError>
270where
271    T: EncodingAdapter + TypedContract,
272    ContractError: From<
273        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
274            <T as EncodingAdapter>::Parameters,
275        >>::Error,
276    >,
277    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
278{
279    let typed_params =
280        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
281    let typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
282    let related_container = RelatedContractsContainer::from(related);
283    typed_state.verify(typed_params, related_container)
284}
285
286pub fn inner_update_state<T>(
287    parameters: Parameters<'static>,
288    state: State<'static>,
289    data: Vec<UpdateData<'static>>,
290) -> Result<UpdateModification<'static>, ContractError>
291where
292    T: EncodingAdapter + TypedContract,
293    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
294    ContractError: From<
295        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
296            <T as EncodingAdapter>::Parameters,
297        >>::Error,
298    >,
299    ContractError: From<
300        <<T as EncodingAdapter>::DeltaEncoder as Encoder<<T as EncodingAdapter>::Delta>>::Error,
301    >,
302{
303    let typed_params =
304        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
305    let mut typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
306    let self_updates = UpdateData::get_self_states(&data);
307    let related_container = RelatedContractsContainer::from(data);
308    for (state, delta) in self_updates {
309        let state = state
310            .map(|s| <<T as EncodingAdapter>::SelfEncoder>::deserialize(s.as_ref()))
311            .transpose()?;
312        let delta = delta
313            .map(|d| <<T as EncodingAdapter>::DeltaEncoder>::deserialize(d.as_ref()))
314            .transpose()?;
315        let typed_update = TypedUpdateData::try_from((state, delta))?;
316        match typed_state.merge(&typed_params, typed_update, &related_container) {
317            MergeResult::Success => {}
318            MergeResult::RequestRelated(req) => {
319                return UpdateModification::requires(req.into());
320            }
321            MergeResult::Error(err) => return Err(err),
322        }
323    }
324    let encoded = <<T as EncodingAdapter>::SelfEncoder>::serialize(&typed_state)?;
325    Ok(UpdateModification::valid(encoded.into()))
326}
327
328pub fn inner_summarize_state<T>(
329    parameters: Parameters<'static>,
330    state: State<'static>,
331) -> Result<StateSummary<'static>, ContractError>
332where
333    T: EncodingAdapter + TypedContract,
334    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
335    ContractError: From<
336        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
337            <T as EncodingAdapter>::Parameters,
338        >>::Error,
339    >,
340    ContractError: From<
341        <<T as EncodingAdapter>::SummaryEncoder as Encoder<<T as EncodingAdapter>::Summary>>::Error,
342    >,
343{
344    let typed_params =
345        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
346    let typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
347    let summary = typed_state.summarize(typed_params)?;
348    let encoded = <<T as EncodingAdapter>::SummaryEncoder>::serialize(&summary)?;
349    Ok(encoded.into())
350}
351
352pub fn inner_state_delta<T>(
353    parameters: Parameters<'static>,
354    state: State<'static>,
355    summary: StateSummary<'static>,
356) -> Result<StateDelta<'static>, ContractError>
357where
358    T: EncodingAdapter + TypedContract,
359    ContractError: From<<<T as EncodingAdapter>::SelfEncoder as Encoder<T>>::Error>,
360    ContractError: From<
361        <<T as EncodingAdapter>::ParametersEncoder as Encoder<
362            <T as EncodingAdapter>::Parameters,
363        >>::Error,
364    >,
365    ContractError: From<
366        <<T as EncodingAdapter>::SummaryEncoder as Encoder<<T as EncodingAdapter>::Summary>>::Error,
367    >,
368    ContractError: From<
369        <<T as EncodingAdapter>::DeltaEncoder as Encoder<<T as EncodingAdapter>::Delta>>::Error,
370    >,
371{
372    let typed_params =
373        <<T as EncodingAdapter>::ParametersEncoder>::deserialize(parameters.as_ref())?;
374    let typed_state = <<T as EncodingAdapter>::SelfEncoder>::deserialize(state.as_ref())?;
375    let typed_summary = <<T as EncodingAdapter>::SummaryEncoder>::deserialize(summary.as_ref())?;
376    let summary = typed_state.delta(typed_params, typed_summary)?;
377    let encoded = <<T as EncodingAdapter>::DeltaEncoder>::serialize(&summary)?;
378    Ok(encoded.into())
379}