casper_types/
account.rs

1//! Contains types and constants associated with user accounts.
2
3mod account_hash;
4pub mod action_thresholds;
5mod action_type;
6pub mod associated_keys;
7mod error;
8mod weight;
9
10use serde::Serialize;
11
12use alloc::{collections::BTreeSet, vec::Vec};
13use core::{
14    convert::TryFrom,
15    fmt::{self, Debug, Display, Formatter},
16    iter,
17};
18
19#[cfg(feature = "datasize")]
20use datasize::DataSize;
21
22pub use self::{
23    account_hash::{AccountHash, ACCOUNT_HASH_FORMATTED_STRING_PREFIX, ACCOUNT_HASH_LENGTH},
24    action_thresholds::ActionThresholds,
25    action_type::ActionType,
26    associated_keys::AssociatedKeys,
27    error::{FromStrError, SetThresholdFailure, TryFromIntError, TryFromSliceForAccountHashError},
28    weight::{Weight, WEIGHT_SERIALIZED_LENGTH},
29};
30use crate::{
31    bytesrepr::{self, FromBytes, ToBytes},
32    contracts::NamedKeys,
33    crypto, AccessRights, ContextAccessRights, Key, URef, BLAKE2B_DIGEST_LENGTH,
34};
35
36/// Represents an Account in the global state.
37#[derive(PartialEq, Eq, Clone, Debug, Serialize)]
38#[cfg_attr(feature = "datasize", derive(DataSize))]
39pub struct Account {
40    account_hash: AccountHash,
41    named_keys: NamedKeys,
42    main_purse: URef,
43    associated_keys: AssociatedKeys,
44    action_thresholds: ActionThresholds,
45}
46
47impl Account {
48    /// Creates a new account.
49    pub fn new(
50        account_hash: AccountHash,
51        named_keys: NamedKeys,
52        main_purse: URef,
53        associated_keys: AssociatedKeys,
54        action_thresholds: ActionThresholds,
55    ) -> Self {
56        Account {
57            account_hash,
58            named_keys,
59            main_purse,
60            associated_keys,
61            action_thresholds,
62        }
63    }
64
65    /// An Account constructor with presets for associated_keys and action_thresholds.
66    ///
67    /// An account created with this method is valid and can be used as the target of a transaction.
68    /// It will be created with an [`AssociatedKeys`] with a [`Weight`] of 1, and a default
69    /// [`ActionThresholds`].
70    pub fn create(account: AccountHash, named_keys: NamedKeys, main_purse: URef) -> Self {
71        let associated_keys = AssociatedKeys::new(account, Weight::new(1));
72
73        let action_thresholds: ActionThresholds = Default::default();
74        Account::new(
75            account,
76            named_keys,
77            main_purse,
78            associated_keys,
79            action_thresholds,
80        )
81    }
82
83    /// Extracts the access rights from the named keys and main purse of the account.
84    pub fn extract_access_rights(&self) -> ContextAccessRights {
85        let urefs_iter = self
86            .named_keys
87            .values()
88            .filter_map(|key| key.as_uref().copied())
89            .chain(iter::once(self.main_purse));
90        ContextAccessRights::new(Key::from(self.account_hash), urefs_iter)
91    }
92
93    /// Appends named keys to an account's named_keys field.
94    pub fn named_keys_append(&mut self, keys: &mut NamedKeys) {
95        self.named_keys.append(keys);
96    }
97
98    /// Returns named keys.
99    pub fn named_keys(&self) -> &NamedKeys {
100        &self.named_keys
101    }
102
103    /// Returns a mutable reference to named keys.
104    pub fn named_keys_mut(&mut self) -> &mut NamedKeys {
105        &mut self.named_keys
106    }
107
108    /// Returns account hash.
109    pub fn account_hash(&self) -> AccountHash {
110        self.account_hash
111    }
112
113    /// Returns main purse.
114    pub fn main_purse(&self) -> URef {
115        self.main_purse
116    }
117
118    /// Returns an [`AccessRights::ADD`]-only version of the main purse's [`URef`].
119    pub fn main_purse_add_only(&self) -> URef {
120        URef::new(self.main_purse.addr(), AccessRights::ADD)
121    }
122
123    /// Returns associated keys.
124    pub fn associated_keys(&self) -> &AssociatedKeys {
125        &self.associated_keys
126    }
127
128    /// Returns action thresholds.
129    pub fn action_thresholds(&self) -> &ActionThresholds {
130        &self.action_thresholds
131    }
132
133    /// Adds an associated key to an account.
134    pub fn add_associated_key(
135        &mut self,
136        account_hash: AccountHash,
137        weight: Weight,
138    ) -> Result<(), AddKeyFailure> {
139        self.associated_keys.add_key(account_hash, weight)
140    }
141
142    /// Checks if removing given key would properly satisfy thresholds.
143    fn can_remove_key(&self, account_hash: AccountHash) -> bool {
144        let total_weight_without = self
145            .associated_keys
146            .total_keys_weight_excluding(account_hash);
147
148        // Returns true if the total weight calculated without given public key would be greater or
149        // equal to all of the thresholds.
150        total_weight_without >= *self.action_thresholds().deployment()
151            && total_weight_without >= *self.action_thresholds().key_management()
152    }
153
154    /// Checks if adding a weight to a sum of all weights excluding the given key would make the
155    /// resulting value to fall below any of the thresholds on account.
156    fn can_update_key(&self, account_hash: AccountHash, weight: Weight) -> bool {
157        // Calculates total weight of all keys excluding the given key
158        let total_weight = self
159            .associated_keys
160            .total_keys_weight_excluding(account_hash);
161
162        // Safely calculate new weight by adding the updated weight
163        let new_weight = total_weight.value().saturating_add(weight.value());
164
165        // Returns true if the new weight would be greater or equal to all of
166        // the thresholds.
167        new_weight >= self.action_thresholds().deployment().value()
168            && new_weight >= self.action_thresholds().key_management().value()
169    }
170
171    /// Removes an associated key from an account.
172    ///
173    /// Verifies that removing the key will not cause the remaining weight to fall below any action
174    /// thresholds.
175    pub fn remove_associated_key(
176        &mut self,
177        account_hash: AccountHash,
178    ) -> Result<(), RemoveKeyFailure> {
179        if self.associated_keys.contains_key(&account_hash) {
180            // Check if removing this weight would fall below thresholds
181            if !self.can_remove_key(account_hash) {
182                return Err(RemoveKeyFailure::ThresholdViolation);
183            }
184        }
185        self.associated_keys.remove_key(&account_hash)
186    }
187
188    /// Updates an associated key.
189    ///
190    /// Returns an error if the update would result in a violation of the key management thresholds.
191    pub fn update_associated_key(
192        &mut self,
193        account_hash: AccountHash,
194        weight: Weight,
195    ) -> Result<(), UpdateKeyFailure> {
196        if let Some(current_weight) = self.associated_keys.get(&account_hash) {
197            if weight < *current_weight {
198                // New weight is smaller than current weight
199                if !self.can_update_key(account_hash, weight) {
200                    return Err(UpdateKeyFailure::ThresholdViolation);
201                }
202            }
203        }
204        self.associated_keys.update_key(account_hash, weight)
205    }
206
207    /// Sets a new action threshold for a given action type for the account.
208    ///
209    /// Returns an error if the new action threshold weight is greater than the total weight of the
210    /// account's associated keys.
211    pub fn set_action_threshold(
212        &mut self,
213        action_type: ActionType,
214        weight: Weight,
215    ) -> Result<(), SetThresholdFailure> {
216        // Verify if new threshold weight exceeds total weight of all associated
217        // keys.
218        self.can_set_threshold(weight)?;
219        // Set new weight for given action
220        self.action_thresholds.set_threshold(action_type, weight)
221    }
222
223    /// Verifies if user can set action threshold.
224    pub fn can_set_threshold(&self, new_threshold: Weight) -> Result<(), SetThresholdFailure> {
225        let total_weight = self.associated_keys.total_keys_weight();
226        if new_threshold > total_weight {
227            return Err(SetThresholdFailure::InsufficientTotalWeight);
228        }
229        Ok(())
230    }
231
232    /// Sets a new action threshold for a given action type for the account without checking against
233    /// the total weight of the associated keys.
234    ///
235    /// This should only be called when authorized by an administrator account.
236    ///
237    /// Returns an error if setting the action would cause the `ActionType::Deployment` threshold to
238    /// be greater than any of the other action types.
239    pub fn set_action_threshold_unchecked(
240        &mut self,
241        action_type: ActionType,
242        threshold: Weight,
243    ) -> Result<(), SetThresholdFailure> {
244        self.action_thresholds.set_threshold(action_type, threshold)
245    }
246
247    /// Checks whether all authorization keys are associated with this account.
248    pub fn can_authorize(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
249        !authorization_keys.is_empty()
250            && authorization_keys
251                .iter()
252                .all(|e| self.associated_keys.contains_key(e))
253    }
254
255    /// Checks whether the sum of the weights of all authorization keys is
256    /// greater or equal to deploy threshold.
257    pub fn can_deploy_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
258        let total_weight = self
259            .associated_keys
260            .calculate_keys_weight(authorization_keys);
261
262        total_weight >= *self.action_thresholds().deployment()
263    }
264
265    /// Checks whether the sum of the weights of all authorization keys is
266    /// greater or equal to key management threshold.
267    pub fn can_manage_keys_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
268        let total_weight = self
269            .associated_keys
270            .calculate_keys_weight(authorization_keys);
271
272        total_weight >= *self.action_thresholds().key_management()
273    }
274}
275
276impl ToBytes for Account {
277    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
278        let mut result = bytesrepr::allocate_buffer(self)?;
279        self.account_hash().write_bytes(&mut result)?;
280        self.named_keys().write_bytes(&mut result)?;
281        self.main_purse.write_bytes(&mut result)?;
282        self.associated_keys().write_bytes(&mut result)?;
283        self.action_thresholds().write_bytes(&mut result)?;
284        Ok(result)
285    }
286
287    fn serialized_length(&self) -> usize {
288        self.account_hash.serialized_length()
289            + self.named_keys.serialized_length()
290            + self.main_purse.serialized_length()
291            + self.associated_keys.serialized_length()
292            + self.action_thresholds.serialized_length()
293    }
294
295    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
296        self.account_hash().write_bytes(writer)?;
297        self.named_keys().write_bytes(writer)?;
298        self.main_purse().write_bytes(writer)?;
299        self.associated_keys().write_bytes(writer)?;
300        self.action_thresholds().write_bytes(writer)?;
301        Ok(())
302    }
303}
304
305impl FromBytes for Account {
306    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
307        let (account_hash, rem) = AccountHash::from_bytes(bytes)?;
308        let (named_keys, rem) = NamedKeys::from_bytes(rem)?;
309        let (main_purse, rem) = URef::from_bytes(rem)?;
310        let (associated_keys, rem) = AssociatedKeys::from_bytes(rem)?;
311        let (action_thresholds, rem) = ActionThresholds::from_bytes(rem)?;
312        Ok((
313            Account {
314                account_hash,
315                named_keys,
316                main_purse,
317                associated_keys,
318                action_thresholds,
319            },
320            rem,
321        ))
322    }
323}
324
325#[doc(hidden)]
326#[deprecated(
327    since = "1.4.4",
328    note = "function moved to casper_types::crypto::blake2b"
329)]
330pub fn blake2b<T: AsRef<[u8]>>(data: T) -> [u8; BLAKE2B_DIGEST_LENGTH] {
331    crypto::blake2b(data)
332}
333
334/// Errors that can occur while adding a new [`AccountHash`] to an account's associated keys map.
335#[derive(PartialEq, Eq, Debug, Copy, Clone)]
336#[repr(i32)]
337#[non_exhaustive]
338pub enum AddKeyFailure {
339    /// There are already maximum [`AccountHash`]s associated with the given account.
340    MaxKeysLimit = 1,
341    /// The given [`AccountHash`] is already associated with the given account.
342    DuplicateKey = 2,
343    /// Caller doesn't have sufficient permissions to associate a new [`AccountHash`] with the
344    /// given account.
345    PermissionDenied = 3,
346}
347
348impl Display for AddKeyFailure {
349    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
350        match self {
351            AddKeyFailure::MaxKeysLimit => formatter.write_str(
352                "Unable to add new associated key because maximum amount of keys is reached",
353            ),
354            AddKeyFailure::DuplicateKey => formatter
355                .write_str("Unable to add new associated key because given key already exists"),
356            AddKeyFailure::PermissionDenied => formatter
357                .write_str("Unable to add new associated key due to insufficient permissions"),
358        }
359    }
360}
361
362// This conversion is not intended to be used by third party crates.
363#[doc(hidden)]
364impl TryFrom<i32> for AddKeyFailure {
365    type Error = TryFromIntError;
366
367    fn try_from(value: i32) -> Result<Self, Self::Error> {
368        match value {
369            d if d == AddKeyFailure::MaxKeysLimit as i32 => Ok(AddKeyFailure::MaxKeysLimit),
370            d if d == AddKeyFailure::DuplicateKey as i32 => Ok(AddKeyFailure::DuplicateKey),
371            d if d == AddKeyFailure::PermissionDenied as i32 => Ok(AddKeyFailure::PermissionDenied),
372            _ => Err(TryFromIntError(())),
373        }
374    }
375}
376
377/// Errors that can occur while removing a [`AccountHash`] from an account's associated keys map.
378#[derive(Debug, Eq, PartialEq, Copy, Clone)]
379#[repr(i32)]
380#[non_exhaustive]
381pub enum RemoveKeyFailure {
382    /// The given [`AccountHash`] is not associated with the given account.
383    MissingKey = 1,
384    /// Caller doesn't have sufficient permissions to remove an associated [`AccountHash`] from the
385    /// given account.
386    PermissionDenied = 2,
387    /// Removing the given associated [`AccountHash`] would cause the total weight of all remaining
388    /// `AccountHash`s to fall below one of the action thresholds for the given account.
389    ThresholdViolation = 3,
390}
391
392impl Display for RemoveKeyFailure {
393    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
394        match self {
395            RemoveKeyFailure::MissingKey => {
396                formatter.write_str("Unable to remove a key that does not exist")
397            }
398            RemoveKeyFailure::PermissionDenied => formatter
399                .write_str("Unable to remove associated key due to insufficient permissions"),
400            RemoveKeyFailure::ThresholdViolation => formatter.write_str(
401                "Unable to remove a key which would violate action threshold constraints",
402            ),
403        }
404    }
405}
406
407// This conversion is not intended to be used by third party crates.
408#[doc(hidden)]
409impl TryFrom<i32> for RemoveKeyFailure {
410    type Error = TryFromIntError;
411
412    fn try_from(value: i32) -> Result<Self, Self::Error> {
413        match value {
414            d if d == RemoveKeyFailure::MissingKey as i32 => Ok(RemoveKeyFailure::MissingKey),
415            d if d == RemoveKeyFailure::PermissionDenied as i32 => {
416                Ok(RemoveKeyFailure::PermissionDenied)
417            }
418            d if d == RemoveKeyFailure::ThresholdViolation as i32 => {
419                Ok(RemoveKeyFailure::ThresholdViolation)
420            }
421            _ => Err(TryFromIntError(())),
422        }
423    }
424}
425
426/// Errors that can occur while updating the [`Weight`] of a [`AccountHash`] in an account's
427/// associated keys map.
428#[derive(PartialEq, Eq, Debug, Copy, Clone)]
429#[repr(i32)]
430#[non_exhaustive]
431pub enum UpdateKeyFailure {
432    /// The given [`AccountHash`] is not associated with the given account.
433    MissingKey = 1,
434    /// Caller doesn't have sufficient permissions to update an associated [`AccountHash`] from the
435    /// given account.
436    PermissionDenied = 2,
437    /// Updating the [`Weight`] of the given associated [`AccountHash`] would cause the total
438    /// weight of all `AccountHash`s to fall below one of the action thresholds for the given
439    /// account.
440    ThresholdViolation = 3,
441}
442
443impl Display for UpdateKeyFailure {
444    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
445        match self {
446            UpdateKeyFailure::MissingKey => formatter.write_str(
447                "Unable to update the value under an associated key that does not exist",
448            ),
449            UpdateKeyFailure::PermissionDenied => formatter
450                .write_str("Unable to update associated key due to insufficient permissions"),
451            UpdateKeyFailure::ThresholdViolation => formatter.write_str(
452                "Unable to update weight that would fall below any of action thresholds",
453            ),
454        }
455    }
456}
457
458// This conversion is not intended to be used by third party crates.
459#[doc(hidden)]
460impl TryFrom<i32> for UpdateKeyFailure {
461    type Error = TryFromIntError;
462
463    fn try_from(value: i32) -> Result<Self, Self::Error> {
464        match value {
465            d if d == UpdateKeyFailure::MissingKey as i32 => Ok(UpdateKeyFailure::MissingKey),
466            d if d == UpdateKeyFailure::PermissionDenied as i32 => {
467                Ok(UpdateKeyFailure::PermissionDenied)
468            }
469            d if d == UpdateKeyFailure::ThresholdViolation as i32 => {
470                Ok(UpdateKeyFailure::ThresholdViolation)
471            }
472            _ => Err(TryFromIntError(())),
473        }
474    }
475}
476
477#[doc(hidden)]
478#[cfg(any(feature = "testing", feature = "gens", test))]
479pub mod gens {
480    use proptest::prelude::*;
481
482    use crate::{
483        account::{
484            action_thresholds::gens::action_thresholds_arb,
485            associated_keys::gens::associated_keys_arb, Account, Weight,
486        },
487        gens::{account_hash_arb, named_keys_arb, uref_arb},
488    };
489
490    prop_compose! {
491        pub fn account_arb()(
492            account_hash in account_hash_arb(),
493            urefs in named_keys_arb(3),
494            purse in uref_arb(),
495            thresholds in action_thresholds_arb(),
496            mut associated_keys in associated_keys_arb(),
497        ) -> Account {
498                associated_keys.add_key(account_hash, Weight::new(1)).unwrap();
499                Account::new(
500                    account_hash,
501                    urefs,
502                    purse,
503                    associated_keys,
504                    thresholds,
505                )
506        }
507    }
508}
509
510#[cfg(test)]
511mod tests {
512    use crate::{
513        account::{
514            Account, AccountHash, ActionThresholds, ActionType, AssociatedKeys, RemoveKeyFailure,
515            SetThresholdFailure, UpdateKeyFailure, Weight,
516        },
517        contracts::NamedKeys,
518        AccessRights, URef,
519    };
520    use std::{collections::BTreeSet, convert::TryFrom, iter::FromIterator, vec::Vec};
521
522    use super::*;
523
524    #[test]
525    fn account_hash_from_slice() {
526        let bytes: Vec<u8> = (0..32).collect();
527        let account_hash = AccountHash::try_from(&bytes[..]).expect("should create account hash");
528        assert_eq!(&bytes, &account_hash.as_bytes());
529    }
530
531    #[test]
532    fn account_hash_from_slice_too_small() {
533        let _account_hash =
534            AccountHash::try_from(&[0u8; 31][..]).expect_err("should not create account hash");
535    }
536
537    #[test]
538    fn account_hash_from_slice_too_big() {
539        let _account_hash =
540            AccountHash::try_from(&[0u8; 33][..]).expect_err("should not create account hash");
541    }
542
543    #[test]
544    fn try_from_i32_for_set_threshold_failure() {
545        let max_valid_value_for_variant = SetThresholdFailure::InsufficientTotalWeight as i32;
546        assert_eq!(
547            Err(TryFromIntError(())),
548            SetThresholdFailure::try_from(max_valid_value_for_variant + 1),
549            "Did you forget to update `SetThresholdFailure::try_from` for a new variant of \
550                   `SetThresholdFailure`, or `max_valid_value_for_variant` in this test?"
551        );
552    }
553
554    #[test]
555    fn try_from_i32_for_add_key_failure() {
556        let max_valid_value_for_variant = AddKeyFailure::PermissionDenied as i32;
557        assert_eq!(
558            Err(TryFromIntError(())),
559            AddKeyFailure::try_from(max_valid_value_for_variant + 1),
560            "Did you forget to update `AddKeyFailure::try_from` for a new variant of \
561                   `AddKeyFailure`, or `max_valid_value_for_variant` in this test?"
562        );
563    }
564
565    #[test]
566    fn try_from_i32_for_remove_key_failure() {
567        let max_valid_value_for_variant = RemoveKeyFailure::ThresholdViolation as i32;
568        assert_eq!(
569            Err(TryFromIntError(())),
570            RemoveKeyFailure::try_from(max_valid_value_for_variant + 1),
571            "Did you forget to update `RemoveKeyFailure::try_from` for a new variant of \
572                   `RemoveKeyFailure`, or `max_valid_value_for_variant` in this test?"
573        );
574    }
575
576    #[test]
577    fn try_from_i32_for_update_key_failure() {
578        let max_valid_value_for_variant = UpdateKeyFailure::ThresholdViolation as i32;
579        assert_eq!(
580            Err(TryFromIntError(())),
581            UpdateKeyFailure::try_from(max_valid_value_for_variant + 1),
582            "Did you forget to update `UpdateKeyFailure::try_from` for a new variant of \
583                   `UpdateKeyFailure`, or `max_valid_value_for_variant` in this test?"
584        );
585    }
586
587    #[test]
588    fn account_hash_from_str() {
589        let account_hash = AccountHash([3; 32]);
590        let encoded = account_hash.to_formatted_string();
591        let decoded = AccountHash::from_formatted_str(&encoded).unwrap();
592        assert_eq!(account_hash, decoded);
593
594        let invalid_prefix =
595            "accounthash-0000000000000000000000000000000000000000000000000000000000000000";
596        assert!(AccountHash::from_formatted_str(invalid_prefix).is_err());
597
598        let invalid_prefix =
599            "account-hash0000000000000000000000000000000000000000000000000000000000000000";
600        assert!(AccountHash::from_formatted_str(invalid_prefix).is_err());
601
602        let short_addr =
603            "account-hash-00000000000000000000000000000000000000000000000000000000000000";
604        assert!(AccountHash::from_formatted_str(short_addr).is_err());
605
606        let long_addr =
607            "account-hash-000000000000000000000000000000000000000000000000000000000000000000";
608        assert!(AccountHash::from_formatted_str(long_addr).is_err());
609
610        let invalid_hex =
611            "account-hash-000000000000000000000000000000000000000000000000000000000000000g";
612        assert!(AccountHash::from_formatted_str(invalid_hex).is_err());
613    }
614
615    #[test]
616    fn account_hash_serde_roundtrip() {
617        let account_hash = AccountHash([255; 32]);
618        let serialized = bincode::serialize(&account_hash).unwrap();
619        let decoded = bincode::deserialize(&serialized).unwrap();
620        assert_eq!(account_hash, decoded);
621    }
622
623    #[test]
624    fn account_hash_json_roundtrip() {
625        let account_hash = AccountHash([255; 32]);
626        let json_string = serde_json::to_string_pretty(&account_hash).unwrap();
627        let decoded = serde_json::from_str(&json_string).unwrap();
628        assert_eq!(account_hash, decoded);
629    }
630
631    #[test]
632    fn associated_keys_can_authorize_keys() {
633        let key_1 = AccountHash::new([0; 32]);
634        let key_2 = AccountHash::new([1; 32]);
635        let key_3 = AccountHash::new([2; 32]);
636        let mut keys = AssociatedKeys::default();
637
638        keys.add_key(key_2, Weight::new(2))
639            .expect("should add key_1");
640        keys.add_key(key_1, Weight::new(1))
641            .expect("should add key_1");
642        keys.add_key(key_3, Weight::new(3))
643            .expect("should add key_1");
644
645        let account = Account::new(
646            AccountHash::new([0u8; 32]),
647            NamedKeys::new(),
648            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
649            keys,
650            // deploy: 33 (3*11)
651            ActionThresholds::new(Weight::new(33), Weight::new(48))
652                .expect("should create thresholds"),
653        );
654
655        assert!(account.can_authorize(&BTreeSet::from_iter(vec![key_3, key_2, key_1])));
656        assert!(account.can_authorize(&BTreeSet::from_iter(vec![key_1, key_3, key_2])));
657
658        assert!(account.can_authorize(&BTreeSet::from_iter(vec![key_1, key_2])));
659        assert!(account.can_authorize(&BTreeSet::from_iter(vec![key_1])));
660
661        assert!(!account.can_authorize(&BTreeSet::from_iter(vec![
662            key_1,
663            key_2,
664            AccountHash::new([42; 32])
665        ])));
666        assert!(!account.can_authorize(&BTreeSet::from_iter(vec![
667            AccountHash::new([42; 32]),
668            key_1,
669            key_2
670        ])));
671        assert!(!account.can_authorize(&BTreeSet::from_iter(vec![
672            AccountHash::new([43; 32]),
673            AccountHash::new([44; 32]),
674            AccountHash::new([42; 32])
675        ])));
676        assert!(!account.can_authorize(&BTreeSet::new()));
677    }
678
679    #[test]
680    fn account_can_deploy_with() {
681        let associated_keys = {
682            let mut res = AssociatedKeys::new(AccountHash::new([1u8; 32]), Weight::new(1));
683            res.add_key(AccountHash::new([2u8; 32]), Weight::new(11))
684                .expect("should add key 1");
685            res.add_key(AccountHash::new([3u8; 32]), Weight::new(11))
686                .expect("should add key 2");
687            res.add_key(AccountHash::new([4u8; 32]), Weight::new(11))
688                .expect("should add key 3");
689            res
690        };
691        let account = Account::new(
692            AccountHash::new([0u8; 32]),
693            NamedKeys::new(),
694            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
695            associated_keys,
696            // deploy: 33 (3*11)
697            ActionThresholds::new(Weight::new(33), Weight::new(48))
698                .expect("should create thresholds"),
699        );
700
701        // sum: 22, required 33 - can't deploy
702        assert!(!account.can_deploy_with(&BTreeSet::from_iter(vec![
703            AccountHash::new([3u8; 32]),
704            AccountHash::new([2u8; 32]),
705        ])));
706
707        // sum: 33, required 33 - can deploy
708        assert!(account.can_deploy_with(&BTreeSet::from_iter(vec![
709            AccountHash::new([4u8; 32]),
710            AccountHash::new([3u8; 32]),
711            AccountHash::new([2u8; 32]),
712        ])));
713
714        // sum: 34, required 33 - can deploy
715        assert!(account.can_deploy_with(&BTreeSet::from_iter(vec![
716            AccountHash::new([2u8; 32]),
717            AccountHash::new([1u8; 32]),
718            AccountHash::new([4u8; 32]),
719            AccountHash::new([3u8; 32]),
720        ])));
721    }
722
723    #[test]
724    fn account_can_manage_keys_with() {
725        let associated_keys = {
726            let mut res = AssociatedKeys::new(AccountHash::new([1u8; 32]), Weight::new(1));
727            res.add_key(AccountHash::new([2u8; 32]), Weight::new(11))
728                .expect("should add key 1");
729            res.add_key(AccountHash::new([3u8; 32]), Weight::new(11))
730                .expect("should add key 2");
731            res.add_key(AccountHash::new([4u8; 32]), Weight::new(11))
732                .expect("should add key 3");
733            res
734        };
735        let account = Account::new(
736            AccountHash::new([0u8; 32]),
737            NamedKeys::new(),
738            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
739            associated_keys,
740            // deploy: 33 (3*11)
741            ActionThresholds::new(Weight::new(11), Weight::new(33))
742                .expect("should create thresholds"),
743        );
744
745        // sum: 22, required 33 - can't manage
746        assert!(!account.can_manage_keys_with(&BTreeSet::from_iter(vec![
747            AccountHash::new([3u8; 32]),
748            AccountHash::new([2u8; 32]),
749        ])));
750
751        // sum: 33, required 33 - can manage
752        assert!(account.can_manage_keys_with(&BTreeSet::from_iter(vec![
753            AccountHash::new([4u8; 32]),
754            AccountHash::new([3u8; 32]),
755            AccountHash::new([2u8; 32]),
756        ])));
757
758        // sum: 34, required 33 - can manage
759        assert!(account.can_manage_keys_with(&BTreeSet::from_iter(vec![
760            AccountHash::new([2u8; 32]),
761            AccountHash::new([1u8; 32]),
762            AccountHash::new([4u8; 32]),
763            AccountHash::new([3u8; 32]),
764        ])));
765    }
766
767    #[test]
768    fn set_action_threshold_higher_than_total_weight() {
769        let identity_key = AccountHash::new([1u8; 32]);
770        let key_1 = AccountHash::new([2u8; 32]);
771        let key_2 = AccountHash::new([3u8; 32]);
772        let key_3 = AccountHash::new([4u8; 32]);
773        let associated_keys = {
774            let mut res = AssociatedKeys::new(identity_key, Weight::new(1));
775            res.add_key(key_1, Weight::new(2))
776                .expect("should add key 1");
777            res.add_key(key_2, Weight::new(3))
778                .expect("should add key 2");
779            res.add_key(key_3, Weight::new(4))
780                .expect("should add key 3");
781            res
782        };
783        let mut account = Account::new(
784            AccountHash::new([0u8; 32]),
785            NamedKeys::new(),
786            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
787            associated_keys,
788            // deploy: 33 (3*11)
789            ActionThresholds::new(Weight::new(33), Weight::new(48))
790                .expect("should create thresholds"),
791        );
792
793        assert_eq!(
794            account
795                .set_action_threshold(ActionType::Deployment, Weight::new(1 + 2 + 3 + 4 + 1))
796                .unwrap_err(),
797            SetThresholdFailure::InsufficientTotalWeight,
798        );
799        assert_eq!(
800            account
801                .set_action_threshold(ActionType::Deployment, Weight::new(1 + 2 + 3 + 4 + 245))
802                .unwrap_err(),
803            SetThresholdFailure::InsufficientTotalWeight,
804        )
805    }
806
807    #[test]
808    fn remove_key_would_violate_action_thresholds() {
809        let identity_key = AccountHash::new([1u8; 32]);
810        let key_1 = AccountHash::new([2u8; 32]);
811        let key_2 = AccountHash::new([3u8; 32]);
812        let key_3 = AccountHash::new([4u8; 32]);
813        let associated_keys = {
814            let mut res = AssociatedKeys::new(identity_key, Weight::new(1));
815            res.add_key(key_1, Weight::new(2))
816                .expect("should add key 1");
817            res.add_key(key_2, Weight::new(3))
818                .expect("should add key 2");
819            res.add_key(key_3, Weight::new(4))
820                .expect("should add key 3");
821            res
822        };
823        let mut account = Account::new(
824            AccountHash::new([0u8; 32]),
825            NamedKeys::new(),
826            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
827            associated_keys,
828            // deploy: 33 (3*11)
829            ActionThresholds::new(Weight::new(1 + 2 + 3 + 4), Weight::new(1 + 2 + 3 + 4 + 5))
830                .expect("should create thresholds"),
831        );
832
833        assert_eq!(
834            account.remove_associated_key(key_3).unwrap_err(),
835            RemoveKeyFailure::ThresholdViolation,
836        )
837    }
838
839    #[test]
840    fn updating_key_would_violate_action_thresholds() {
841        let identity_key = AccountHash::new([1u8; 32]);
842        let identity_key_weight = Weight::new(1);
843        let key_1 = AccountHash::new([2u8; 32]);
844        let key_1_weight = Weight::new(2);
845        let key_2 = AccountHash::new([3u8; 32]);
846        let key_2_weight = Weight::new(3);
847        let key_3 = AccountHash::new([4u8; 32]);
848        let key_3_weight = Weight::new(4);
849        let associated_keys = {
850            let mut res = AssociatedKeys::new(identity_key, identity_key_weight);
851            res.add_key(key_1, key_1_weight).expect("should add key 1");
852            res.add_key(key_2, key_2_weight).expect("should add key 2");
853            res.add_key(key_3, key_3_weight).expect("should add key 3");
854            // 1 + 2 + 3 + 4
855            res
856        };
857
858        let deployment_threshold = Weight::new(
859            identity_key_weight.value()
860                + key_1_weight.value()
861                + key_2_weight.value()
862                + key_3_weight.value(),
863        );
864        let key_management_threshold = Weight::new(deployment_threshold.value() + 1);
865        let mut account = Account::new(
866            identity_key,
867            NamedKeys::new(),
868            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
869            associated_keys,
870            // deploy: 33 (3*11)
871            ActionThresholds::new(deployment_threshold, key_management_threshold)
872                .expect("should create thresholds"),
873        );
874
875        // Decreases by 3
876        assert_eq!(
877            account
878                .clone()
879                .update_associated_key(key_3, Weight::new(1))
880                .unwrap_err(),
881            UpdateKeyFailure::ThresholdViolation,
882        );
883
884        // increase total weight (12)
885        account
886            .update_associated_key(identity_key, Weight::new(3))
887            .unwrap();
888
889        // variant a) decrease total weight by 1 (total 11)
890        account
891            .clone()
892            .update_associated_key(key_3, Weight::new(3))
893            .unwrap();
894        // variant b) decrease total weight by 3 (total 9) - fail
895        assert_eq!(
896            account
897                .update_associated_key(key_3, Weight::new(1))
898                .unwrap_err(),
899            UpdateKeyFailure::ThresholdViolation
900        );
901    }
902
903    #[test]
904    fn overflowing_should_allow_removal() {
905        let identity_key = AccountHash::new([42; 32]);
906        let key_1 = AccountHash::new([2u8; 32]);
907        let key_2 = AccountHash::new([3u8; 32]);
908
909        let associated_keys = {
910            // Identity
911            let mut res = AssociatedKeys::new(identity_key, Weight::new(1));
912
913            // Spare key
914            res.add_key(key_1, Weight::new(2))
915                .expect("should add key 1");
916            // Big key
917            res.add_key(key_2, Weight::new(255))
918                .expect("should add key 2");
919
920            res
921        };
922
923        let mut account = Account::new(
924            identity_key,
925            NamedKeys::new(),
926            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
927            associated_keys,
928            ActionThresholds::new(Weight::new(1), Weight::new(254))
929                .expect("should create thresholds"),
930        );
931
932        account.remove_associated_key(key_1).expect("should work")
933    }
934
935    #[test]
936    fn overflowing_should_allow_updating() {
937        let identity_key = AccountHash::new([1; 32]);
938        let identity_key_weight = Weight::new(1);
939        let key_1 = AccountHash::new([2u8; 32]);
940        let key_1_weight = Weight::new(3);
941        let key_2 = AccountHash::new([3u8; 32]);
942        let key_2_weight = Weight::new(255);
943        let deployment_threshold = Weight::new(1);
944        let key_management_threshold = Weight::new(254);
945
946        let associated_keys = {
947            // Identity
948            let mut res = AssociatedKeys::new(identity_key, identity_key_weight);
949
950            // Spare key
951            res.add_key(key_1, key_1_weight).expect("should add key 1");
952            // Big key
953            res.add_key(key_2, key_2_weight).expect("should add key 2");
954
955            res
956        };
957
958        let mut account = Account::new(
959            identity_key,
960            NamedKeys::new(),
961            URef::new([0u8; 32], AccessRights::READ_ADD_WRITE),
962            associated_keys,
963            ActionThresholds::new(deployment_threshold, key_management_threshold)
964                .expect("should create thresholds"),
965        );
966
967        // decrease so total weight would be changed from 1 + 3 + 255 to 1 + 1 + 255
968        account
969            .update_associated_key(key_1, Weight::new(1))
970            .expect("should work");
971    }
972
973    #[test]
974    fn should_extract_access_rights() {
975        const MAIN_PURSE: URef = URef::new([2; 32], AccessRights::READ_ADD_WRITE);
976        const OTHER_UREF: URef = URef::new([3; 32], AccessRights::READ);
977
978        let account_hash = AccountHash::new([1u8; 32]);
979        let mut named_keys = NamedKeys::new();
980        named_keys.insert("a".to_string(), Key::URef(OTHER_UREF));
981        let associated_keys = AssociatedKeys::new(account_hash, Weight::new(1));
982        let account = Account::new(
983            account_hash,
984            named_keys,
985            MAIN_PURSE,
986            associated_keys,
987            ActionThresholds::new(Weight::new(1), Weight::new(1))
988                .expect("should create thresholds"),
989        );
990
991        let actual_access_rights = account.extract_access_rights();
992
993        let expected_access_rights =
994            ContextAccessRights::new(Key::from(account_hash), vec![MAIN_PURSE, OTHER_UREF]);
995        assert_eq!(actual_access_rights, expected_access_rights)
996    }
997}
998
999#[cfg(test)]
1000mod proptests {
1001    use proptest::prelude::*;
1002
1003    use crate::bytesrepr;
1004
1005    use super::*;
1006
1007    proptest! {
1008        #[test]
1009        fn test_value_account(acct in gens::account_arb()) {
1010            bytesrepr::test_serialization_roundtrip(&acct);
1011        }
1012    }
1013}