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