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