Skip to main content

dpp/identity/
identity_nonce.rs

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)]
34/// The result of the merge of the identity contract nonce
35pub enum MergeIdentityNonceResult {
36    /// The nonce is an invalid value
37    /// This could be 0
38    InvalidNonce,
39    /// The nonce is too far in the future
40    NonceTooFarInFuture,
41    /// The nonce is too far in the past
42    NonceTooFarInPast,
43    /// The nonce is already present at the tip
44    NonceAlreadyPresentAtTip,
45    /// The nonce is already present in the past
46    NonceAlreadyPresentInPast(u64),
47    /// The merge is a success
48    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    /// Gives a result from the enum
59    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    /// Is this result an error?
75    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        // we are too far away from the actual revision
86        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            // we were not able to update the revision as it is the same as we already had
108            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                // we are too far away from the actual revision
121                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                // we are too far away from the actual revision
137                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}