use std::{
cell::RefCell,
collections::BTreeSet,
convert::{TryFrom, TryInto},
fmt::Debug,
rc::Rc,
};
use tracing::error;
use casper_types::{
account::{
Account, AccountHash, ActionType, AddKeyFailure, RemoveKeyFailure, SetThresholdFailure,
UpdateKeyFailure, Weight,
},
bytesrepr::ToBytes,
contracts::NamedKeys,
system::auction::EraInfo,
AccessRights, BlockTime, CLType, CLValue, ContextAccessRights, Contract, ContractHash,
ContractPackage, ContractPackageHash, DeployHash, DeployInfo, EntryPointAccess, EntryPointType,
Gas, GrantedAccess, Key, KeyTag, Phase, ProtocolVersion, PublicKey, RuntimeArgs, StoredValue,
Transfer, TransferAddr, URef, URefAddr, DICTIONARY_ITEM_KEY_MAX_LENGTH, KEY_HASH_LENGTH, U512,
};
use crate::{
core::{
engine_state::{execution_effect::ExecutionEffect, EngineConfig, SystemContractRegistry},
execution::{AddressGenerator, Error},
runtime_context::dictionary::DictionaryValue,
tracking_copy::{AddResult, TrackingCopy, TrackingCopyExt},
},
shared::{execution_journal::ExecutionJournal, newtypes::CorrelationId},
storage::global_state::StateReader,
};
pub(crate) mod dictionary;
#[cfg(test)]
mod tests;
pub const RANDOM_BYTES_COUNT: usize = 32;
pub fn validate_group_membership(
contract_package: &ContractPackage,
access: &EntryPointAccess,
validator: impl Fn(&URef) -> bool,
) -> Result<(), Error> {
if let EntryPointAccess::Groups(groups) = access {
if groups.is_empty() {
return Err(Error::InvalidContext);
}
let find_result = groups.iter().find(|g| {
contract_package
.groups()
.get(g)
.and_then(|set| set.iter().find(|u| validator(u)))
.is_some()
});
if find_result.is_none() {
return Err(Error::InvalidContext);
}
}
Ok(())
}
pub struct RuntimeContext<'a, R> {
tracking_copy: Rc<RefCell<TrackingCopy<R>>>,
named_keys: &'a mut NamedKeys,
access_rights: ContextAccessRights,
account: &'a Account,
args: RuntimeArgs,
authorization_keys: BTreeSet<AccountHash>,
base_key: Key,
blocktime: BlockTime,
deploy_hash: DeployHash,
gas_limit: Gas,
gas_counter: Gas,
address_generator: Rc<RefCell<AddressGenerator>>,
protocol_version: ProtocolVersion,
correlation_id: CorrelationId,
phase: Phase,
engine_config: EngineConfig,
entry_point_type: EntryPointType,
transfers: Vec<TransferAddr>,
remaining_spending_limit: U512,
}
impl<'a, R> RuntimeContext<'a, R>
where
R: StateReader<Key, StoredValue>,
R::Error: Into<Error>,
{
#[allow(clippy::too_many_arguments)]
pub fn new(
tracking_copy: Rc<RefCell<TrackingCopy<R>>>,
entry_point_type: EntryPointType,
named_keys: &'a mut NamedKeys,
access_rights: ContextAccessRights,
runtime_args: RuntimeArgs,
authorization_keys: BTreeSet<AccountHash>,
account: &'a Account,
base_key: Key,
blocktime: BlockTime,
deploy_hash: DeployHash,
gas_limit: Gas,
gas_counter: Gas,
address_generator: Rc<RefCell<AddressGenerator>>,
protocol_version: ProtocolVersion,
correlation_id: CorrelationId,
phase: Phase,
engine_config: EngineConfig,
transfers: Vec<TransferAddr>,
remaining_spending_limit: U512,
) -> Self {
RuntimeContext {
tracking_copy,
entry_point_type,
named_keys,
access_rights,
args: runtime_args,
account,
authorization_keys,
blocktime,
deploy_hash,
base_key,
gas_limit,
gas_counter,
address_generator,
protocol_version,
correlation_id,
phase,
engine_config,
transfers,
remaining_spending_limit,
}
}
#[allow(clippy::too_many_arguments)]
pub fn new_from_self(
&self,
base_key: Key,
entry_point_type: EntryPointType,
named_keys: &'a mut NamedKeys,
access_rights: ContextAccessRights,
runtime_args: RuntimeArgs,
) -> Self {
let tracking_copy = self.state();
let authorization_keys = self.authorization_keys.clone();
let account = self.account;
let blocktime = self.blocktime;
let deploy_hash = self.deploy_hash;
let gas_limit = self.gas_limit;
let gas_counter = self.gas_counter;
let address_generator = self.address_generator.clone();
let protocol_version = self.protocol_version;
let correlation_id = self.correlation_id;
let phase = self.phase;
let engine_config = self.engine_config;
let transfers = self.transfers.clone();
let remaining_spending_limit = self.remaining_spending_limit();
RuntimeContext {
tracking_copy,
entry_point_type,
named_keys,
access_rights,
args: runtime_args,
account,
authorization_keys,
blocktime,
deploy_hash,
base_key,
gas_limit,
gas_counter,
address_generator,
protocol_version,
correlation_id,
phase,
engine_config,
transfers,
remaining_spending_limit,
}
}
pub fn authorization_keys(&self) -> &BTreeSet<AccountHash> {
&self.authorization_keys
}
pub fn named_keys_get(&self, name: &str) -> Option<&Key> {
self.named_keys.get(name)
}
pub fn named_keys(&self) -> &NamedKeys {
self.named_keys
}
pub fn named_keys_mut(&mut self) -> &mut NamedKeys {
self.named_keys
}
pub fn named_keys_contains_key(&self, name: &str) -> bool {
self.named_keys.contains_key(name)
}
fn remove_key_from_contract(
&mut self,
key: Key,
mut contract: Contract,
name: &str,
) -> Result<(), Error> {
if contract.remove_named_key(name).is_none() {
return Ok(());
}
self.metered_write_gs_unsafe(key, contract)?;
Ok(())
}
pub fn remove_key(&mut self, name: &str) -> Result<(), Error> {
match self.base_key() {
account_hash @ Key::Account(_) => {
let account: Account = {
let mut account: Account = self.read_gs_typed(&account_hash)?;
account.named_keys_mut().remove(name);
account
};
self.named_keys.remove(name);
let account_value = self.account_to_validated_value(account)?;
self.metered_write_gs_unsafe(account_hash, account_value)?;
Ok(())
}
contract_uref @ Key::URef(_) => {
let contract: Contract = {
let value: StoredValue = self
.tracking_copy
.borrow_mut()
.read(self.correlation_id, &contract_uref)
.map_err(Into::into)?
.ok_or(Error::KeyNotFound(contract_uref))?;
value.try_into().map_err(Error::TypeMismatch)?
};
self.named_keys.remove(name);
self.remove_key_from_contract(contract_uref, contract, name)
}
contract_hash @ Key::Hash(_) => {
let contract: Contract = self.read_gs_typed(&contract_hash)?;
self.named_keys.remove(name);
self.remove_key_from_contract(contract_hash, contract, name)
}
transfer_addr @ Key::Transfer(_) => {
let _transfer: Transfer = self.read_gs_typed(&transfer_addr)?;
self.named_keys.remove(name);
Ok(())
}
deploy_info_addr @ Key::DeployInfo(_) => {
let _deploy_info: DeployInfo = self.read_gs_typed(&deploy_info_addr)?;
self.named_keys.remove(name);
Ok(())
}
era_info_addr @ Key::EraInfo(_) => {
let _era_info: EraInfo = self.read_gs_typed(&era_info_addr)?;
self.named_keys.remove(name);
Ok(())
}
Key::Balance(_) => {
self.named_keys.remove(name);
Ok(())
}
Key::Bid(_) => {
self.named_keys.remove(name);
Ok(())
}
Key::Withdraw(_) => {
self.named_keys.remove(name);
Ok(())
}
Key::Dictionary(_) => {
self.named_keys.remove(name);
Ok(())
}
Key::SystemContractRegistry => {
error!("should not remove the system contract registry key");
Err(Error::RemoveKeyFailure(RemoveKeyFailure::PermissionDenied))
}
Key::EraSummary => {
self.named_keys.remove(name);
Ok(())
}
Key::Unbond(_) => {
self.named_keys.remove(name);
Ok(())
}
Key::ChainspecRegistry => {
error!("should not remove the chainspec registry key");
Err(Error::RemoveKeyFailure(RemoveKeyFailure::PermissionDenied))
}
Key::ChecksumRegistry => {
error!("should not remove the checksum registry key");
Err(Error::RemoveKeyFailure(RemoveKeyFailure::PermissionDenied))
}
}
}
pub fn get_caller(&self) -> AccountHash {
self.account.account_hash()
}
pub fn get_blocktime(&self) -> BlockTime {
self.blocktime
}
pub fn get_deploy_hash(&self) -> DeployHash {
self.deploy_hash
}
pub fn access_rights_extend(&mut self, urefs: &[URef]) {
self.access_rights.extend(urefs);
}
pub fn access_rights(&self) -> &ContextAccessRights {
&self.access_rights
}
pub fn account(&self) -> &'a Account {
self.account
}
pub fn args(&self) -> &RuntimeArgs {
&self.args
}
pub(crate) fn set_args(&mut self, args: RuntimeArgs) {
self.args = args
}
pub fn address_generator(&self) -> Rc<RefCell<AddressGenerator>> {
Rc::clone(&self.address_generator)
}
pub(super) fn state(&self) -> Rc<RefCell<TrackingCopy<R>>> {
Rc::clone(&self.tracking_copy)
}
pub fn gas_limit(&self) -> Gas {
self.gas_limit
}
pub fn gas_counter(&self) -> Gas {
self.gas_counter
}
pub fn set_gas_counter(&mut self, new_gas_counter: Gas) {
self.gas_counter = new_gas_counter;
}
pub fn base_key(&self) -> Key {
self.base_key
}
pub fn protocol_version(&self) -> ProtocolVersion {
self.protocol_version
}
pub fn correlation_id(&self) -> CorrelationId {
self.correlation_id
}
pub fn phase(&self) -> Phase {
self.phase
}
pub fn new_hash_address(&mut self) -> Result<[u8; KEY_HASH_LENGTH], Error> {
Ok(self.address_generator.borrow_mut().new_hash_address())
}
pub fn random_bytes(&mut self) -> Result<[u8; RANDOM_BYTES_COUNT], Error> {
Ok(self.address_generator.borrow_mut().create_address())
}
pub fn new_uref(&mut self, value: StoredValue) -> Result<URef, Error> {
let uref = self
.address_generator
.borrow_mut()
.new_uref(AccessRights::READ_ADD_WRITE);
self.insert_uref(uref);
self.metered_write_gs(Key::URef(uref), value)?;
Ok(uref)
}
pub(crate) fn new_unit_uref(&mut self) -> Result<URef, Error> {
self.new_uref(StoredValue::CLValue(CLValue::unit()))
}
pub fn new_transfer_addr(&mut self) -> Result<TransferAddr, Error> {
let transfer_addr = self.address_generator.borrow_mut().create_address();
Ok(TransferAddr::new(transfer_addr))
}
pub fn put_key(&mut self, name: String, key: Key) -> Result<(), Error> {
let named_key_value = StoredValue::CLValue(CLValue::from_t((name.clone(), key))?);
self.validate_value(&named_key_value)?;
self.metered_add_gs_unsafe(self.base_key(), named_key_value)?;
self.insert_named_key(name, key);
Ok(())
}
#[cfg(test)]
pub(crate) fn read_purse_uref(&mut self, purse_uref: &URef) -> Result<Option<CLValue>, Error> {
match self
.tracking_copy
.borrow_mut()
.read(self.correlation_id, &Key::Hash(purse_uref.addr()))
.map_err(Into::into)?
{
Some(stored_value) => Ok(Some(stored_value.try_into().map_err(Error::TypeMismatch)?)),
None => Ok(None),
}
}
#[cfg(test)]
pub(crate) fn write_purse_uref(
&mut self,
purse_uref: URef,
cl_value: CLValue,
) -> Result<(), Error> {
self.metered_write_gs_unsafe(Key::Hash(purse_uref.addr()), cl_value)
}
pub fn read_gs(&mut self, key: &Key) -> Result<Option<StoredValue>, Error> {
self.validate_readable(key)?;
self.validate_key(key)?;
let maybe_stored_value = self
.tracking_copy
.borrow_mut()
.read(self.correlation_id, key)
.map_err(Into::into)?;
let stored_value = match maybe_stored_value {
Some(stored_value) => dictionary::handle_stored_value(*key, stored_value)?,
None => return Ok(None),
};
Ok(Some(stored_value))
}
pub fn read_gs_direct(&mut self, key: &Key) -> Result<Option<StoredValue>, Error> {
self.tracking_copy
.borrow_mut()
.read(self.correlation_id, key)
.map_err(Into::into)
}
pub fn read_gs_typed<T>(&mut self, key: &Key) -> Result<T, Error>
where
T: TryFrom<StoredValue>,
T::Error: Debug,
{
let value = match self.read_gs(key)? {
None => return Err(Error::KeyNotFound(*key)),
Some(value) => value,
};
value.try_into().map_err(|error| {
Error::FunctionNotFound(format!(
"Type mismatch for value under {:?}: {:?}",
key, error
))
})
}
pub fn get_keys(&mut self, key_tag: &KeyTag) -> Result<BTreeSet<Key>, Error> {
self.tracking_copy
.borrow_mut()
.get_keys(self.correlation_id, key_tag)
.map_err(Into::into)
}
pub fn read_account(&mut self, key: &Key) -> Result<Option<StoredValue>, Error> {
if let Key::Account(_) = key {
self.validate_key(key)?;
self.tracking_copy
.borrow_mut()
.read(self.correlation_id, key)
.map_err(Into::into)
} else {
panic!("Do not use this function for reading from non-account keys")
}
}
pub fn write_account(&mut self, key: Key, account: Account) -> Result<(), Error> {
if let Key::Account(_) = key {
self.validate_key(&key)?;
let account_value = self.account_to_validated_value(account)?;
self.metered_write_gs_unsafe(key, account_value)?;
Ok(())
} else {
panic!("Do not use this function for writing non-account keys")
}
}
pub fn write_transfer(&mut self, key: Key, value: Transfer) {
if let Key::Transfer(_) = key {
self.tracking_copy
.borrow_mut()
.write(key, StoredValue::Transfer(value));
} else {
panic!("Do not use this function for writing non-transfer keys")
}
}
pub fn write_era_info(&mut self, key: Key, value: EraInfo) {
if let Key::EraSummary = key {
self.tracking_copy
.borrow_mut()
.write(key, StoredValue::EraInfo(value));
} else {
panic!("Do not use this function for writing non-era-info keys")
}
}
fn insert_named_key(&mut self, name: String, key: Key) {
if let Key::URef(uref) = key {
self.insert_uref(uref);
}
self.named_keys.insert(name, key);
}
fn insert_uref(&mut self, uref: URef) {
self.access_rights.extend(&[uref])
}
pub fn grant_access(&mut self, uref: URef) -> GrantedAccess {
self.access_rights.grant_access(uref)
}
pub fn remove_access(&mut self, uref_addr: URefAddr, access_rights: AccessRights) {
self.access_rights.remove_access(uref_addr, access_rights)
}
pub fn effect(&self) -> ExecutionEffect {
self.tracking_copy.borrow().effect()
}
pub fn execution_journal(&self) -> ExecutionJournal {
self.tracking_copy.borrow().execution_journal()
}
pub fn transfers(&self) -> &Vec<TransferAddr> {
&self.transfers
}
pub fn transfers_mut(&mut self) -> &mut Vec<TransferAddr> {
&mut self.transfers
}
fn validate_cl_value(&self, cl_value: &CLValue) -> Result<(), Error> {
match cl_value.cl_type() {
CLType::Bool
| CLType::I32
| CLType::I64
| CLType::U8
| CLType::U32
| CLType::U64
| CLType::U128
| CLType::U256
| CLType::U512
| CLType::Unit
| CLType::String
| CLType::Option(_)
| CLType::List(_)
| CLType::ByteArray(..)
| CLType::Result { .. }
| CLType::Map { .. }
| CLType::Tuple1(_)
| CLType::Tuple3(_)
| CLType::Any
| CLType::PublicKey => Ok(()),
CLType::Key => {
let key: Key = cl_value.to_owned().into_t()?; self.validate_key(&key)
}
CLType::URef => {
let uref: URef = cl_value.to_owned().into_t()?; self.validate_uref(&uref)
}
tuple @ CLType::Tuple2(_) if *tuple == casper_types::named_key_type() => {
let (_name, key): (String, Key) = cl_value.to_owned().into_t()?; self.validate_key(&key)
}
CLType::Tuple2(_) => Ok(()),
}
}
fn validate_value(&self, value: &StoredValue) -> Result<(), Error> {
match value {
StoredValue::CLValue(cl_value) => self.validate_cl_value(cl_value),
StoredValue::Account(account) => {
account
.named_keys()
.values()
.try_for_each(|key| self.validate_key(key))
}
StoredValue::ContractWasm(_) => Ok(()),
StoredValue::Contract(contract_header) => contract_header
.named_keys()
.values()
.try_for_each(|key| self.validate_key(key)),
StoredValue::ContractPackage(_) => Ok(()),
StoredValue::Transfer(_) => Ok(()),
StoredValue::DeployInfo(_) => Ok(()),
StoredValue::EraInfo(_) => Ok(()),
StoredValue::Bid(_) => Ok(()),
StoredValue::Withdraw(_) => Ok(()),
StoredValue::Unbonding(_) => Ok(()),
}
}
pub(crate) fn validate_key(&self, key: &Key) -> Result<(), Error> {
let uref = match key {
Key::URef(uref) => uref,
_ => return Ok(()),
};
self.validate_uref(uref)
}
pub(crate) fn validate_uref(&self, uref: &URef) -> Result<(), Error> {
if self.access_rights.has_access_rights_to_uref(uref) {
Ok(())
} else {
Err(Error::ForgedReference(*uref))
}
}
fn validate_readable(&self, key: &Key) -> Result<(), Error> {
if self.is_readable(key) {
Ok(())
} else {
Err(Error::InvalidAccess {
required: AccessRights::READ,
})
}
}
fn validate_addable(&self, key: &Key) -> Result<(), Error> {
if self.is_addable(key) {
Ok(())
} else {
Err(Error::InvalidAccess {
required: AccessRights::ADD,
})
}
}
fn validate_writeable(&self, key: &Key) -> Result<(), Error> {
if self.is_writeable(key) {
Ok(())
} else {
Err(Error::InvalidAccess {
required: AccessRights::WRITE,
})
}
}
pub fn is_readable(&self, key: &Key) -> bool {
match key {
Key::Account(_) => &self.base_key() == key,
Key::Hash(_) => true,
Key::URef(uref) => uref.is_readable(),
Key::Transfer(_) => true,
Key::DeployInfo(_) => true,
Key::EraInfo(_) => true,
Key::Balance(_) => false,
Key::Bid(_) => true,
Key::Withdraw(_) => true,
Key::Dictionary(_) => true,
Key::SystemContractRegistry => true,
Key::EraSummary => true,
Key::Unbond(_) => true,
Key::ChainspecRegistry => true,
Key::ChecksumRegistry => true,
}
}
pub fn is_addable(&self, key: &Key) -> bool {
match key {
Key::Account(_) | Key::Hash(_) => &self.base_key() == key, Key::URef(uref) => uref.is_addable(),
Key::Transfer(_) => false,
Key::DeployInfo(_) => false,
Key::EraInfo(_) => false,
Key::Balance(_) => false,
Key::Bid(_) => false,
Key::Withdraw(_) => false,
Key::Dictionary(_) => false,
Key::SystemContractRegistry => false,
Key::EraSummary => false,
Key::Unbond(_) => false,
Key::ChainspecRegistry => false,
Key::ChecksumRegistry => false,
}
}
pub fn is_writeable(&self, key: &Key) -> bool {
match key {
Key::Account(_) | Key::Hash(_) => false,
Key::URef(uref) => uref.is_writeable(),
Key::Transfer(_) => false,
Key::DeployInfo(_) => false,
Key::EraInfo(_) => false,
Key::Balance(_) => false,
Key::Bid(_) => false,
Key::Withdraw(_) => false,
Key::Dictionary(_) => false,
Key::SystemContractRegistry => false,
Key::EraSummary => false,
Key::Unbond(_) => false,
Key::ChainspecRegistry => false,
Key::ChecksumRegistry => false,
}
}
pub(crate) fn charge_gas(&mut self, amount: Gas) -> Result<(), Error> {
let prev = self.gas_counter();
let gas_limit = self.gas_limit();
match prev.checked_add(amount) {
None => {
self.set_gas_counter(gas_limit);
Err(Error::GasLimit)
}
Some(val) if val > gas_limit => {
self.set_gas_counter(gas_limit);
Err(Error::GasLimit)
}
Some(val) => {
self.set_gas_counter(val);
Ok(())
}
}
}
pub(crate) fn is_system_contract(&self, contract_hash: &ContractHash) -> Result<bool, Error> {
Ok(self
.system_contract_registry()?
.has_contract_hash(contract_hash))
}
fn charge_gas_storage(&mut self, bytes_count: usize) -> Result<(), Error> {
if let Some(base_key) = self.base_key().into_hash() {
let contract_hash = ContractHash::new(base_key);
if self.is_system_contract(&contract_hash)? {
return Ok(());
}
}
let storage_costs = self.engine_config.wasm_config().storage_costs();
let gas_cost = storage_costs.calculate_gas_cost(bytes_count);
self.charge_gas(gas_cost)
}
pub(crate) fn charge_system_contract_call<T>(&mut self, call_cost: T) -> Result<(), Error>
where
T: Into<Gas>,
{
if self.account.account_hash() == PublicKey::System.to_account_hash() {
return Ok(());
}
let amount: Gas = call_cost.into();
self.charge_gas(amount)
}
pub(crate) fn metered_write_gs_unsafe<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
where
K: Into<Key>,
V: Into<StoredValue>,
{
let stored_value = value.into();
let bytes_count = stored_value.serialized_length();
self.charge_gas_storage(bytes_count)?;
self.tracking_copy
.borrow_mut()
.write(key.into(), stored_value);
Ok(())
}
pub(crate) fn metered_write_gs<T>(&mut self, key: Key, value: T) -> Result<(), Error>
where
T: Into<StoredValue>,
{
let stored_value = value.into();
self.validate_writeable(&key)?;
self.validate_key(&key)?;
self.validate_value(&stored_value)?;
self.metered_write_gs_unsafe(key, stored_value)
}
pub(crate) fn metered_add_gs_unsafe(
&mut self,
key: Key,
value: StoredValue,
) -> Result<(), Error> {
let value_bytes_count = value.serialized_length();
self.charge_gas_storage(value_bytes_count)?;
match self
.tracking_copy
.borrow_mut()
.add(self.correlation_id, key, value)
{
Err(storage_error) => Err(storage_error.into()),
Ok(AddResult::Success) => Ok(()),
Ok(AddResult::KeyNotFound(key)) => Err(Error::KeyNotFound(key)),
Ok(AddResult::TypeMismatch(type_mismatch)) => Err(Error::TypeMismatch(type_mismatch)),
Ok(AddResult::Serialization(error)) => Err(Error::BytesRepr(error)),
}
}
pub(crate) fn metered_add_gs<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
where
K: Into<Key>,
V: Into<StoredValue>,
{
let key = key.into();
let value = value.into();
self.validate_addable(&key)?;
self.validate_key(&key)?;
self.validate_value(&value)?;
self.metered_add_gs_unsafe(key, value)
}
pub(crate) fn add_associated_key(
&mut self,
account_hash: AccountHash,
weight: Weight,
) -> Result<(), Error> {
if !self.is_valid_context() {
return Err(AddKeyFailure::PermissionDenied.into());
}
if !self
.account()
.can_manage_keys_with(&self.authorization_keys)
{
return Err(AddKeyFailure::PermissionDenied.into());
}
let key = Key::Account(self.account().account_hash());
let account = {
let mut account: Account = self.read_gs_typed(&key)?;
if account.associated_keys().len()
>= (self.engine_config.max_associated_keys() as usize)
{
return Err(Error::AddKeyFailure(AddKeyFailure::MaxKeysLimit));
}
account
.add_associated_key(account_hash, weight)
.map_err(Error::from)?;
account
};
let account_value = self.account_to_validated_value(account)?;
self.metered_write_gs_unsafe(key, account_value)?;
Ok(())
}
pub(crate) fn remove_associated_key(&mut self, account_hash: AccountHash) -> Result<(), Error> {
if !self.is_valid_context() {
return Err(RemoveKeyFailure::PermissionDenied.into());
}
if !self
.account()
.can_manage_keys_with(&self.authorization_keys)
{
return Err(RemoveKeyFailure::PermissionDenied.into());
}
let key = Key::Account(self.account().account_hash());
let mut account: Account = self.read_gs_typed(&key)?;
account
.remove_associated_key(account_hash)
.map_err(Error::from)?;
let account_value = self.account_to_validated_value(account)?;
self.metered_write_gs_unsafe(key, account_value)?;
Ok(())
}
pub(crate) fn update_associated_key(
&mut self,
account_hash: AccountHash,
weight: Weight,
) -> Result<(), Error> {
if !self.is_valid_context() {
return Err(UpdateKeyFailure::PermissionDenied.into());
}
if !self
.account()
.can_manage_keys_with(&self.authorization_keys)
{
return Err(UpdateKeyFailure::PermissionDenied.into());
}
let key = Key::Account(self.account().account_hash());
let mut account: Account = self.read_gs_typed(&key)?;
account
.update_associated_key(account_hash, weight)
.map_err(Error::from)?;
let account_value = self.account_to_validated_value(account)?;
self.metered_write_gs_unsafe(key, account_value)?;
Ok(())
}
pub(crate) fn set_action_threshold(
&mut self,
action_type: ActionType,
threshold: Weight,
) -> Result<(), Error> {
if !self.is_valid_context() {
return Err(SetThresholdFailure::PermissionDeniedError.into());
}
if !self
.account()
.can_manage_keys_with(&self.authorization_keys)
{
return Err(SetThresholdFailure::PermissionDeniedError.into());
}
let key = Key::Account(self.account().account_hash());
let mut account: Account = self.read_gs_typed(&key)?;
account
.set_action_threshold(action_type, threshold)
.map_err(Error::from)?;
let account_value = self.account_to_validated_value(account)?;
self.metered_write_gs_unsafe(key, account_value)?;
Ok(())
}
fn account_to_validated_value(&self, account: Account) -> Result<StoredValue, Error> {
let value = StoredValue::Account(account);
self.validate_value(&value)?;
Ok(value)
}
fn is_valid_context(&self) -> bool {
self.base_key() == Key::Account(self.account().account_hash())
}
pub fn get_main_purse(&mut self) -> Result<URef, Error> {
if !self.is_valid_context() {
return Err(Error::InvalidContext);
}
let main_purse = self.account().main_purse();
Ok(main_purse)
}
pub fn entry_point_type(&self) -> EntryPointType {
self.entry_point_type
}
pub(crate) fn get_validated_contract_package(
&mut self,
package_hash: ContractPackageHash,
) -> Result<ContractPackage, Error> {
let package_hash_key = Key::from(package_hash);
self.validate_key(&package_hash_key)?;
let contract_package: ContractPackage = self.read_gs_typed(&Key::from(package_hash))?;
self.validate_uref(&contract_package.access_key())?;
Ok(contract_package)
}
pub(crate) fn dictionary_get(
&mut self,
uref: URef,
dictionary_item_key: &str,
) -> Result<Option<CLValue>, Error> {
self.validate_readable(&uref.into())?;
self.validate_key(&uref.into())?;
let dictionary_item_key_bytes = dictionary_item_key.as_bytes();
if dictionary_item_key_bytes.len() > DICTIONARY_ITEM_KEY_MAX_LENGTH {
return Err(Error::DictionaryItemKeyExceedsLength);
}
let dictionary_key = Key::dictionary(uref, dictionary_item_key_bytes);
self.dictionary_read(dictionary_key)
}
pub(crate) fn dictionary_read(
&mut self,
dictionary_key: Key,
) -> Result<Option<CLValue>, Error> {
let maybe_stored_value = self
.tracking_copy
.borrow_mut()
.read(self.correlation_id, &dictionary_key)
.map_err(Into::into)?;
if let Some(stored_value) = maybe_stored_value {
let stored_value = dictionary::handle_stored_value(dictionary_key, stored_value)?;
let cl_value = CLValue::try_from(stored_value).map_err(Error::TypeMismatch)?;
Ok(Some(cl_value))
} else {
Ok(None)
}
}
pub fn dictionary_put(
&mut self,
seed_uref: URef,
dictionary_item_key: &str,
cl_value: CLValue,
) -> Result<(), Error> {
let dictionary_item_key_bytes = dictionary_item_key.as_bytes();
if dictionary_item_key_bytes.len() > DICTIONARY_ITEM_KEY_MAX_LENGTH {
return Err(Error::DictionaryItemKeyExceedsLength);
}
self.validate_writeable(&seed_uref.into())?;
self.validate_uref(&seed_uref)?;
self.validate_cl_value(&cl_value)?;
let wrapped_cl_value = {
let dictionary_value = DictionaryValue::new(
cl_value,
seed_uref.addr().to_vec(),
dictionary_item_key_bytes.to_vec(),
);
CLValue::from_t(dictionary_value).map_err(Error::from)?
};
let dictionary_key = Key::dictionary(seed_uref, dictionary_item_key_bytes);
self.metered_write_gs_unsafe(dictionary_key, wrapped_cl_value)?;
Ok(())
}
pub(crate) fn get_system_contract(&self, name: &str) -> Result<ContractHash, Error> {
let registry = self.system_contract_registry()?;
let hash = registry.get(name).ok_or_else(|| {
error!("Missing system contract hash: {}", name);
Error::MissingSystemContractHash(name.to_string())
})?;
Ok(*hash)
}
pub fn system_contract_registry(&self) -> Result<SystemContractRegistry, Error> {
self.tracking_copy
.borrow_mut()
.get_system_contracts(self.correlation_id)
.map_err(|_| {
error!("Missing system contract registry");
Error::MissingSystemContractRegistry
})
}
pub(super) fn remaining_spending_limit(&self) -> U512 {
self.remaining_spending_limit
}
pub(crate) fn subtract_amount_spent(&mut self, amount: U512) -> Option<U512> {
if let Some(res) = self.remaining_spending_limit.checked_sub(amount) {
self.remaining_spending_limit = res;
Some(self.remaining_spending_limit)
} else {
error!(
limit = %self.remaining_spending_limit,
spent = %amount,
"exceeded main purse spending limit"
);
self.remaining_spending_limit = U512::zero();
None
}
}
pub(crate) fn set_remaining_spending_limit(&mut self, amount: U512) {
self.remaining_spending_limit = amount;
}
}