freenet_stdlib/contract_interface/
update.rs1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use crate::client_api::{TryFromFbs, WsApiError};
11use crate::common_generated::common::{UpdateData as FbsUpdateData, UpdateDataType};
12use crate::generated::client_request::RelatedContracts as FbsRelatedContracts;
13
14use super::{ContractError, ContractInstanceId, State, StateDelta, CONTRACT_KEY_SIZE};
15
16#[non_exhaustive]
19#[derive(Debug, Serialize, Deserialize)]
20pub struct UpdateModification<'a> {
21 #[serde(borrow)]
22 pub new_state: Option<State<'a>>,
23 pub related: Vec<RelatedContract>,
25}
26
27impl<'a> UpdateModification<'a> {
28 pub fn valid(new_state: State<'a>) -> Self {
30 Self {
31 new_state: Some(new_state),
32 related: vec![],
33 }
34 }
35
36 pub fn unwrap_valid(self) -> State<'a> {
40 match self.new_state {
41 Some(s) => s,
42 _ => panic!("failed unwrapping state in modification"),
43 }
44 }
45}
46
47impl UpdateModification<'_> {
48 pub fn requires(related: Vec<RelatedContract>) -> Result<Self, ContractError> {
51 if related.is_empty() {
52 return Err(ContractError::InvalidUpdateWithInfo {
53 reason: "At least one related contract is required".into(),
54 });
55 }
56 Ok(Self {
57 new_state: None,
58 related,
59 })
60 }
61
62 pub fn get_related(&self) -> &[RelatedContract] {
64 &self.related
65 }
66
67 pub fn into_owned(self) -> UpdateModification<'static> {
69 let Self { new_state, related } = self;
70 UpdateModification {
71 new_state: new_state.map(State::into_owned),
72 related,
73 }
74 }
75
76 pub fn requires_dependencies(&self) -> bool {
77 !self.related.is_empty()
78 }
79}
80
81#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
85pub struct RelatedContracts<'a> {
86 #[serde(borrow)]
87 map: HashMap<ContractInstanceId, Option<State<'a>>>,
88}
89
90impl RelatedContracts<'_> {
91 pub fn new() -> Self {
92 Self {
93 map: HashMap::new(),
94 }
95 }
96
97 pub fn into_owned(self) -> RelatedContracts<'static> {
99 let mut map = HashMap::with_capacity(self.map.len());
100 for (k, v) in self.map {
101 map.insert(k, v.map(|s| s.into_owned()));
102 }
103 RelatedContracts { map }
104 }
105
106 pub fn deser_related_contracts<'de, D>(deser: D) -> Result<RelatedContracts<'static>, D::Error>
107 where
108 D: serde::Deserializer<'de>,
109 {
110 let value = <RelatedContracts as Deserialize>::deserialize(deser)?;
111 Ok(value.into_owned())
112 }
113}
114
115impl RelatedContracts<'static> {
116 pub fn states(&self) -> impl Iterator<Item = (&ContractInstanceId, &Option<State<'static>>)> {
117 self.map.iter()
118 }
119}
120
121impl<'a> RelatedContracts<'a> {
122 pub fn update(
123 &mut self,
124 ) -> impl Iterator<Item = (&ContractInstanceId, &mut Option<State<'a>>)> + '_ {
125 self.map.iter_mut()
126 }
127
128 pub fn missing(&mut self, contracts: Vec<ContractInstanceId>) {
129 for key in contracts {
130 self.map.entry(key).or_default();
131 }
132 }
133}
134
135impl<'a> TryFromFbs<&FbsRelatedContracts<'a>> for RelatedContracts<'a> {
136 fn try_decode_fbs(related_contracts: &FbsRelatedContracts<'a>) -> Result<Self, WsApiError> {
137 let mut map = HashMap::with_capacity(related_contracts.contracts().len());
138 for related in related_contracts.contracts().iter() {
139 let id = ContractInstanceId::from_bytes(related.instance_id().data().bytes()).unwrap();
140 let state = State::from(related.state().bytes());
141 map.insert(id, Some(state));
142 }
143 Ok(RelatedContracts::from(map))
144 }
145}
146
147impl<'a> From<HashMap<ContractInstanceId, Option<State<'a>>>> for RelatedContracts<'a> {
148 fn from(related_contracts: HashMap<ContractInstanceId, Option<State<'a>>>) -> Self {
149 Self {
150 map: related_contracts,
151 }
152 }
153}
154
155#[derive(Debug, Serialize, Deserialize)]
158pub struct RelatedContract {
159 pub contract_instance_id: ContractInstanceId,
160 pub mode: RelatedMode,
161 }
163
164#[derive(Debug, Serialize, Deserialize)]
166pub enum RelatedMode {
167 StateOnce,
169 StateThenSubscribe,
171}
172
173#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
175pub enum ValidateResult {
176 Valid,
177 Invalid,
178 RequestRelated(Vec<ContractInstanceId>),
181}
182
183#[non_exhaustive]
190#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
191pub enum UpdateData<'a> {
192 State(#[serde(borrow)] State<'a>),
193 Delta(#[serde(borrow)] StateDelta<'a>),
194 StateAndDelta {
195 #[serde(borrow)]
196 state: State<'a>,
197 #[serde(borrow)]
198 delta: StateDelta<'a>,
199 },
200 RelatedState {
201 related_to: ContractInstanceId,
202 #[serde(borrow)]
203 state: State<'a>,
204 },
205 RelatedDelta {
206 related_to: ContractInstanceId,
207 #[serde(borrow)]
208 delta: StateDelta<'a>,
209 },
210 RelatedStateAndDelta {
211 related_to: ContractInstanceId,
212 #[serde(borrow)]
213 state: State<'a>,
214 #[serde(borrow)]
215 delta: StateDelta<'a>,
216 },
217}
218
219impl UpdateData<'_> {
220 pub fn size(&self) -> usize {
221 match self {
222 UpdateData::State(state) => state.size(),
223 UpdateData::Delta(delta) => delta.size(),
224 UpdateData::StateAndDelta { state, delta } => state.size() + delta.size(),
225 UpdateData::RelatedState { state, .. } => state.size() + CONTRACT_KEY_SIZE,
226 UpdateData::RelatedDelta { delta, .. } => delta.size() + CONTRACT_KEY_SIZE,
227 UpdateData::RelatedStateAndDelta { state, delta, .. } => {
228 state.size() + delta.size() + CONTRACT_KEY_SIZE
229 }
230 }
231 }
232
233 pub fn unwrap_delta(&self) -> &StateDelta<'_> {
234 match self {
235 UpdateData::Delta(delta) => delta,
236 _ => panic!(),
237 }
238 }
239
240 pub fn into_owned(self) -> UpdateData<'static> {
242 match self {
243 UpdateData::State(s) => UpdateData::State(State::from(s.into_bytes())),
244 UpdateData::Delta(d) => UpdateData::Delta(StateDelta::from(d.into_bytes())),
245 UpdateData::StateAndDelta { state, delta } => UpdateData::StateAndDelta {
246 delta: StateDelta::from(delta.into_bytes()),
247 state: State::from(state.into_bytes()),
248 },
249 UpdateData::RelatedState { related_to, state } => UpdateData::RelatedState {
250 related_to,
251 state: State::from(state.into_bytes()),
252 },
253 UpdateData::RelatedDelta { related_to, delta } => UpdateData::RelatedDelta {
254 related_to,
255 delta: StateDelta::from(delta.into_bytes()),
256 },
257 UpdateData::RelatedStateAndDelta {
258 related_to,
259 state,
260 delta,
261 } => UpdateData::RelatedStateAndDelta {
262 related_to,
263 state: State::from(state.into_bytes()),
264 delta: StateDelta::from(delta.into_bytes()),
265 },
266 }
267 }
268
269 pub(crate) fn get_self_states<'a>(
270 updates: &[UpdateData<'a>],
271 ) -> Vec<(Option<State<'a>>, Option<StateDelta<'a>>)> {
272 let mut own_states = Vec::with_capacity(updates.len());
273 for update in updates {
274 match update {
275 UpdateData::State(state) => own_states.push((Some(state.clone()), None)),
276 UpdateData::Delta(delta) => own_states.push((None, Some(delta.clone()))),
277 UpdateData::StateAndDelta { state, delta } => {
278 own_states.push((Some(state.clone()), Some(delta.clone())))
279 }
280 _ => {}
281 }
282 }
283 own_states
284 }
285
286 pub(crate) fn deser_update_data<'de, D>(deser: D) -> Result<UpdateData<'static>, D::Error>
287 where
288 D: serde::Deserializer<'de>,
289 {
290 let value = <UpdateData as Deserialize>::deserialize(deser)?;
291 Ok(value.into_owned())
292 }
293}
294
295impl<'a> From<StateDelta<'a>> for UpdateData<'a> {
296 fn from(delta: StateDelta<'a>) -> Self {
297 UpdateData::Delta(delta)
298 }
299}
300
301impl<'a> TryFromFbs<&FbsUpdateData<'a>> for UpdateData<'a> {
302 fn try_decode_fbs(update_data: &FbsUpdateData<'a>) -> Result<Self, WsApiError> {
303 match update_data.update_data_type() {
304 UpdateDataType::StateUpdate => {
305 let update = update_data.update_data_as_state_update().unwrap();
306 let state = State::from(update.state().bytes());
307 Ok(UpdateData::State(state))
308 }
309 UpdateDataType::DeltaUpdate => {
310 let update = update_data.update_data_as_delta_update().unwrap();
311 let delta = StateDelta::from(update.delta().bytes());
312 Ok(UpdateData::Delta(delta))
313 }
314 UpdateDataType::StateAndDeltaUpdate => {
315 let update = update_data.update_data_as_state_and_delta_update().unwrap();
316 let state = State::from(update.state().bytes());
317 let delta = StateDelta::from(update.delta().bytes());
318 Ok(UpdateData::StateAndDelta { state, delta })
319 }
320 UpdateDataType::RelatedStateUpdate => {
321 let update = update_data.update_data_as_related_state_update().unwrap();
322 let state = State::from(update.state().bytes());
323 let related_to =
324 ContractInstanceId::from_bytes(update.related_to().data().bytes()).unwrap();
325 Ok(UpdateData::RelatedState { related_to, state })
326 }
327 UpdateDataType::RelatedDeltaUpdate => {
328 let update = update_data.update_data_as_related_delta_update().unwrap();
329 let delta = StateDelta::from(update.delta().bytes());
330 let related_to =
331 ContractInstanceId::from_bytes(update.related_to().data().bytes()).unwrap();
332 Ok(UpdateData::RelatedDelta { related_to, delta })
333 }
334 UpdateDataType::RelatedStateAndDeltaUpdate => {
335 let update = update_data
336 .update_data_as_related_state_and_delta_update()
337 .unwrap();
338 let state = State::from(update.state().bytes());
339 let delta = StateDelta::from(update.delta().bytes());
340 let related_to =
341 ContractInstanceId::from_bytes(update.related_to().data().bytes()).unwrap();
342 Ok(UpdateData::RelatedStateAndDelta {
343 related_to,
344 state,
345 delta,
346 })
347 }
348 _ => unreachable!(),
349 }
350 }
351}
352
353#[cfg(test)]
354mod update_data_wire_format_tests {
355 use super::*;
356
357 #[test]
366 fn update_data_wire_format_is_stable() {
367 let state = UpdateData::State(State::from(vec![0xAA, 0xBB]));
368 let state_bytes = bincode::serialize(&state).unwrap();
369 assert_eq!(
370 state_bytes[..4],
371 [0, 0, 0, 0],
372 "UpdateData::State must stay at variant tag 0"
373 );
374
375 let delta = UpdateData::Delta(StateDelta::from(vec![0xCC, 0xDD]));
376 let delta_bytes = bincode::serialize(&delta).unwrap();
377 assert_eq!(
378 delta_bytes[..4],
379 [1, 0, 0, 0],
380 "UpdateData::Delta must stay at variant tag 1"
381 );
382
383 let decoded_state: UpdateData<'_> = bincode::deserialize(&state_bytes).unwrap();
385 assert!(matches!(decoded_state, UpdateData::State(_)));
386 let decoded_delta: UpdateData<'_> = bincode::deserialize(&delta_bytes).unwrap();
387 assert!(matches!(decoded_delta, UpdateData::Delta(_)));
388 }
389}