1use crate::ProtocolError;
2use platform_serialization_derive::{
3 PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
4};
5use std::fmt::{Debug, Display, Formatter};
6
7use crate::consensus::state::identity::invalid_identity_contract_nonce_error::InvalidIdentityNonceError;
8use crate::consensus::state::state_error::StateError;
9use crate::consensus::ConsensusError;
10use crate::prelude::IdentityNonce;
11use crate::validation::SimpleConsensusValidationResult;
12use bincode::{Decode, DecodeUntrusted, Encode};
13use platform_value::Identifier;
14
15pub const IDENTITY_NONCE_VALUE_FILTER: u64 = 0xFFFFFFFFFF;
16pub const MISSING_IDENTITY_REVISIONS_FILTER: u64 = 0xFFFFFF0000000000;
17pub const MAX_MISSING_IDENTITY_REVISIONS: u64 = 24;
18pub const MISSING_IDENTITY_REVISIONS_MAX_BYTES: u64 = MAX_MISSING_IDENTITY_REVISIONS;
19pub const IDENTITY_NONCE_VALUE_FILTER_MAX_BYTES: u64 = 40;
20
21#[derive(
22 Debug,
23 Clone,
24 Copy,
25 PartialEq,
26 Eq,
27 Encode,
28 Decode,
29 PlatformSerialize,
30 PlatformDeserializeTrusted,
31 PlatformDeserializeUntrusted,
32 DecodeUntrusted,
33)]
34pub enum MergeIdentityNonceResult {
36 InvalidNonce,
39 NonceTooFarInFuture,
41 NonceTooFarInPast,
43 NonceAlreadyPresentAtTip,
45 NonceAlreadyPresentInPast(u64),
47 MergeIdentityNonceSuccess(IdentityNonce),
49}
50
51impl Display for MergeIdentityNonceResult {
52 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53 f.write_str(self.error_message().unwrap_or("no error"))
54 }
55}
56
57impl MergeIdentityNonceResult {
58 pub fn error_message(&self) -> Option<&'static str> {
60 match self {
61 MergeIdentityNonceResult::NonceTooFarInFuture => Some("nonce too far in future"),
62 MergeIdentityNonceResult::NonceTooFarInPast => Some("nonce too far in past"),
63 MergeIdentityNonceResult::NonceAlreadyPresentAtTip => {
64 Some("nonce already present at tip")
65 }
66 MergeIdentityNonceResult::NonceAlreadyPresentInPast(_) => {
67 Some("nonce already present in past")
68 }
69 MergeIdentityNonceResult::MergeIdentityNonceSuccess(_) => None,
70 MergeIdentityNonceResult::InvalidNonce => Some("nonce is an invalid value"),
71 }
72 }
73
74 pub fn is_error(&self) -> bool {
76 !matches!(self, MergeIdentityNonceResult::MergeIdentityNonceSuccess(_))
77 }
78}
79
80pub fn validate_new_identity_nonce(
81 new_revision_nonce: IdentityNonce,
82 identity_id: Identifier,
83) -> SimpleConsensusValidationResult {
84 if new_revision_nonce >= MISSING_IDENTITY_REVISIONS_MAX_BYTES {
85 SimpleConsensusValidationResult::new_with_error(ConsensusError::StateError(
87 StateError::InvalidIdentityNonceError(InvalidIdentityNonceError {
88 identity_id,
89 current_identity_nonce: None,
90 setting_identity_nonce: new_revision_nonce,
91 error: MergeIdentityNonceResult::NonceTooFarInPast,
92 }),
93 ))
94 } else {
95 SimpleConsensusValidationResult::new()
96 }
97}
98
99pub fn validate_identity_nonce_update(
100 existing_nonce: IdentityNonce,
101 new_revision_nonce: IdentityNonce,
102 identity_id: Identifier,
103) -> SimpleConsensusValidationResult {
104 let actual_existing_revision = existing_nonce & IDENTITY_NONCE_VALUE_FILTER;
105 match actual_existing_revision.cmp(&new_revision_nonce) {
106 std::cmp::Ordering::Equal => {
107 return SimpleConsensusValidationResult::new_with_error(ConsensusError::StateError(
109 StateError::InvalidIdentityNonceError(InvalidIdentityNonceError {
110 identity_id,
111 current_identity_nonce: Some(existing_nonce),
112 setting_identity_nonce: new_revision_nonce,
113 error: MergeIdentityNonceResult::NonceAlreadyPresentAtTip,
114 }),
115 ));
116 }
117 std::cmp::Ordering::Less => {
118 if new_revision_nonce - actual_existing_revision > MISSING_IDENTITY_REVISIONS_MAX_BYTES
119 {
120 return SimpleConsensusValidationResult::new_with_error(
122 ConsensusError::StateError(StateError::InvalidIdentityNonceError(
123 InvalidIdentityNonceError {
124 identity_id,
125 current_identity_nonce: Some(existing_nonce),
126 setting_identity_nonce: new_revision_nonce,
127 error: MergeIdentityNonceResult::NonceTooFarInFuture,
128 },
129 )),
130 );
131 }
132 }
133 std::cmp::Ordering::Greater => {
134 let previous_revision_position_from_top = actual_existing_revision - new_revision_nonce;
135 if previous_revision_position_from_top > MISSING_IDENTITY_REVISIONS_MAX_BYTES {
136 return SimpleConsensusValidationResult::new_with_error(
138 ConsensusError::StateError(StateError::InvalidIdentityNonceError(
139 InvalidIdentityNonceError {
140 identity_id,
141 current_identity_nonce: Some(existing_nonce),
142 setting_identity_nonce: new_revision_nonce,
143 error: MergeIdentityNonceResult::NonceTooFarInPast,
144 },
145 )),
146 );
147 } else {
148 let old_missing_revisions = existing_nonce & MISSING_IDENTITY_REVISIONS_FILTER;
149 let old_revision_already_set = if old_missing_revisions == 0 {
150 true
151 } else {
152 let byte_to_unset = 1
153 << (previous_revision_position_from_top - 1
154 + IDENTITY_NONCE_VALUE_FILTER_MAX_BYTES);
155 old_missing_revisions | byte_to_unset != old_missing_revisions
156 };
157
158 if old_revision_already_set {
159 return SimpleConsensusValidationResult::new_with_error(
160 ConsensusError::StateError(StateError::InvalidIdentityNonceError(
161 InvalidIdentityNonceError {
162 identity_id,
163 current_identity_nonce: Some(existing_nonce),
164 setting_identity_nonce: new_revision_nonce,
165 error: MergeIdentityNonceResult::NonceAlreadyPresentInPast(
166 previous_revision_position_from_top,
167 ),
168 },
169 )),
170 );
171 }
172 }
173 }
174 }
175 SimpleConsensusValidationResult::new()
176}
177
178#[cfg(test)]
179mod tests {
180 use crate::consensus::state::state_error::StateError;
181 use crate::consensus::ConsensusError;
182 use crate::identity::identity_nonce::{
183 validate_identity_nonce_update, validate_new_identity_nonce, MergeIdentityNonceResult,
184 MISSING_IDENTITY_REVISIONS_MAX_BYTES,
185 };
186 use platform_value::Identifier;
187
188 #[test]
189 fn validate_new_identity_nonce_valid_zero() {
190 let result = validate_new_identity_nonce(0, Identifier::default());
191 assert!(result.errors.is_empty());
192 }
193
194 #[test]
195 fn validate_new_identity_nonce_invalid_at_max() {
196 let nonce = MISSING_IDENTITY_REVISIONS_MAX_BYTES;
197 let result = validate_new_identity_nonce(nonce, Identifier::default());
198
199 let Some(ConsensusError::StateError(StateError::InvalidIdentityNonceError(e))) =
200 result.errors.first()
201 else {
202 panic!("expected state error");
203 };
204 assert_eq!(e.error, MergeIdentityNonceResult::NonceTooFarInPast);
205 }
206
207 #[test]
208 fn validate_identity_nonce_not_changed() {
209 let tip = 50;
210 let new_nonce = tip;
211 let identity_id = Identifier::default();
212 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
213
214 let Some(ConsensusError::StateError(StateError::InvalidIdentityNonceError(e))) =
215 result.errors.first()
216 else {
217 panic!("expected state error");
218 };
219 assert_eq!(e.error, MergeIdentityNonceResult::NonceAlreadyPresentAtTip);
220 }
221
222 #[test]
223 fn validate_identity_nonce_update_too_far_in_past() {
224 let tip = 50;
225 let new_nonce = tip - 25;
226 let identity_id = Identifier::default();
227 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
228
229 let Some(ConsensusError::StateError(StateError::InvalidIdentityNonceError(e))) =
230 result.errors.first()
231 else {
232 panic!("expected state error");
233 };
234 assert_eq!(e.error, MergeIdentityNonceResult::NonceTooFarInPast);
235 }
236
237 #[test]
238 fn validate_identity_nonce_update_too_far_in_future() {
239 let tip = 50;
240 let new_nonce = tip + 25;
241 let identity_id = Identifier::default();
242 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
243
244 let Some(ConsensusError::StateError(StateError::InvalidIdentityNonceError(e))) =
245 result.errors.first()
246 else {
247 panic!("expected state error");
248 };
249 assert_eq!(e.error, MergeIdentityNonceResult::NonceTooFarInFuture);
250 }
251
252 #[test]
253 fn validate_identity_nonce_update_already_in_past_no_missing_in_nonce() {
254 let tip = 50;
255 let new_nonce = tip - 24;
256 let identity_id = Identifier::default();
257 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
258
259 let Some(ConsensusError::StateError(StateError::InvalidIdentityNonceError(e))) =
260 result.errors.first()
261 else {
262 panic!("expected state error");
263 };
264 assert_eq!(
265 e.error,
266 MergeIdentityNonceResult::NonceAlreadyPresentInPast(24)
267 );
268 }
269
270 #[test]
271 fn validate_identity_nonce_update_already_in_past_some_missing_in_nonce() {
272 let tip = 50 | 0x0FFF000000000000;
273 let new_nonce = 50 - 24;
274 let identity_id = Identifier::default();
275 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
276
277 let Some(ConsensusError::StateError(StateError::InvalidIdentityNonceError(e))) =
278 result.errors.first()
279 else {
280 panic!("expected state error");
281 };
282 assert_eq!(
283 e.error,
284 MergeIdentityNonceResult::NonceAlreadyPresentInPast(24)
285 );
286 }
287
288 #[test]
289 fn validate_identity_nonce_update_not_in_past_some_missing_in_nonce() {
290 let tip = 50 | 0x0FFF000000000000;
291 let new_nonce = 50 - 20;
292 let identity_id = Identifier::default();
293 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
294
295 assert!(result.errors.is_empty())
296 }
297
298 #[test]
299 fn validate_identity_nonce_in_close_future() {
300 let tip = 50 | 0x0FFF000000000000;
301 let new_nonce = 50 + 24;
302 let identity_id = Identifier::default();
303 let result = validate_identity_nonce_update(tip, new_nonce, identity_id);
304
305 assert!(result.errors.is_empty())
306 }
307}