casper_types/
runtime_footprint.rs

1use crate::{
2    account::AccountHash,
3    addressable_entity::{AssociatedKeys, ContractRuntimeTag, Weight},
4    contracts::{ContractHash, NamedKeys},
5    system::SystemEntityType,
6    Account, AddressableEntity, ContextAccessRights, Contract, EntityAddr, EntityKind, EntryPoints,
7    HashAddr, Key, ProtocolVersion, URef,
8};
9use alloc::{
10    collections::{BTreeMap, BTreeSet},
11    string::String,
12};
13use core::{fmt::Debug, iter};
14#[cfg(feature = "datasize")]
15use datasize::DataSize;
16#[cfg(feature = "json-schema")]
17use schemars::JsonSchema;
18use serde::{Deserialize, Serialize};
19
20/// Runtime Address.
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22#[cfg_attr(feature = "datasize", derive(DataSize))]
23#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
24pub enum RuntimeAddress {
25    /// Account address
26    Hash(HashAddr),
27    /// Runtime executable address.
28    StoredContract {
29        /// The hash addr of the runtime entity
30        hash_addr: HashAddr,
31        /// The package hash
32        package_hash_addr: HashAddr,
33        /// The wasm hash
34        wasm_hash_addr: HashAddr,
35        /// protocol version
36        protocol_version: ProtocolVersion,
37    },
38}
39
40impl RuntimeAddress {
41    /// Returns a new hash
42    pub fn new_hash(hash_addr: HashAddr) -> Self {
43        Self::Hash(hash_addr)
44    }
45
46    /// Returns new stored contract
47    pub fn new_stored_contract(
48        hash_addr: HashAddr,
49        package_hash_addr: HashAddr,
50        wasm_hash_addr: HashAddr,
51        protocol_version: ProtocolVersion,
52    ) -> Self {
53        Self::StoredContract {
54            hash_addr,
55            package_hash_addr,
56            wasm_hash_addr,
57            protocol_version,
58        }
59    }
60
61    /// The hash addr for the runtime.
62    pub fn hash_addr(&self) -> HashAddr {
63        match self {
64            RuntimeAddress::Hash(hash_addr) => *hash_addr,
65            RuntimeAddress::StoredContract { hash_addr, .. } => *hash_addr,
66        }
67    }
68}
69
70#[repr(u8)]
71#[allow(clippy::enum_variant_names)]
72pub(crate) enum Action {
73    KeyManagement = 0,
74    DeployManagement,
75    UpgradeManagement,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79#[cfg_attr(feature = "datasize", derive(DataSize))]
80pub struct RuntimeFootprint {
81    named_keys: NamedKeys,
82    action_thresholds: BTreeMap<u8, Weight>,
83    associated_keys: AssociatedKeys,
84    entry_points: EntryPoints,
85    entity_kind: EntityKind,
86
87    main_purse: Option<URef>,
88    runtime_address: RuntimeAddress,
89}
90
91impl RuntimeFootprint {
92    pub fn new(
93        named_keys: NamedKeys,
94        action_thresholds: BTreeMap<u8, Weight>,
95        associated_keys: AssociatedKeys,
96        entry_points: EntryPoints,
97        entity_kind: EntityKind,
98        main_purse: Option<URef>,
99        runtime_address: RuntimeAddress,
100    ) -> Self {
101        Self {
102            named_keys,
103            action_thresholds,
104            associated_keys,
105            entry_points,
106            entity_kind,
107            main_purse,
108            runtime_address,
109        }
110    }
111
112    pub fn new_account_footprint(account: Account) -> Self {
113        let named_keys = account.named_keys().clone();
114        let action_thresholds = {
115            let mut ret = BTreeMap::new();
116            ret.insert(
117                Action::KeyManagement as u8,
118                Weight::new(account.action_thresholds().key_management.value()),
119            );
120            ret.insert(
121                Action::DeployManagement as u8,
122                Weight::new(account.action_thresholds().deployment.value()),
123            );
124            ret
125        };
126        let associated_keys = account.associated_keys().clone().into();
127        let entry_points = EntryPoints::new();
128        let entity_kind = EntityKind::Account(account.account_hash());
129        let main_purse = Some(account.main_purse());
130        let runtime_address = RuntimeAddress::new_hash(account.account_hash().value());
131
132        Self::new(
133            named_keys,
134            action_thresholds,
135            associated_keys,
136            entry_points,
137            entity_kind,
138            main_purse,
139            runtime_address,
140        )
141    }
142
143    pub fn new_contract_footprint(
144        contract_hash: ContractHash,
145        contract: Contract,
146        system_entity_type: Option<SystemEntityType>,
147    ) -> Self {
148        let contract_package_hash = contract.contract_package_hash();
149        let contract_wasm_hash = contract.contract_wasm_hash();
150        let entry_points = contract.entry_points().clone().into();
151        let protocol_version = contract.protocol_version();
152        let named_keys = contract.take_named_keys();
153
154        let runtime_address = RuntimeAddress::new_stored_contract(
155            contract_hash.value(),
156            contract_package_hash.value(),
157            contract_wasm_hash.value(),
158            protocol_version,
159        );
160
161        let main_purse = None;
162        let action_thresholds = BTreeMap::new();
163        let associated_keys = AssociatedKeys::empty_keys();
164
165        let entity_kind = match system_entity_type {
166            None => EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1),
167            Some(kind) => EntityKind::System(kind),
168        };
169
170        Self::new(
171            named_keys,
172            action_thresholds,
173            associated_keys,
174            entry_points,
175            entity_kind,
176            main_purse,
177            runtime_address,
178        )
179    }
180
181    pub fn new_entity_footprint(
182        entity_addr: EntityAddr,
183        entity: AddressableEntity,
184        named_keys: NamedKeys,
185        entry_points: EntryPoints,
186    ) -> Self {
187        let runtime_address = RuntimeAddress::new_stored_contract(
188            entity_addr.value(),
189            entity.package_hash().value(),
190            entity.byte_code_hash().value(),
191            entity.protocol_version(),
192        );
193        let action_thresholds = {
194            let mut ret = BTreeMap::new();
195            ret.insert(
196                Action::KeyManagement as u8,
197                entity.action_thresholds().key_management,
198            );
199            ret.insert(
200                Action::DeployManagement as u8,
201                entity.action_thresholds().key_management,
202            );
203            ret.insert(
204                Action::UpgradeManagement as u8,
205                entity.action_thresholds().upgrade_management,
206            );
207            ret
208        };
209        Self::new(
210            named_keys,
211            action_thresholds,
212            entity.associated_keys().clone(),
213            entry_points,
214            entity.entity_kind(),
215            Some(entity.main_purse()),
216            runtime_address,
217        )
218    }
219
220    pub fn package_hash(&self) -> Option<HashAddr> {
221        match &self.runtime_address {
222            RuntimeAddress::Hash(_) => None,
223            RuntimeAddress::StoredContract {
224                package_hash_addr, ..
225            } => Some(*package_hash_addr),
226        }
227    }
228
229    pub fn associated_keys(&self) -> &AssociatedKeys {
230        &self.associated_keys
231    }
232
233    pub fn wasm_hash(&self) -> Option<HashAddr> {
234        match &self.runtime_address {
235            RuntimeAddress::Hash(_) => None,
236            RuntimeAddress::StoredContract { wasm_hash_addr, .. } => Some(*wasm_hash_addr),
237        }
238    }
239
240    pub fn hash_addr(&self) -> HashAddr {
241        match &self.runtime_address {
242            RuntimeAddress::Hash(hash_addr) => *hash_addr,
243            RuntimeAddress::StoredContract { hash_addr, .. } => *hash_addr,
244        }
245    }
246
247    pub fn named_keys(&self) -> &NamedKeys {
248        &self.named_keys
249    }
250
251    pub fn insert_into_named_keys(&mut self, name: String, key: Key) {
252        self.named_keys.insert(name, key);
253    }
254
255    pub fn named_keys_mut(&mut self) -> &mut NamedKeys {
256        &mut self.named_keys
257    }
258
259    pub fn take_named_keys(self) -> NamedKeys {
260        self.named_keys
261    }
262
263    pub fn main_purse(&self) -> Option<URef> {
264        self.main_purse
265    }
266
267    pub fn set_main_purse(&mut self, purse: URef) {
268        self.main_purse = Some(purse);
269    }
270
271    pub fn entry_points(&self) -> &EntryPoints {
272        &self.entry_points
273    }
274
275    pub fn entity_kind(&self) -> EntityKind {
276        self.entity_kind
277    }
278
279    /// Checks whether all authorization keys are associated with this addressable entity.
280    pub fn can_authorize(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
281        !authorization_keys.is_empty()
282            && authorization_keys
283                .iter()
284                .any(|e| self.associated_keys.contains_key(e))
285    }
286
287    /// Checks whether the sum of the weights of all authorization keys is
288    /// greater or equal to key management threshold.
289    pub fn can_manage_keys_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
290        let total_weight = self
291            .associated_keys
292            .calculate_keys_weight(authorization_keys);
293
294        match self.action_thresholds.get(&(Action::KeyManagement as u8)) {
295            None => false,
296            Some(weight) => total_weight >= *weight,
297        }
298    }
299
300    /// Checks whether the sum of the weights of all authorization keys is
301    /// greater or equal to deploy threshold.
302    pub fn can_deploy_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
303        let total_weight = self
304            .associated_keys
305            .calculate_keys_weight(authorization_keys);
306
307        match self
308            .action_thresholds
309            .get(&(Action::DeployManagement as u8))
310        {
311            None => false,
312            Some(weight) => total_weight >= *weight,
313        }
314    }
315
316    pub fn can_upgrade_with(&self, authorization_keys: &BTreeSet<AccountHash>) -> bool {
317        let total_weight = self
318            .associated_keys
319            .calculate_keys_weight(authorization_keys);
320
321        match self
322            .action_thresholds
323            .get(&(Action::UpgradeManagement as u8))
324        {
325            None => false,
326            Some(weight) => total_weight >= *weight,
327        }
328    }
329
330    /// Extracts the access rights from the named keys of the addressable entity.
331    pub fn extract_access_rights(&self, hash_addr: HashAddr) -> ContextAccessRights {
332        match self.main_purse {
333            Some(purse) => {
334                let urefs_iter = self
335                    .named_keys
336                    .keys()
337                    .filter_map(|key| key.as_uref().copied())
338                    .chain(iter::once(purse));
339                ContextAccessRights::new(hash_addr, urefs_iter)
340            }
341            None => {
342                let urefs_iter = self
343                    .named_keys
344                    .keys()
345                    .filter_map(|key| key.as_uref().copied());
346                ContextAccessRights::new(hash_addr, urefs_iter)
347            }
348        }
349    }
350}