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#[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 Hash(HashAddr),
27 StoredContract {
29 hash_addr: HashAddr,
31 package_hash_addr: HashAddr,
33 wasm_hash_addr: HashAddr,
35 protocol_version: ProtocolVersion,
37 },
38}
39
40impl RuntimeAddress {
41 pub fn new_hash(hash_addr: HashAddr) -> Self {
43 Self::Hash(hash_addr)
44 }
45
46 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 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 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 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 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 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}