freenet_git_repo_contract/
lib.rs1#![deny(unsafe_code)]
17
18use freenet_git_types as types;
19use freenet_git_types::{RepoParams, RepoState, RepoSummary};
20use freenet_stdlib::prelude::*;
21
22pub struct Contract;
26
27#[contract]
28impl ContractInterface for Contract {
29 fn validate_state(
30 parameters: Parameters<'static>,
31 state: State<'static>,
32 _related: RelatedContracts<'static>,
33 ) -> Result<ValidateResult, ContractError> {
34 let params = decode_params(¶meters)?;
35 let state = decode_state(&state)?;
36 match types::validate_state(¶ms, &state) {
37 Ok(()) => Ok(ValidateResult::Valid),
38 Err(e) => Err(ContractError::InvalidUpdateWithInfo {
39 reason: format!("validate_state: {e}"),
40 }),
41 }
42 }
43
44 fn update_state(
45 parameters: Parameters<'static>,
46 state: State<'static>,
47 data: Vec<UpdateData<'static>>,
48 ) -> Result<UpdateModification<'static>, ContractError> {
49 let params = decode_params(¶meters)?;
50 let mut current = decode_state(&state)?;
51
52 for update in data {
53 match update {
54 UpdateData::State(new_state_bytes) => {
55 let incoming = decode_state(new_state_bytes.as_ref())?;
56 current = types::update_state(¶ms, ¤t, &incoming).map_err(|e| {
57 ContractError::InvalidUpdateWithInfo {
58 reason: format!("update_state (full state): {e}"),
59 }
60 })?;
61 }
62 UpdateData::Delta(delta_bytes) => {
63 if delta_bytes.as_ref().is_empty() {
64 continue;
65 }
66 let delta = decode_state(delta_bytes.as_ref())?;
67 current = types::update_state(¶ms, ¤t, &delta).map_err(|e| {
68 ContractError::InvalidUpdateWithInfo {
69 reason: format!("update_state (delta): {e}"),
70 }
71 })?;
72 }
73 UpdateData::StateAndDelta { state, delta } => {
74 let incoming_state = decode_state(state.as_ref())?;
75 current =
76 types::update_state(¶ms, ¤t, &incoming_state).map_err(|e| {
77 ContractError::InvalidUpdateWithInfo {
78 reason: format!("update_state (state+delta state half): {e}"),
79 }
80 })?;
81 if !delta.as_ref().is_empty() {
82 let incoming_delta = decode_state(delta.as_ref())?;
83 current = types::update_state(¶ms, ¤t, &incoming_delta).map_err(
84 |e| ContractError::InvalidUpdateWithInfo {
85 reason: format!("update_state (state+delta delta half): {e}"),
86 },
87 )?;
88 }
89 }
90 UpdateData::RelatedState { .. }
91 | UpdateData::RelatedDelta { .. }
92 | UpdateData::RelatedStateAndDelta { .. } => {
93 return Err(ContractError::InvalidUpdate);
97 }
98 _ => {
99 return Err(ContractError::InvalidUpdate);
100 }
101 }
102 }
103
104 let bytes =
105 bincode::serialize(¤t).map_err(|e| ContractError::InvalidUpdateWithInfo {
106 reason: format!("re-serialize updated state: {e}"),
107 })?;
108 Ok(UpdateModification::valid(bytes.into()))
109 }
110
111 fn summarize_state(
112 parameters: Parameters<'static>,
113 state: State<'static>,
114 ) -> Result<StateSummary<'static>, ContractError> {
115 let _params = decode_params(¶meters)?;
116 let state = decode_state(&state)?;
117 let summary = types::summarize_state(&state);
118 let bytes =
119 bincode::serialize(&summary).map_err(|e| ContractError::InvalidUpdateWithInfo {
120 reason: format!("serialize summary: {e}"),
121 })?;
122 Ok(StateSummary::from(bytes))
123 }
124
125 fn get_state_delta(
126 parameters: Parameters<'static>,
127 state: State<'static>,
128 summary: StateSummary<'static>,
129 ) -> Result<StateDelta<'static>, ContractError> {
130 let _params = decode_params(¶meters)?;
131 let state = decode_state(&state)?;
132 let summary = decode_summary(&summary)?;
133 let delta = types::get_state_delta(&state, &summary);
134 if is_empty_delta(&delta) {
138 return Ok(StateDelta::from(Vec::new()));
139 }
140 let bytes =
141 bincode::serialize(&delta).map_err(|e| ContractError::InvalidUpdateWithInfo {
142 reason: format!("serialize delta: {e}"),
143 })?;
144 Ok(StateDelta::from(bytes))
145 }
146}
147
148fn decode_params(p: &Parameters<'_>) -> Result<RepoParams, ContractError> {
149 RepoParams::from_bytes(p.as_ref()).map_err(|e| ContractError::InvalidUpdateWithInfo {
150 reason: format!("decode params: {e}"),
151 })
152}
153
154fn decode_state(bytes: &[u8]) -> Result<RepoState, ContractError> {
155 RepoState::from_bytes(bytes).map_err(|e| ContractError::InvalidUpdateWithInfo {
156 reason: format!("decode state: {e}"),
157 })
158}
159
160fn decode_summary(s: &StateSummary<'_>) -> Result<RepoSummary, ContractError> {
161 let bytes = s.as_ref();
162 if bytes.is_empty() {
163 return Ok(RepoSummary::default());
164 }
165 bincode::deserialize::<RepoSummary>(bytes).map_err(|e| ContractError::InvalidUpdateWithInfo {
166 reason: format!("decode summary: {e}"),
167 })
168}
169
170fn is_empty_delta(s: &RepoState) -> bool {
171 s.name.is_none()
172 && s.description.is_none()
173 && s.default_branch.is_none()
174 && s.force_push_allowed.is_none()
175 && s.acl.is_none()
176 && s.upgrade.is_none()
177 && s.refs.is_empty()
178 && s.object_index.is_empty()
179 && s.extensions.is_empty()
180 }
185
186#[allow(dead_code)]
190fn _force_link_merge_state() -> RepoState {
191 let a = RepoState::default();
192 let b = RepoState::default();
193 types::merge_state(&a, &b)
194}