casper_storage/system/
runtime_native.rs

1use crate::{
2    global_state::{error::Error as GlobalStateReader, state::StateReader},
3    tracking_copy::{TrackingCopyEntityExt, TrackingCopyError, TrackingCopyExt},
4    AddressGenerator, TrackingCopy,
5};
6use casper_types::{
7    account::AccountHash, contracts::NamedKeys, Chainspec, ContextAccessRights, EntityAddr,
8    FeeHandling, Key, Phase, ProtocolVersion, PublicKey, RefundHandling, RuntimeFootprint,
9    StoredValue, TransactionHash, Transfer, URef, U512,
10};
11use num_rational::Ratio;
12use parking_lot::RwLock;
13use std::{cell::RefCell, collections::BTreeSet, rc::Rc, sync::Arc};
14use tracing::error;
15
16/// Configuration settings.
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
18pub struct Config {
19    transfer_config: TransferConfig,
20    fee_handling: FeeHandling,
21    refund_handling: RefundHandling,
22    vesting_schedule_period_millis: u64,
23    allow_auction_bids: bool,
24    compute_rewards: bool,
25    max_delegators_per_validator: u32,
26    minimum_bid_amount: u64,
27    minimum_delegation_amount: u64,
28    balance_hold_interval: u64,
29    include_credits: bool,
30    credit_cap: Ratio<U512>,
31    enable_addressable_entity: bool,
32    native_transfer_cost: u32,
33}
34
35impl Config {
36    /// Ctor.
37    #[allow(clippy::too_many_arguments)]
38    pub const fn new(
39        transfer_config: TransferConfig,
40        fee_handling: FeeHandling,
41        refund_handling: RefundHandling,
42        vesting_schedule_period_millis: u64,
43        allow_auction_bids: bool,
44        compute_rewards: bool,
45        max_delegators_per_validator: u32,
46        minimum_bid_amount: u64,
47        minimum_delegation_amount: u64,
48        balance_hold_interval: u64,
49        include_credits: bool,
50        credit_cap: Ratio<U512>,
51        enable_addressable_entity: bool,
52        native_transfer_cost: u32,
53    ) -> Self {
54        Config {
55            transfer_config,
56            fee_handling,
57            refund_handling,
58            vesting_schedule_period_millis,
59            allow_auction_bids,
60            compute_rewards,
61            max_delegators_per_validator,
62            minimum_bid_amount,
63            minimum_delegation_amount,
64            balance_hold_interval,
65            include_credits,
66            credit_cap,
67            enable_addressable_entity,
68            native_transfer_cost,
69        }
70    }
71
72    /// Ctor from chainspec.
73    pub fn from_chainspec(chainspec: &Chainspec) -> Self {
74        let transfer_config = TransferConfig::from_chainspec(chainspec);
75        let fee_handling = chainspec.core_config.fee_handling;
76        let refund_handling = chainspec.core_config.refund_handling;
77        let vesting_schedule_period_millis = chainspec.core_config.vesting_schedule_period.millis();
78        let allow_auction_bids = chainspec.core_config.allow_auction_bids;
79        let compute_rewards = chainspec.core_config.compute_rewards;
80        let max_delegators_per_validator = chainspec.core_config.max_delegators_per_validator;
81        let minimum_bid_amount = chainspec.core_config.minimum_bid_amount;
82        let minimum_delegation_amount = chainspec.core_config.minimum_delegation_amount;
83        let balance_hold_interval = chainspec.core_config.gas_hold_interval.millis();
84        let include_credits = chainspec.core_config.fee_handling == FeeHandling::NoFee;
85        let credit_cap = Ratio::new_raw(
86            U512::from(*chainspec.core_config.validator_credit_cap.numer()),
87            U512::from(*chainspec.core_config.validator_credit_cap.denom()),
88        );
89        let enable_addressable_entity = chainspec.core_config.enable_addressable_entity;
90        let native_transfer_cost = chainspec.system_costs_config.mint_costs().transfer;
91        Config::new(
92            transfer_config,
93            fee_handling,
94            refund_handling,
95            vesting_schedule_period_millis,
96            allow_auction_bids,
97            compute_rewards,
98            max_delegators_per_validator,
99            minimum_bid_amount,
100            minimum_delegation_amount,
101            balance_hold_interval,
102            include_credits,
103            credit_cap,
104            enable_addressable_entity,
105            native_transfer_cost,
106        )
107    }
108
109    /// Returns transfer config.
110    pub fn transfer_config(&self) -> &TransferConfig {
111        &self.transfer_config
112    }
113
114    /// Returns fee handling setting.
115    pub fn fee_handling(&self) -> &FeeHandling {
116        &self.fee_handling
117    }
118
119    /// Returns refund handling setting.
120    pub fn refund_handling(&self) -> &RefundHandling {
121        &self.refund_handling
122    }
123
124    /// Returns vesting schedule period millis setting.
125    pub fn vesting_schedule_period_millis(&self) -> u64 {
126        self.vesting_schedule_period_millis
127    }
128
129    /// Returns if auction bids are allowed.
130    pub fn allow_auction_bids(&self) -> bool {
131        self.allow_auction_bids
132    }
133
134    /// Returns if rewards should be computed.
135    pub fn compute_rewards(&self) -> bool {
136        self.compute_rewards
137    }
138
139    /// Returns max delegators per validator setting.
140    pub fn max_delegators_per_validator(&self) -> u32 {
141        self.max_delegators_per_validator
142    }
143
144    /// Returns minimum bid amount setting.
145    pub fn minimum_bid_amount(&self) -> u64 {
146        self.minimum_bid_amount
147    }
148
149    /// Returns minimum delegation amount setting.
150    pub fn minimum_delegation_amount(&self) -> u64 {
151        self.minimum_delegation_amount
152    }
153
154    /// Returns balance hold interval setting.
155    pub fn balance_hold_interval(&self) -> u64 {
156        self.balance_hold_interval
157    }
158
159    /// Returns include credit setting.
160    pub fn include_credits(&self) -> bool {
161        self.include_credits
162    }
163
164    /// Returns validator credit cap setting.
165    pub fn credit_cap(&self) -> Ratio<U512> {
166        self.credit_cap
167    }
168
169    /// Enable the addressable entity and migrate accounts/contracts to entities.
170    pub fn enable_addressable_entity(&self) -> bool {
171        self.enable_addressable_entity
172    }
173
174    /// Changes the transfer config.
175    pub fn set_transfer_config(self, transfer_config: TransferConfig) -> Self {
176        Config {
177            transfer_config,
178            fee_handling: self.fee_handling,
179            refund_handling: self.refund_handling,
180            vesting_schedule_period_millis: self.vesting_schedule_period_millis,
181            max_delegators_per_validator: self.max_delegators_per_validator,
182            allow_auction_bids: self.allow_auction_bids,
183            minimum_bid_amount: self.minimum_bid_amount,
184            minimum_delegation_amount: self.minimum_delegation_amount,
185            compute_rewards: self.compute_rewards,
186            balance_hold_interval: self.balance_hold_interval,
187            include_credits: self.include_credits,
188            credit_cap: self.credit_cap,
189            enable_addressable_entity: self.enable_addressable_entity,
190            native_transfer_cost: self.native_transfer_cost,
191        }
192    }
193}
194
195/// Configuration for transfer.
196#[derive(Debug, Clone, PartialEq, Eq, Default)]
197pub enum TransferConfig {
198    /// Transfers are affected by the existence of administrative_accounts. This is a
199    /// behavior specific to private or managed chains, not a public chain.
200    Administered {
201        /// Retrusn the set of account hashes for all administrators.
202        administrative_accounts: BTreeSet<AccountHash>,
203        /// If true, transfers are unrestricted.
204        /// If false, the source and / or target of a transfer must be an administrative account.
205        allow_unrestricted_transfers: bool,
206    },
207    /// Transfers are not affected by the existence of administrative_accounts (the standard
208    /// behavior).
209    #[default]
210    Unadministered,
211}
212
213impl TransferConfig {
214    /// Returns a new instance.
215    pub fn new(
216        administrative_accounts: BTreeSet<AccountHash>,
217        allow_unrestricted_transfers: bool,
218    ) -> Self {
219        if administrative_accounts.is_empty() && allow_unrestricted_transfers {
220            TransferConfig::Unadministered
221        } else {
222            TransferConfig::Administered {
223                administrative_accounts,
224                allow_unrestricted_transfers,
225            }
226        }
227    }
228
229    /// New instance from chainspec.
230    pub fn from_chainspec(chainspec: &Chainspec) -> Self {
231        let administrative_accounts: BTreeSet<AccountHash> = chainspec
232            .core_config
233            .administrators
234            .iter()
235            .map(|x| x.to_account_hash())
236            .collect();
237        let allow_unrestricted_transfers = chainspec.core_config.allow_unrestricted_transfers;
238        if administrative_accounts.is_empty() && allow_unrestricted_transfers {
239            TransferConfig::Unadministered
240        } else {
241            TransferConfig::Administered {
242                administrative_accounts,
243                allow_unrestricted_transfers,
244            }
245        }
246    }
247
248    /// Does account hash belong to an administrative account?
249    pub fn is_administrator(&self, account_hash: &AccountHash) -> bool {
250        match self {
251            TransferConfig::Administered {
252                administrative_accounts,
253                ..
254            } => administrative_accounts.contains(account_hash),
255            TransferConfig::Unadministered => false,
256        }
257    }
258
259    /// Administrative accounts, if any.
260    pub fn administrative_accounts(&self) -> BTreeSet<AccountHash> {
261        match self {
262            TransferConfig::Administered {
263                administrative_accounts,
264                ..
265            } => administrative_accounts.clone(),
266            TransferConfig::Unadministered => BTreeSet::default(),
267        }
268    }
269
270    /// Allow unrestricted transfers.
271    pub fn allow_unrestricted_transfers(&self) -> bool {
272        match self {
273            TransferConfig::Administered {
274                allow_unrestricted_transfers,
275                ..
276            } => *allow_unrestricted_transfers,
277            TransferConfig::Unadministered => true,
278        }
279    }
280
281    /// Restricted transfer should be enforced.
282    pub fn enforce_transfer_restrictions(&self, account_hash: &AccountHash) -> bool {
283        !self.allow_unrestricted_transfers() && !self.is_administrator(account_hash)
284    }
285}
286
287/// Id for runtime processing.
288pub enum Id {
289    /// Hash of current transaction.
290    Transaction(TransactionHash),
291    /// An arbitrary set of bytes to be used as a seed value.
292    Seed(Vec<u8>),
293}
294
295impl Id {
296    /// Ctor for id enum.
297    pub fn seed(&self) -> Vec<u8> {
298        match self {
299            Id::Transaction(hash) => hash.digest().into_vec(),
300            Id::Seed(bytes) => bytes.clone(),
301        }
302    }
303}
304
305/// State held by an instance of runtime native.
306pub struct RuntimeNative<S> {
307    config: Config,
308
309    id: Id,
310    address_generator: Arc<RwLock<AddressGenerator>>,
311    protocol_version: ProtocolVersion,
312
313    tracking_copy: Rc<RefCell<TrackingCopy<S>>>,
314    address: AccountHash,
315    context_key: Key,
316    runtime_footprint: RuntimeFootprint,
317    access_rights: ContextAccessRights,
318    remaining_spending_limit: U512,
319    transfers: Vec<Transfer>,
320    phase: Phase,
321}
322
323impl<S> RuntimeNative<S>
324where
325    S: StateReader<Key, StoredValue, Error = GlobalStateReader>,
326{
327    /// Ctor.
328    #[allow(clippy::too_many_arguments)]
329    pub fn new(
330        config: Config,
331        protocol_version: ProtocolVersion,
332        id: Id,
333        address_generator: Arc<RwLock<AddressGenerator>>,
334        tracking_copy: Rc<RefCell<TrackingCopy<S>>>,
335        address: AccountHash,
336        context_key: Key,
337        runtime_footprint: RuntimeFootprint,
338        access_rights: ContextAccessRights,
339        remaining_spending_limit: U512,
340        phase: Phase,
341    ) -> Self {
342        let transfers = vec![];
343        RuntimeNative {
344            config,
345
346            id,
347            address_generator,
348            protocol_version,
349
350            tracking_copy,
351            address,
352            context_key,
353            runtime_footprint,
354            access_rights,
355            remaining_spending_limit,
356            transfers,
357            phase,
358        }
359    }
360
361    /// Creates a runtime with elevated permissions for systemic behaviors.
362    pub fn new_system_runtime(
363        config: Config,
364        protocol_version: ProtocolVersion,
365        id: Id,
366        address_generator: Arc<RwLock<AddressGenerator>>,
367        tracking_copy: Rc<RefCell<TrackingCopy<S>>>,
368        phase: Phase,
369    ) -> Result<Self, TrackingCopyError> {
370        let transfers = vec![];
371        let (entity_addr, runtime_footprint, access_rights) = tracking_copy
372            .borrow_mut()
373            .system_entity_runtime_footprint(protocol_version)?;
374        let address = PublicKey::System.to_account_hash();
375        let context_key = if config.enable_addressable_entity {
376            Key::AddressableEntity(entity_addr)
377        } else {
378            Key::Hash(entity_addr.value())
379        };
380        let remaining_spending_limit = U512::MAX; // system has no spending limit
381        Ok(RuntimeNative {
382            config,
383            id,
384            address_generator,
385            protocol_version,
386
387            tracking_copy,
388            address,
389            context_key,
390            runtime_footprint,
391            access_rights,
392            remaining_spending_limit,
393            transfers,
394            phase,
395        })
396    }
397
398    /// Creates a runtime context for a system contract.
399    pub fn new_system_contract_runtime(
400        config: Config,
401        protocol_version: ProtocolVersion,
402        id: Id,
403        address_generator: Arc<RwLock<AddressGenerator>>,
404        tracking_copy: Rc<RefCell<TrackingCopy<S>>>,
405        phase: Phase,
406        name: &str,
407    ) -> Result<Self, TrackingCopyError> {
408        let transfers = vec![];
409
410        let system_entity_registry = tracking_copy.borrow().get_system_entity_registry()?;
411        let hash = match system_entity_registry.get(name).copied() {
412            Some(hash) => hash,
413            None => {
414                error!("unexpected failure; system contract {} not found", name);
415                return Err(TrackingCopyError::MissingSystemContractHash(
416                    name.to_string(),
417                ));
418            }
419        };
420        let context_key = if config.enable_addressable_entity {
421            Key::AddressableEntity(EntityAddr::System(hash))
422        } else {
423            Key::Hash(hash)
424        };
425        let runtime_footprint = tracking_copy
426            .borrow_mut()
427            .runtime_footprint_by_hash_addr(hash)?;
428        let access_rights = runtime_footprint.extract_access_rights(hash);
429        let address = PublicKey::System.to_account_hash();
430        let remaining_spending_limit = U512::MAX; // system has no spending limit
431        Ok(RuntimeNative {
432            config,
433            id,
434            address_generator,
435            protocol_version,
436
437            tracking_copy,
438            address,
439            context_key,
440            runtime_footprint,
441            access_rights,
442            remaining_spending_limit,
443            transfers,
444            phase,
445        })
446    }
447
448    /// Returns mutable reference to address generator.
449    pub fn address_generator(&mut self) -> Arc<RwLock<AddressGenerator>> {
450        Arc::clone(&self.address_generator)
451    }
452
453    /// Returns reference to config.
454    pub fn config(&self) -> &Config {
455        &self.config
456    }
457
458    /// Returns reference to transfer config.
459    pub fn transfer_config(&self) -> &TransferConfig {
460        &self.config.transfer_config
461    }
462
463    /// Returns protocol version.
464    pub fn protocol_version(&self) -> ProtocolVersion {
465        self.protocol_version
466    }
467
468    /// Returns handle to tracking copy.
469    pub fn tracking_copy(&self) -> Rc<RefCell<TrackingCopy<S>>> {
470        Rc::clone(&self.tracking_copy)
471    }
472
473    /// Returns account hash being used by this instance.
474    pub fn address(&self) -> AccountHash {
475        self.address
476    }
477
478    /// Changes the account hash being used by this instance.
479    pub fn with_address(&mut self, account_hash: AccountHash) {
480        self.address = account_hash;
481    }
482
483    /// Returns the context key being used by this instance.
484    pub fn context_key(&self) -> &Key {
485        &self.context_key
486    }
487
488    /// Returns a reference to the runtime footprint used by this instance.
489    pub fn runtime_footprint(&self) -> &RuntimeFootprint {
490        &self.runtime_footprint
491    }
492
493    /// Returns the addressable entity being used by this instance.
494    pub fn runtime_footprint_mut(&mut self) -> &mut RuntimeFootprint {
495        &mut self.runtime_footprint
496    }
497
498    /// Changes the addressable entity being used by this instance.
499    pub fn with_addressable_entity(&mut self, runtime_footprint: RuntimeFootprint) {
500        self.runtime_footprint = runtime_footprint;
501    }
502
503    /// Returns a reference to the named keys being used by this instance.
504    pub fn named_keys(&self) -> &NamedKeys {
505        self.runtime_footprint().named_keys()
506    }
507
508    /// Returns a mutable reference to the named keys being used by this instance.
509    pub fn named_keys_mut(&mut self) -> &mut NamedKeys {
510        self.runtime_footprint.named_keys_mut()
511    }
512
513    /// Returns a reference to the access rights being used by this instance.
514    pub fn access_rights(&self) -> &ContextAccessRights {
515        &self.access_rights
516    }
517
518    /// Returns a mutable reference to the access rights being used by this instance.
519    pub fn access_rights_mut(&mut self) -> &mut ContextAccessRights {
520        &mut self.access_rights
521    }
522
523    /// Extends the access rights being used by this instance.
524    pub fn extend_access_rights(&mut self, urefs: &[URef]) {
525        self.access_rights.extend(urefs)
526    }
527
528    /// Returns the remaining spending limit.
529    pub fn remaining_spending_limit(&self) -> U512 {
530        self.remaining_spending_limit
531    }
532
533    /// Set remaining spending limit.
534    pub fn set_remaining_spending_limit(&mut self, remaining: U512) {
535        self.remaining_spending_limit = remaining;
536    }
537
538    /// Get references to transfers.
539    pub fn transfers(&self) -> &Vec<Transfer> {
540        &self.transfers
541    }
542
543    /// Push transfer instance.
544    pub fn push_transfer(&mut self, transfer: Transfer) {
545        self.transfers.push(transfer);
546    }
547
548    /// Get id.
549    pub fn id(&self) -> &Id {
550        &self.id
551    }
552
553    /// Get phase.
554    pub fn phase(&self) -> Phase {
555        self.phase
556    }
557
558    /// Vesting schedule period in milliseconds.
559    pub fn vesting_schedule_period_millis(&self) -> u64 {
560        self.config.vesting_schedule_period_millis
561    }
562
563    /// Are auction bids allowed?
564    pub fn allow_auction_bids(&self) -> bool {
565        self.config.allow_auction_bids
566    }
567
568    /// Are rewards computed?
569    pub fn compute_rewards(&self) -> bool {
570        self.config.compute_rewards
571    }
572
573    /// Extracts transfer items.
574    pub fn into_transfers(self) -> Vec<Transfer> {
575        self.transfers
576    }
577
578    pub(crate) fn native_transfer_cost(&self) -> u32 {
579        self.config.native_transfer_cost
580    }
581}