Skip to main content

freenet_stdlib/contract_interface/
trait_def.rs

1//! Contract interface trait definition.
2//!
3//! This module defines the `ContractInterface` trait which all contracts must implement.
4
5use crate::parameters::Parameters;
6
7use super::{
8    ContractError, RelatedContracts, State, StateDelta, StateSummary, UpdateData,
9    UpdateModification, ValidateResult,
10};
11
12/// Trait to implement for the contract building.
13///
14/// Contains all necessary methods to interact with the contract.
15///
16/// # Examples
17///
18/// Implementing `ContractInterface` on a type:
19///
20/// ```
21/// # use freenet_stdlib::prelude::*;
22/// struct Contract;
23///
24/// #[contract]
25/// impl ContractInterface for Contract {
26///     fn validate_state(
27///         _parameters: Parameters<'static>,
28///         _state: State<'static>,
29///         _related: RelatedContracts
30///     ) -> Result<ValidateResult, ContractError> {
31///         Ok(ValidateResult::Valid)
32///     }
33///
34///     fn update_state(
35///         _parameters: Parameters<'static>,
36///         state: State<'static>,
37///         _data: Vec<UpdateData>,
38///     ) -> Result<UpdateModification<'static>, ContractError> {
39///         Ok(UpdateModification::valid(state))
40///     }
41///
42///     fn summarize_state(
43///         _parameters: Parameters<'static>,
44///         _state: State<'static>,
45///     ) -> Result<StateSummary<'static>, ContractError> {
46///         Ok(StateSummary::from(vec![]))
47///     }
48///
49///     fn get_state_delta(
50///         _parameters: Parameters<'static>,
51///         _state: State<'static>,
52///         _summary: StateSummary<'static>,
53///     ) -> Result<StateDelta<'static>, ContractError> {
54///         Ok(StateDelta::from(vec![]))
55///     }
56/// }
57/// ```
58// ANCHOR: contractifce
59/// # ContractInterface
60///
61/// This trait defines the core functionality for managing and updating a contract's state.
62/// Implementations must ensure that the state merge operation is *associative*, *commutative*,
63/// and *idempotent* — i.e. state forms an idempotent commutative monoid (a join-semilattice),
64/// the same algebraic structure used by state-based CRDTs. In other words, when applying
65/// multiple delta updates to a state, the order in which these updates are applied should not
66/// affect the final state, and applying the same update more than once must not change the
67/// result beyond its first application. Once all deltas are applied, the resulting state
68/// should be the same, regardless of the order or repetition of application.
69///
70/// Implementations must also keep the delta negligible when the requesting peer's summary
71/// shows it already holds this state: the delta must not contain that state, or approach
72/// its size. See [`Self::get_state_delta`].
73///
74/// Noncompliant behavior, such as failing to obey the associativity, commutativity, or
75/// idempotence rules, or returning a state-sized delta to a peer that is already up to date,
76/// may result in the contract being deprioritized or removed from the p2p network.
77pub trait ContractInterface {
78    /// Verify that the state is valid, given the parameters.
79    fn validate_state(
80        parameters: Parameters<'static>,
81        state: State<'static>,
82        related: RelatedContracts<'static>,
83    ) -> Result<ValidateResult, ContractError>;
84
85    /// Update the state to account for the new data
86    fn update_state(
87        parameters: Parameters<'static>,
88        state: State<'static>,
89        data: Vec<UpdateData<'static>>,
90    ) -> Result<UpdateModification<'static>, ContractError>;
91
92    /// Generate a concise summary of a state that can be used to create deltas
93    /// relative to this state.
94    ///
95    /// The summary must be much smaller than the state it summarizes. A summary whose
96    /// size is comparable to the state defeats delta computation, and a summary that is
97    /// a copy of the state is always a bug. See [`Self::get_state_delta`] for the
98    /// delta-size requirement this summary feeds into.
99    fn summarize_state(
100        parameters: Parameters<'static>,
101        state: State<'static>,
102    ) -> Result<StateSummary<'static>, ContractError>;
103
104    /// Generate a state delta using a summary from the current state.
105    /// This along with [`Self::summarize_state`] allows flexible and efficient
106    /// state synchronization between peers.
107    ///
108    /// # The delta to an up-to-date peer must be negligible
109    ///
110    /// When `summary` shows that the requesting peer already holds everything this state
111    /// has, the delta carries no information, and its size has to reflect that.
112    ///
113    /// - **MUST NOT** return a delta that contains the state, or whose size approaches the
114    ///   state's. This is the actual defect. A `get_state_delta` that ignores `summary`
115    ///   and returns the whole state makes every reconciliation re-ship data the peer
116    ///   already holds, forever, and is the behavior that may get a contract
117    ///   deprioritized or removed from the network.
118    /// - **SHOULD** return a literally empty delta, `StateDelta::from(vec![])`. Peers read
119    ///   zero bytes as an unambiguous "converged" and skip the broadcast, so this is the
120    ///   cheapest and clearest answer.
121    /// - **Acceptable**: a small fixed amount of encoding framing. Serializing a delta
122    ///   struct whose fields are all `None` or empty costs about a byte per field with
123    ///   bincode, and tens of bytes with CBOR, since ciborium writes field names. That
124    ///   is not the unambiguous converged signal, so prefer zero, but it is not a defect
125    ///   and carries no penalty.
126    ///
127    /// What matters is delta size relative to state size, not the exact byte count. Twenty
128    /// bytes against a 500 KB state is fine. A state-sized delta is not.
129    ///
130    /// Building state with `freenet-scaffold` gets the empty case for free: its
131    /// `#[composable]` derive collapses an all-`None` delta struct to `None`, which the
132    /// contract maps to `StateDelta::from(vec![])`. A hand-rolled `get_state_delta` has
133    /// no such collapse and must add the check itself if it wants zero bytes.
134    fn get_state_delta(
135        parameters: Parameters<'static>,
136        state: State<'static>,
137        summary: StateSummary<'static>,
138    ) -> Result<StateDelta<'static>, ContractError>;
139}
140// ANCHOR_END: contractifce