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