logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! This module contains types and functions for managing action thresholds.

use alloc::vec::Vec;
use serde::{Deserialize, Serialize};

use crate::{
    account::{ActionType, SetThresholdFailure, Weight, WEIGHT_SERIALIZED_LENGTH},
    bytesrepr::{self, Error, FromBytes, ToBytes},
};

/// Thresholds that have to be met when executing an action of a certain type.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ActionThresholds {
    deployment: Weight,
    key_management: Weight,
}

impl ActionThresholds {
    /// Creates new ActionThresholds object with provided weights
    ///
    /// Requires deployment threshold to be lower than or equal to
    /// key management threshold.
    pub fn new(
        deployment: Weight,
        key_management: Weight,
    ) -> Result<ActionThresholds, SetThresholdFailure> {
        if deployment > key_management {
            return Err(SetThresholdFailure::DeploymentThreshold);
        }
        Ok(ActionThresholds {
            deployment,
            key_management,
        })
    }
    /// Sets new threshold for [ActionType::Deployment].
    /// Should return an error if setting new threshold for `action_type` breaks
    /// one of the invariants. Currently, invariant is that
    /// `ActionType::Deployment` threshold shouldn't be higher than any
    /// other, which should be checked both when increasing `Deployment`
    /// threshold and decreasing the other.
    pub fn set_deployment_threshold(
        &mut self,
        new_threshold: Weight,
    ) -> Result<(), SetThresholdFailure> {
        if new_threshold > self.key_management {
            Err(SetThresholdFailure::DeploymentThreshold)
        } else {
            self.deployment = new_threshold;
            Ok(())
        }
    }

    /// Sets new threshold for [ActionType::KeyManagement].
    pub fn set_key_management_threshold(
        &mut self,
        new_threshold: Weight,
    ) -> Result<(), SetThresholdFailure> {
        if self.deployment > new_threshold {
            Err(SetThresholdFailure::KeyManagementThreshold)
        } else {
            self.key_management = new_threshold;
            Ok(())
        }
    }

    /// Returns the deployment action threshold.
    pub fn deployment(&self) -> &Weight {
        &self.deployment
    }

    /// Returns key management action threshold.
    pub fn key_management(&self) -> &Weight {
        &self.key_management
    }

    /// Unified function that takes an action type, and changes appropriate
    /// threshold defined by the [ActionType] variants.
    pub fn set_threshold(
        &mut self,
        action_type: ActionType,
        new_threshold: Weight,
    ) -> Result<(), SetThresholdFailure> {
        match action_type {
            ActionType::Deployment => self.set_deployment_threshold(new_threshold),
            ActionType::KeyManagement => self.set_key_management_threshold(new_threshold),
        }
    }
}

impl Default for ActionThresholds {
    fn default() -> Self {
        ActionThresholds {
            deployment: Weight::new(1),
            key_management: Weight::new(1),
        }
    }
}

impl ToBytes for ActionThresholds {
    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
        let mut result = bytesrepr::unchecked_allocate_buffer(self);
        result.append(&mut self.deployment.to_bytes()?);
        result.append(&mut self.key_management.to_bytes()?);
        Ok(result)
    }

    fn serialized_length(&self) -> usize {
        2 * WEIGHT_SERIALIZED_LENGTH
    }
}

impl FromBytes for ActionThresholds {
    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
        let (deployment, rem) = Weight::from_bytes(bytes)?;
        let (key_management, rem) = Weight::from_bytes(rem)?;
        let ret = ActionThresholds {
            deployment,
            key_management,
        };
        Ok((ret, rem))
    }
}

#[doc(hidden)]
#[cfg(any(feature = "gens", test))]
pub mod gens {
    use proptest::prelude::*;

    use super::ActionThresholds;

    pub fn action_thresholds_arb() -> impl Strategy<Value = ActionThresholds> {
        Just(Default::default())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn should_create_new_action_thresholds() {
        let action_thresholds = ActionThresholds::new(Weight::new(1), Weight::new(42)).unwrap();
        assert_eq!(*action_thresholds.deployment(), Weight::new(1));
        assert_eq!(*action_thresholds.key_management(), Weight::new(42));
    }

    #[test]
    fn should_not_create_action_thresholds_with_invalid_deployment_threshold() {
        // deployment cant be greater than key management
        assert!(ActionThresholds::new(Weight::new(5), Weight::new(1)).is_err());
    }

    #[test]
    fn serialization_roundtrip() {
        let action_thresholds = ActionThresholds::new(Weight::new(1), Weight::new(42)).unwrap();
        bytesrepr::test_serialization_roundtrip(&action_thresholds);
    }
}