casper_execution_engine/runtime/
auction_internal.rs

1use std::collections::BTreeSet;
2use tracing::{debug, error};
3
4use casper_storage::{
5    global_state::{error::Error as GlobalStateError, state::StateReader},
6    system::{
7        auction::{
8            providers::{AccountProvider, MintProvider, RuntimeProvider, StorageProvider},
9            Auction,
10        },
11        mint::Mint,
12    },
13};
14use casper_types::{
15    account::AccountHash,
16    bytesrepr::{FromBytes, ToBytes},
17    system::{
18        auction::{BidAddr, BidKind, EraInfo, Error, Unbond, UnbondEra, UnbondKind},
19        mint,
20    },
21    AccessRights, CLTyped, CLValue, Key, KeyTag, PublicKey, RuntimeArgs, StoredValue, URef, U512,
22};
23
24use super::Runtime;
25use crate::execution::ExecError;
26
27impl From<ExecError> for Option<Error> {
28    fn from(exec_error: ExecError) -> Self {
29        match exec_error {
30            // This is used to propagate [`execution::Error::GasLimit`] to make sure [`Auction`]
31            // contract running natively supports propagating gas limit errors without a panic.
32            ExecError::GasLimit => Some(Error::GasLimit),
33            // There are possibly other exec errors happening but such translation would be lossy.
34            _ => None,
35        }
36    }
37}
38
39impl<R> StorageProvider for Runtime<'_, R>
40where
41    R: StateReader<Key, StoredValue, Error = GlobalStateError>,
42{
43    fn read<T: FromBytes + CLTyped>(&mut self, uref: URef) -> Result<Option<T>, Error> {
44        match self.context.read_gs(&uref.into()) {
45            Ok(Some(StoredValue::CLValue(cl_value))) => {
46                Ok(Some(cl_value.into_t().map_err(|_| Error::CLValue)?))
47            }
48            Ok(Some(_)) => {
49                error!("StorageProvider::read: unexpected StoredValue variant");
50                Err(Error::Storage)
51            }
52            Ok(None) => Ok(None),
53            Err(ExecError::BytesRepr(_)) => Err(Error::Serialization),
54            // NOTE: This extra condition is needed to correctly propagate GasLimit to the user. See
55            // also [`Runtime::reverter`] and [`to_auction_error`]
56            Err(ExecError::GasLimit) => Err(Error::GasLimit),
57            Err(err) => {
58                error!("StorageProvider::read: {:?}", err);
59                Err(Error::Storage)
60            }
61        }
62    }
63
64    fn write<T: ToBytes + CLTyped>(&mut self, uref: URef, value: T) -> Result<(), Error> {
65        let cl_value = CLValue::from_t(value).map_err(|_| Error::CLValue)?;
66        self.context
67            .metered_write_gs(uref.into(), StoredValue::CLValue(cl_value))
68            .map_err(|exec_error| {
69                error!("StorageProvider::write: {:?}", exec_error);
70                <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
71            })
72    }
73
74    fn read_bid(&mut self, key: &Key) -> Result<Option<BidKind>, Error> {
75        match self.context.read_gs(key) {
76            Ok(Some(StoredValue::BidKind(bid_kind))) => Ok(Some(bid_kind)),
77            Ok(Some(_)) => {
78                error!("StorageProvider::read_bid: unexpected StoredValue variant");
79                Err(Error::Storage)
80            }
81            Ok(None) => Ok(None),
82            Err(ExecError::BytesRepr(_)) => Err(Error::Serialization),
83            // NOTE: This extra condition is needed to correctly propagate GasLimit to the user. See
84            // also [`Runtime::reverter`] and [`to_auction_error`]
85            Err(ExecError::GasLimit) => Err(Error::GasLimit),
86            Err(err) => {
87                error!("StorageProvider::read_bid: {:?}", err);
88                Err(Error::Storage)
89            }
90        }
91    }
92
93    fn write_bid(&mut self, key: Key, bid_kind: BidKind) -> Result<(), Error> {
94        self.context
95            .metered_write_gs_unsafe(key, StoredValue::BidKind(bid_kind))
96            .map_err(|exec_error| {
97                error!("StorageProvider::write_bid: {:?}", exec_error);
98                <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
99            })
100    }
101
102    fn read_unbond(&mut self, bid_addr: BidAddr) -> Result<Option<Unbond>, Error> {
103        match self.context.read_gs(&Key::BidAddr(bid_addr)) {
104            Ok(Some(StoredValue::BidKind(BidKind::Unbond(unbonds)))) => Ok(Some(*unbonds)),
105            Ok(Some(_)) => {
106                error!("StorageProvider::read_unbonds: unexpected StoredValue variant");
107                Err(Error::Storage)
108            }
109            Ok(None) => Ok(None),
110            Err(ExecError::BytesRepr(_)) => Err(Error::Serialization),
111            // NOTE: This extra condition is needed to correctly propagate GasLimit to the user. See
112            // also [`Runtime::reverter`] and [`to_auction_error`]
113            Err(ExecError::GasLimit) => Err(Error::GasLimit),
114            Err(err) => {
115                error!("StorageProvider::read_unbonds: {:?}", err);
116                Err(Error::Storage)
117            }
118        }
119    }
120
121    fn write_unbond(&mut self, bid_addr: BidAddr, unbond: Option<Unbond>) -> Result<(), Error> {
122        let unbond_key = Key::BidAddr(bid_addr);
123        match unbond {
124            Some(unbond) => self
125                .context
126                .metered_write_gs_unsafe(
127                    unbond_key,
128                    StoredValue::BidKind(BidKind::Unbond(Box::new(unbond))),
129                )
130                .map_err(|exec_error| {
131                    error!("StorageProvider::write_unbond: {:?}", exec_error);
132                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
133                }),
134            None => {
135                self.context.prune_gs_unsafe(unbond_key);
136                Ok(())
137            }
138        }
139    }
140
141    fn record_era_info(&mut self, era_info: EraInfo) -> Result<(), Error> {
142        Runtime::record_era_info(self, era_info)
143            .map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::RecordEraInfo))
144    }
145
146    fn prune_bid(&mut self, bid_addr: BidAddr) {
147        Runtime::prune(self, bid_addr.into());
148    }
149}
150
151impl<R> RuntimeProvider for Runtime<'_, R>
152where
153    R: StateReader<Key, StoredValue, Error = GlobalStateError>,
154{
155    fn get_caller(&self) -> AccountHash {
156        self.context.get_initiator()
157    }
158
159    fn is_allowed_session_caller(&self, account_hash: &AccountHash) -> bool {
160        Runtime::is_allowed_session_caller(self, account_hash)
161    }
162
163    fn is_valid_uref(&self, uref: URef) -> bool {
164        self.context.validate_uref(&uref).is_ok()
165    }
166
167    fn named_keys_get(&self, name: &str) -> Option<Key> {
168        self.context.named_keys_get(name).cloned()
169    }
170
171    fn get_keys(&mut self, key_tag: &KeyTag) -> Result<BTreeSet<Key>, Error> {
172        self.context.get_keys(key_tag).map_err(|err| {
173            error!(%key_tag, "RuntimeProvider::get_keys: {:?}", err);
174            Error::Storage
175        })
176    }
177
178    fn get_keys_by_prefix(&mut self, prefix: &[u8]) -> Result<Vec<Key>, Error> {
179        self.context
180            .get_keys_with_prefix(prefix)
181            .map_err(|exec_error| {
182                error!("RuntimeProvider::get_keys_by_prefix: {:?}", exec_error);
183                <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
184            })
185    }
186
187    fn delegator_count(&mut self, bid_addr: &BidAddr) -> Result<usize, Error> {
188        let delegated_accounts = {
189            let prefix = bid_addr.delegated_account_prefix()?;
190            let keys = self
191                .context
192                .get_keys_with_prefix(&prefix)
193                .map_err(|exec_error| {
194                    error!("RuntimeProvider::delegator_count accounts {:?}", exec_error);
195                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
196                })?;
197            keys.len()
198        };
199        let delegated_purses = {
200            let prefix = bid_addr.delegated_purse_prefix()?;
201            let keys = self
202                .context
203                .get_keys_with_prefix(&prefix)
204                .map_err(|exec_error| {
205                    error!("RuntimeProvider::delegator_count purses {:?}", exec_error);
206                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
207                })?;
208            keys.len()
209        };
210        Ok(delegated_accounts.saturating_add(delegated_purses))
211    }
212
213    fn reservation_count(&mut self, bid_addr: &BidAddr) -> Result<usize, Error> {
214        let reserved_accounts = {
215            let reservation_prefix = bid_addr.reserved_account_prefix()?;
216            let reservation_keys = self
217                .context
218                .get_keys_with_prefix(&reservation_prefix)
219                .map_err(|exec_error| {
220                    error!("RuntimeProvider::reservation_count {:?}", exec_error);
221                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
222                })?;
223            reservation_keys.len()
224        };
225        let reserved_purses = {
226            let reservation_prefix = bid_addr.reserved_purse_prefix()?;
227            let reservation_keys = self
228                .context
229                .get_keys_with_prefix(&reservation_prefix)
230                .map_err(|exec_error| {
231                    error!("RuntimeProvider::reservation_count {:?}", exec_error);
232                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
233                })?;
234            reservation_keys.len()
235        };
236        Ok(reserved_accounts.saturating_add(reserved_purses))
237    }
238
239    fn used_reservation_count(&mut self, bid_addr: &BidAddr) -> Result<usize, Error> {
240        let reservation_account_prefix = bid_addr.reserved_account_prefix()?;
241        let reservation_purse_prefix = bid_addr.reserved_purse_prefix()?;
242
243        let reservation_keys = {
244            let mut ret = self
245                .context
246                .get_keys_with_prefix(&reservation_account_prefix)
247                .map_err(|exec_error| {
248                    error!("RuntimeProvider::reservation_count {:?}", exec_error);
249                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
250                })?;
251            let purses = self
252                .context
253                .get_keys_with_prefix(&reservation_purse_prefix)
254                .map_err(|exec_error| {
255                    error!("RuntimeProvider::reservation_count {:?}", exec_error);
256                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
257                })?;
258            ret.extend(purses);
259            ret
260        };
261
262        let mut used = 0;
263        for reservation_key in reservation_keys {
264            if let Key::BidAddr(BidAddr::ReservedDelegationAccount {
265                validator,
266                delegator,
267            }) = reservation_key
268            {
269                let key_to_check = Key::BidAddr(BidAddr::DelegatedAccount {
270                    validator,
271                    delegator,
272                });
273                if let Ok(Some(_)) = self.context.read_gs(&key_to_check) {
274                    used += 1;
275                }
276            }
277            if let Key::BidAddr(BidAddr::ReservedDelegationPurse {
278                validator,
279                delegator,
280            }) = reservation_key
281            {
282                let key_to_check = Key::BidAddr(BidAddr::DelegatedPurse {
283                    validator,
284                    delegator,
285                });
286                if let Ok(Some(_)) = self.context.read_gs(&key_to_check) {
287                    used += 1;
288                }
289            }
290        }
291        Ok(used)
292    }
293
294    fn vesting_schedule_period_millis(&self) -> u64 {
295        self.context
296            .engine_config()
297            .vesting_schedule_period_millis()
298    }
299
300    fn allow_auction_bids(&self) -> bool {
301        self.context.engine_config().allow_auction_bids()
302    }
303
304    fn should_compute_rewards(&self) -> bool {
305        self.context.engine_config().compute_rewards()
306    }
307}
308
309impl<R> MintProvider for Runtime<'_, R>
310where
311    R: StateReader<Key, StoredValue, Error = GlobalStateError>,
312{
313    fn unbond(&mut self, unbond_kind: &UnbondKind, unbond_era: &UnbondEra) -> Result<(), Error> {
314        let is_delegator = unbond_kind.is_delegator();
315        let (purse, maybe_account_hash) = match unbond_kind {
316            UnbondKind::Validator(pk) | UnbondKind::DelegatedPublicKey(pk) => {
317                let account_hash = pk.to_account_hash();
318                let maybe_value = self
319                    .context
320                    .read_gs_unsafe(&Key::Account(account_hash))
321                    .map_err(|exec_error| {
322                        error!("MintProvider::unbond: {:?}", exec_error);
323                        <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
324                    })?;
325
326                match maybe_value {
327                    Some(StoredValue::Account(account)) => {
328                        (account.main_purse(), Some(account_hash))
329                    }
330                    Some(StoredValue::CLValue(cl_value)) => {
331                        let entity_key: Key = cl_value.into_t().map_err(|_| Error::CLValue)?;
332                        match self.context.read_gs_unsafe(&entity_key) {
333                            Ok(Some(StoredValue::AddressableEntity(entity))) => {
334                                (entity.main_purse(), Some(account_hash))
335                            }
336                            Ok(Some(StoredValue::CLValue(_))) => {
337                                return Err(Error::CLValue);
338                            }
339                            Ok(Some(_)) => {
340                                return if is_delegator {
341                                    Err(Error::DelegatorNotFound)
342                                } else {
343                                    Err(Error::ValidatorNotFound)
344                                }
345                            }
346                            Ok(None) => {
347                                return Err(Error::InvalidPublicKey);
348                            }
349                            Err(exec_error) => {
350                                error!("MintProvider::unbond: {:?}", exec_error);
351                                return Err(
352                                    <Option<Error>>::from(exec_error).unwrap_or(Error::Storage)
353                                );
354                            }
355                        }
356                    }
357                    Some(_) => return Err(Error::UnexpectedStoredValueVariant),
358                    None => return Err(Error::InvalidPublicKey),
359                }
360            }
361            UnbondKind::DelegatedPurse(addr) => {
362                let purse = URef::new(*addr, AccessRights::READ_ADD_WRITE);
363                match self.balance(purse) {
364                    Ok(Some(_)) => (purse, None),
365                    Ok(None) => return Err(Error::MissingPurse),
366                    Err(err) => {
367                        error!("MintProvider::unbond delegated purse: {:?}", err);
368                        return Err(Error::MintError);
369                    }
370                }
371            }
372        };
373
374        self.mint_transfer_direct(
375            maybe_account_hash,
376            *unbond_era.bonding_purse(),
377            purse,
378            *unbond_era.amount(),
379            None,
380        )
381        .map_err(|_| Error::Transfer)?
382        .map_err(|_| Error::Transfer)?;
383        Ok(())
384    }
385
386    /// Allows optimized auction and mint interaction.
387    /// Intended to be used only by system contracts to manage staked purses.
388    /// NOTE: Never expose this through FFI.
389    fn mint_transfer_direct(
390        &mut self,
391        to: Option<AccountHash>,
392        source: URef,
393        target: URef,
394        amount: U512,
395        id: Option<u64>,
396    ) -> Result<Result<(), mint::Error>, Error> {
397        let is_main_purse_transfer = self
398            .context
399            .runtime_footprint()
400            .borrow()
401            .main_purse()
402            .expect("didnt have purse")
403            .addr()
404            == source.addr();
405        let has_perms = is_main_purse_transfer
406            || (source.is_writeable() && self.context.validate_uref(&source).is_ok());
407        if !(has_perms || self.context.get_initiator() == PublicKey::System.to_account_hash()) {
408            return Err(Error::InvalidCaller);
409        }
410
411        let args_values = RuntimeArgs::try_new(|args| {
412            args.insert(mint::ARG_TO, to)?;
413            args.insert(mint::ARG_SOURCE, source)?;
414            args.insert(mint::ARG_TARGET, target)?;
415            args.insert(mint::ARG_AMOUNT, amount)?;
416            args.insert(mint::ARG_ID, id)?;
417            Ok(())
418        })
419        .map_err(|_| Error::CLValue)?;
420
421        let gas_counter = self.gas_counter();
422
423        self.context
424            .access_rights_extend(&[source, target.into_add()]);
425
426        let mint_hash = self.get_mint_hash().map_err(|exec_error| {
427            <Option<Error>>::from(exec_error).unwrap_or(Error::MissingValue)
428        })?;
429
430        let cl_value = self
431            .call_contract(mint_hash, mint::METHOD_TRANSFER, args_values)
432            .map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::Transfer))?;
433
434        self.set_gas_counter(gas_counter);
435        cl_value.into_t().map_err(|_| Error::CLValue)
436    }
437
438    fn mint_into_existing_purse(
439        &mut self,
440        amount: U512,
441        existing_purse: URef,
442    ) -> Result<(), Error> {
443        if self.context.get_initiator() != PublicKey::System.to_account_hash() {
444            return Err(Error::InvalidCaller);
445        }
446
447        let args_values = RuntimeArgs::try_new(|args| {
448            args.insert(mint::ARG_AMOUNT, amount)?;
449            args.insert(mint::ARG_PURSE, existing_purse)?;
450            Ok(())
451        })
452        .map_err(|_| Error::CLValue)?;
453
454        let gas_counter = self.gas_counter();
455
456        let mint_hash = self.get_mint_hash().map_err(|exec_error| {
457            <Option<Error>>::from(exec_error).unwrap_or(Error::MissingValue)
458        })?;
459
460        let cl_value = self
461            .call_contract(
462                mint_hash,
463                mint::METHOD_MINT_INTO_EXISTING_PURSE,
464                args_values,
465            )
466            .map_err(|error| <Option<Error>>::from(error).unwrap_or(Error::MintError))?;
467        self.set_gas_counter(gas_counter);
468        cl_value
469            .into_t::<Result<(), mint::Error>>()
470            .map_err(|_| Error::CLValue)?
471            .map_err(|_| Error::MintError)
472    }
473
474    fn create_purse(&mut self) -> Result<URef, Error> {
475        Runtime::create_purse(self).map_err(|exec_error| {
476            <Option<Error>>::from(exec_error).unwrap_or(Error::CreatePurseFailed)
477        })
478    }
479
480    fn available_balance(&mut self, purse: URef) -> Result<Option<U512>, Error> {
481        Runtime::available_balance(self, purse)
482            .map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::GetBalance))
483    }
484
485    fn read_base_round_reward(&mut self) -> Result<U512, Error> {
486        let mint_hash = self.get_mint_hash().map_err(|exec_error| {
487            <Option<Error>>::from(exec_error).unwrap_or(Error::MissingValue)
488        })?;
489        self.mint_read_base_round_reward(mint_hash)
490            .map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::MintReward))
491    }
492
493    fn mint(&mut self, amount: U512) -> Result<URef, Error> {
494        let mint_hash = self.get_mint_hash().map_err(|exec_error| {
495            <Option<Error>>::from(exec_error).unwrap_or(Error::MissingValue)
496        })?;
497        self.mint_mint(mint_hash, amount)
498            .map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::MintError))
499    }
500
501    fn reduce_total_supply(&mut self, amount: U512) -> Result<(), Error> {
502        let mint_hash = self.get_mint_hash().map_err(|exec_error| {
503            <Option<Error>>::from(exec_error).unwrap_or(Error::MissingValue)
504        })?;
505        self.mint_reduce_total_supply(mint_hash, amount)
506            .map_err(|exec_error| {
507                <Option<Error>>::from(exec_error).unwrap_or(Error::MintReduceTotalSupply)
508            })
509    }
510}
511
512impl<R> AccountProvider for Runtime<'_, R>
513where
514    R: StateReader<Key, StoredValue, Error = GlobalStateError>,
515{
516    fn get_main_purse(&self) -> Result<URef, Error> {
517        // NOTE: this is used by the system and is not (and should not be made to be) accessible
518        // from userland.
519        match Runtime::context(self)
520            .runtime_footprint()
521            .borrow()
522            .main_purse()
523        {
524            None => {
525                debug!("runtime attempt to access non-existent main purse");
526                Err(Error::InvalidContext)
527            }
528            Some(purse) => Ok(purse),
529        }
530    }
531
532    /// Set main purse.
533    fn set_main_purse(&mut self, purse: URef) {
534        Runtime::context(self)
535            .runtime_footprint()
536            .borrow_mut()
537            .set_main_purse(purse);
538    }
539}
540
541impl<R> Auction for Runtime<'_, R> where R: StateReader<Key, StoredValue, Error = GlobalStateError> {}