use tracing::error;
use crate::{
global_state::{error::Error as GlobalStateError, state::StateReader},
system::{
error::ProviderError,
mint::{
runtime_provider::RuntimeProvider, storage_provider::StorageProvider,
system_provider::SystemProvider, Mint,
},
runtime_native::{Id, RuntimeNative},
},
tracking_copy::{TrackingCopyEntityExt, TrackingCopyExt},
};
use casper_types::{
account::AccountHash,
bytesrepr::{FromBytes, ToBytes},
system::{mint::Error, Caller},
AccessRights, CLTyped, CLValue, Gas, InitiatorAddr, Key, Phase, PublicKey, RuntimeFootprint,
StoredValue, SystemHashRegistry, Transfer, TransferV2, URef, U512,
};
impl<S> RuntimeProvider for RuntimeNative<S>
where
S: StateReader<Key, StoredValue, Error = GlobalStateError>,
{
fn get_caller(&self) -> AccountHash {
self.address()
}
fn get_immediate_caller(&self) -> Option<Caller> {
let caller = Caller::Initiator {
account_hash: PublicKey::System.to_account_hash(),
};
Some(caller)
}
fn is_called_from_standard_payment(&self) -> bool {
self.phase() == Phase::Payment
}
fn get_system_entity_registry(&self) -> Result<SystemHashRegistry, ProviderError> {
self.tracking_copy()
.borrow_mut()
.get_system_entity_registry()
.map_err(|tce| {
error!(%tce, "unable to obtain system entity registry during transfer");
ProviderError::SystemEntityRegistry
})
}
fn runtime_footprint_by_account_hash(
&mut self,
account_hash: AccountHash,
) -> Result<Option<RuntimeFootprint>, ProviderError> {
match self
.tracking_copy()
.borrow_mut()
.runtime_footprint_by_account_hash(self.protocol_version(), account_hash)
{
Ok((_, footprint)) => Ok(Some(footprint)),
Err(tce) => {
error!(%tce, "error reading addressable entity by account hash");
Err(ProviderError::AccountHash(account_hash))
}
}
}
fn get_phase(&self) -> Phase {
self.phase()
}
fn get_key(&self, name: &str) -> Option<Key> {
self.named_keys().get(name).cloned()
}
fn get_approved_spending_limit(&self) -> U512 {
self.remaining_spending_limit()
}
fn sub_approved_spending_limit(&mut self, amount: U512) {
if let Some(remaining) = self.remaining_spending_limit().checked_sub(amount) {
self.set_remaining_spending_limit(remaining);
} else {
error!(
limit = %self.remaining_spending_limit(),
spent = %amount,
"exceeded main purse spending limit"
);
self.set_remaining_spending_limit(U512::zero());
}
}
fn get_main_purse(&self) -> Option<URef> {
self.runtime_footprint().main_purse()
}
fn is_administrator(&self, account_hash: &AccountHash) -> bool {
self.transfer_config().is_administrator(account_hash)
}
fn allow_unrestricted_transfers(&self) -> bool {
self.transfer_config().allow_unrestricted_transfers()
}
fn is_valid_uref(&self, uref: &URef) -> bool {
self.access_rights().has_access_rights_to_uref(uref)
}
}
impl<S> StorageProvider for RuntimeNative<S>
where
S: StateReader<Key, StoredValue, Error = GlobalStateError>,
{
fn new_uref<T: CLTyped + ToBytes>(&mut self, value: T) -> Result<URef, Error> {
let cl_value: CLValue = CLValue::from_t(value).map_err(|_| Error::CLValue)?;
let uref = self
.address_generator()
.write()
.new_uref(AccessRights::READ_ADD_WRITE);
self.extend_access_rights(&[uref]);
self.tracking_copy()
.borrow_mut()
.write(Key::URef(uref), StoredValue::CLValue(cl_value));
Ok(uref)
}
fn read<T: CLTyped + FromBytes>(&mut self, uref: URef) -> Result<Option<T>, Error> {
if !self.access_rights().has_access_rights_to_uref(&uref) {
return Err(Error::ForgedReference);
}
let key = &Key::URef(uref);
let stored_value = match self.tracking_copy().borrow_mut().read(key) {
Ok(Some(stored_value)) => stored_value,
Ok(None) => return Ok(None),
Err(_) => return Err(Error::Storage),
};
if let StoredValue::CLValue(value) = stored_value {
let value = CLValue::into_t(value).map_err(|_| Error::CLValue)?;
Ok(Some(value))
} else {
Err(Error::CLValue)
}
}
fn write_amount(&mut self, uref: URef, amount: U512) -> Result<(), Error> {
let cl_value = CLValue::from_t(amount).map_err(|_| Error::CLValue)?;
if !uref.is_writeable() {
return Err(Error::Storage);
}
if !self.access_rights().has_access_rights_to_uref(&uref) {
return Err(Error::ForgedReference);
}
self.tracking_copy()
.borrow_mut()
.write(Key::URef(uref), StoredValue::CLValue(cl_value));
Ok(())
}
fn add<T: CLTyped + ToBytes>(&mut self, uref: URef, value: T) -> Result<(), Error> {
let cl_value = CLValue::from_t(value).map_err(|_| Error::CLValue)?;
self.tracking_copy()
.borrow_mut()
.add(Key::URef(uref), StoredValue::CLValue(cl_value))
.map_err(|_| Error::Storage)?;
Ok(())
}
fn total_balance(&mut self, purse: URef) -> Result<U512, Error> {
match self
.tracking_copy()
.borrow_mut()
.get_total_balance(purse.into())
{
Ok(total) => Ok(total.value()),
Err(err) => {
error!(?err, "mint native total_balance");
dbg!(&err);
Err(Error::Storage)
}
}
}
fn available_balance(&mut self, purse: URef) -> Result<Option<U512>, Error> {
match self
.tracking_copy()
.borrow_mut()
.get_available_balance(Key::Balance(purse.addr()))
{
Ok(motes) => Ok(Some(motes.value())),
Err(err) => {
error!(?err, "mint native available_balance");
Err(Error::Storage)
}
}
}
fn write_balance(&mut self, uref: URef, balance: U512) -> Result<(), Error> {
let cl_value = CLValue::from_t(balance).map_err(|_| Error::CLValue)?;
self.tracking_copy()
.borrow_mut()
.write(Key::Balance(uref.addr()), StoredValue::CLValue(cl_value));
Ok(())
}
fn add_balance(&mut self, uref: URef, value: U512) -> Result<(), Error> {
let cl_value = CLValue::from_t(value).map_err(|_| Error::CLValue)?;
self.tracking_copy()
.borrow_mut()
.add(Key::Balance(uref.addr()), StoredValue::CLValue(cl_value))
.map_err(|_| Error::Storage)?;
Ok(())
}
}
impl<S> SystemProvider for RuntimeNative<S>
where
S: StateReader<Key, StoredValue, Error = GlobalStateError>,
{
fn record_transfer(
&mut self,
maybe_to: Option<AccountHash>,
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<(), Error> {
if self.phase() != Phase::Session {
return Ok(());
}
let txn_hash = match self.id() {
Id::Transaction(txn_hash) => *txn_hash,
Id::Seed(_) => return Ok(()),
};
let from = InitiatorAddr::AccountHash(self.get_caller());
let fee = Gas::from(self.native_transfer_cost());
let transfer = Transfer::V2(TransferV2::new(
txn_hash, from, maybe_to, source, target, amount, fee, id,
));
self.push_transfer(transfer);
Ok(())
}
}
impl<S> Mint for RuntimeNative<S>
where
S: StateReader<Key, StoredValue, Error = GlobalStateError>,
{
fn purse_exists(&mut self, uref: URef) -> Result<bool, Error> {
let key = Key::Balance(uref.addr());
match self
.tracking_copy()
.borrow_mut()
.read(&key)
.map_err(|_| Error::Storage)?
{
Some(StoredValue::CLValue(value)) => Ok(*value.cl_type() == U512::cl_type()),
Some(_non_cl_value) => Err(Error::CLValue),
None => Ok(false),
}
}
}