mod args;
mod auction_internal;
pub mod cryptography;
mod externals;
mod handle_payment_internal;
mod host_function_flag;
mod mint_internal;
pub mod stack;
mod utils;
pub(crate) mod wasm_prep;
use std::{
cmp,
collections::{BTreeMap, BTreeSet},
convert::{TryFrom, TryInto},
iter::FromIterator,
};
use casper_wasm::elements::Module;
use casper_wasmi::{MemoryRef, Trap, TrapCode};
use tracing::{debug, error, warn};
#[cfg(feature = "test-support")]
use casper_wasmi::RuntimeValue;
use itertools::Itertools;
use num_rational::Ratio;
use casper_storage::{
global_state::{error::Error as GlobalStateError, state::StateReader},
system::{auction::Auction, handle_payment::HandlePayment, mint::Mint},
tracking_copy::TrackingCopyExt,
};
use casper_types::{
account::{
Account, AccountHash, AddKeyFailure, RemoveKeyFailure, SetThresholdFailure,
UpdateKeyFailure,
},
addressable_entity::{
self, ActionThresholds, ActionType, AddressableEntity, AddressableEntityHash,
AssociatedKeys, ContractRuntimeTag, EntityEntryPoint, EntryPointAccess, EntryPointType,
EntryPoints, MessageTopicError, MessageTopics, NamedKeyAddr, NamedKeyValue, Parameter,
Weight, DEFAULT_ENTRY_POINT_NAME,
},
bytesrepr::{self, Bytes, FromBytes, ToBytes},
contract_messages::{
Message, MessageAddr, MessagePayload, MessageTopicOperation, MessageTopicSummary,
},
contracts::{
ContractHash, ContractPackage, ContractPackageHash, ContractPackageStatus,
ContractVersions, DisabledVersions, NamedKeys, ProtocolVersionMajor,
},
system::{
self,
auction::{self, DelegatorKind, EraInfo, MINIMUM_DELEGATION_RATE_KEY},
handle_payment,
mint::{self, MINT_SUSTAIN_PURSE_KEY},
CallStackElement, Caller, CallerInfo, SystemEntityType, AUCTION, HANDLE_PAYMENT, MINT,
STANDARD_PAYMENT,
},
AccessRights, ApiError, BlockGlobalAddr, BlockTime, ByteCode, ByteCodeAddr, ByteCodeHash,
ByteCodeKind, CLTyped, CLValue, ContextAccessRights, Contract, ContractWasm, EntityAddr,
EntityKind, EntityVersion, EntityVersionKey, EntityVersions, Gas, GrantedAccess, Group, Groups,
HashAddr, HostFunction, HostFunctionCost, InitiatorAddr, Key, NamedArg, Package, PackageHash,
PackageStatus, Phase, PublicKey, RewardsHandling, RuntimeArgs, RuntimeFootprint, StoredValue,
Transfer, TransferResult, TransferV2, TransferredTo, URef, DICTIONARY_ITEM_KEY_MAX_LENGTH,
U512,
};
use crate::{
execution::ExecError, runtime::host_function_flag::HostFunctionFlag,
runtime_context::RuntimeContext,
};
pub use stack::{RuntimeStack, RuntimeStackFrame, RuntimeStackOverflow};
pub use wasm_prep::{
cycles_for_instruction, preprocess, PreprocessingError, WasmValidationError,
DEFAULT_BR_TABLE_MAX_SIZE, DEFAULT_MAX_GLOBALS, DEFAULT_MAX_PARAMETER_COUNT,
DEFAULT_MAX_TABLE_SIZE,
};
#[derive(Debug)]
enum CallContractIdentifier {
Contract {
contract_hash: HashAddr,
},
ContractPackage {
contract_package_hash: HashAddr,
version: Option<EntityVersion>,
protocol_version_major: Option<ProtocolVersionMajor>,
},
}
#[repr(u8)]
enum CallerInformation {
Initiator = 0,
Immediate = 1,
FullCallChain = 2,
}
impl TryFrom<u8> for CallerInformation {
type Error = ApiError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(CallerInformation::Initiator),
1 => Ok(CallerInformation::Immediate),
2 => Ok(CallerInformation::FullCallChain),
_ => Err(ApiError::InvalidCallerInfoRequest),
}
}
}
pub struct Runtime<'a, R> {
context: RuntimeContext<'a, R>,
memory: Option<MemoryRef>,
module: Option<Module>,
host_buffer: Option<CLValue>,
stack: Option<RuntimeStack>,
host_function_flag: HostFunctionFlag,
}
impl<'a, R> Runtime<'a, R>
where
R: StateReader<Key, StoredValue, Error = GlobalStateError>,
{
pub(crate) fn new(context: RuntimeContext<'a, R>) -> Self {
Runtime {
context,
memory: None,
module: None,
host_buffer: None,
stack: None,
host_function_flag: HostFunctionFlag::default(),
}
}
fn new_invocation_runtime(
&self,
context: RuntimeContext<'a, R>,
module: Module,
memory: MemoryRef,
stack: RuntimeStack,
) -> Self {
Self::check_preconditions(&stack);
Runtime {
context,
memory: Some(memory),
module: Some(module),
host_buffer: None,
stack: Some(stack),
host_function_flag: self.host_function_flag.clone(),
}
}
pub(crate) fn new_with_stack(
&self,
context: RuntimeContext<'a, R>,
stack: RuntimeStack,
) -> Self {
Self::check_preconditions(&stack);
Runtime {
context,
memory: None,
module: None,
host_buffer: None,
stack: Some(stack),
host_function_flag: self.host_function_flag.clone(),
}
}
fn check_preconditions(stack: &RuntimeStack) {
if stack.is_empty() {
error!("Call stack should not be empty while creating a new Runtime instance");
debug_assert!(false);
}
if stack.first_frame().unwrap().contract_hash().is_some() {
error!("First element of the call stack should always represent a Session call");
debug_assert!(false);
}
}
pub(crate) fn context(&self) -> &RuntimeContext<'a, R> {
&self.context
}
fn gas(&mut self, amount: Gas) -> Result<(), ExecError> {
self.context.charge_gas(amount)
}
fn gas_counter(&self) -> Gas {
self.context.gas_counter()
}
fn set_gas_counter(&mut self, new_gas_counter: Gas) {
self.context.set_gas_counter(new_gas_counter);
}
pub(crate) fn charge_system_contract_call<T>(&mut self, amount: T) -> Result<(), ExecError>
where
T: Into<Gas>,
{
if self.is_system_immediate_caller()? || self.host_function_flag.is_in_host_function_scope()
{
return Ok(());
}
self.context.charge_system_contract_call(amount)
}
fn checked_memory_slice<Ret>(
&self,
offset: usize,
size: usize,
func: impl FnOnce(&[u8]) -> Ret,
) -> Result<Ret, ExecError> {
self.try_get_memory()?
.with_direct_access(|buffer| {
let end = offset.checked_add(size).ok_or_else(|| {
casper_wasmi::Error::Memory(format!(
"trying to access memory block of size {} from offset {}",
size, offset
))
})?;
if end > buffer.len() {
return Err(casper_wasmi::Error::Memory(format!(
"trying to access region [{}..{}] in memory [0..{}]",
offset,
end,
buffer.len(),
)));
}
Ok(func(&buffer[offset..end]))
})
.map_err(Into::into)
}
#[inline]
fn bytes_from_mem(&self, ptr: u32, size: usize) -> Result<Vec<u8>, ExecError> {
self.checked_memory_slice(ptr as usize, size, |data| data.to_vec())
}
#[inline]
fn t_from_mem<T: FromBytes>(&self, ptr: u32, size: u32) -> Result<T, ExecError> {
let result = self.checked_memory_slice(ptr as usize, size as usize, |data| {
bytesrepr::deserialize_from_slice(data)
})?;
Ok(result?)
}
#[inline]
fn key_from_mem(&mut self, key_ptr: u32, key_size: u32) -> Result<Key, ExecError> {
self.t_from_mem(key_ptr, key_size)
}
#[inline]
fn cl_value_from_mem(
&mut self,
cl_value_ptr: u32,
cl_value_size: u32,
) -> Result<CLValue, ExecError> {
self.t_from_mem(cl_value_ptr, cl_value_size)
}
#[inline]
fn string_from_mem(&self, ptr: u32, size: u32) -> Result<String, Trap> {
self.t_from_mem(ptr, size).map_err(Trap::from)
}
fn get_module_from_entry_points(
&mut self,
entry_points: &EntryPoints,
) -> Result<Vec<u8>, ExecError> {
let module = self.try_get_module()?.clone();
let entry_point_names: Vec<&str> = entry_points.keys().map(|s| s.as_str()).collect();
let module_bytes = wasm_prep::get_module_from_entry_points(entry_point_names, module)?;
Ok(module_bytes)
}
#[allow(clippy::wrong_self_convention)]
fn is_valid_uref(&self, uref_ptr: u32, uref_size: u32) -> Result<bool, Trap> {
let uref: URef = self.t_from_mem(uref_ptr, uref_size)?;
Ok(self.context.validate_uref(&uref).is_ok())
}
fn load_key(
&mut self,
name_ptr: u32,
name_size: u32,
output_ptr: u32,
output_size: usize,
bytes_written_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
let name = self.string_from_mem(name_ptr, name_size)?;
let key = match self.context.named_keys_get(&name) {
Some(key) => key,
None => {
return Ok(Err(ApiError::MissingKey));
}
};
let key_bytes = match key.to_bytes() {
Ok(bytes) => bytes,
Err(error) => return Ok(Err(error.into())),
};
if output_size < key_bytes.len() {
return Ok(Err(ApiError::BufferTooSmall));
}
if let Err(error) = self.try_get_memory()?.set(output_ptr, &key_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
let bytes_size: u32 = key_bytes
.len()
.try_into()
.expect("Keys should not serialize to many bytes");
let size_bytes = bytes_size.to_le_bytes(); if let Err(error) = self.try_get_memory()?.set(bytes_written_ptr, &size_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn has_key(&mut self, name_ptr: u32, name_size: u32) -> Result<i32, Trap> {
let name = self.string_from_mem(name_ptr, name_size)?;
if self.context.named_keys_contains_key(&name) {
Ok(0)
} else {
Ok(1)
}
}
fn put_key(
&mut self,
name_ptr: u32,
name_size: u32,
key_ptr: u32,
key_size: u32,
) -> Result<(), Trap> {
let name = self.string_from_mem(name_ptr, name_size)?;
let key = self.key_from_mem(key_ptr, key_size)?;
if let Some(payment_purse) = self.context.maybe_payment_purse() {
if Key::URef(payment_purse).normalize() == key.normalize() {
warn!("attempt to put_key payment purse");
return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
handle_payment::Error::AttemptToPersistPaymentPurse as u8,
))));
}
}
self.context.put_key(name, key).map_err(Into::into)
}
fn remove_key(&mut self, name_ptr: u32, name_size: u32) -> Result<(), Trap> {
let name = self.string_from_mem(name_ptr, name_size)?;
self.context.remove_key(&name)?;
Ok(())
}
fn get_main_purse(&mut self, dest_ptr: u32) -> Result<(), Trap> {
let purse = self.context.get_main_purse()?;
let purse_bytes = purse.into_bytes().map_err(ExecError::BytesRepr)?;
self.try_get_memory()?
.set(dest_ptr, &purse_bytes)
.map_err(|e| ExecError::Interpreter(e.into()).into())
}
fn get_caller(&mut self, output_size_ptr: u32) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let value = CLValue::from_t(self.context.get_initiator()).map_err(ExecError::CLValue)?;
let value_size = value.inner_bytes().len();
if let Err(error) = self.write_host_buffer(value) {
return Ok(Err(error));
}
let output_size_bytes = value_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(output_size_ptr, &output_size_bytes)
{
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn get_immediate_caller(&self) -> Option<&RuntimeStackFrame> {
self.stack.as_ref().and_then(|stack| stack.previous_frame())
}
fn is_allowed_session_caller(&self, provided_account_hash: &AccountHash) -> bool {
if self.context.get_initiator() == PublicKey::System.to_account_hash() {
return true;
}
if let Some(Caller::Initiator { account_hash }) = self.get_immediate_caller() {
return account_hash == provided_account_hash;
}
false
}
fn get_phase(&mut self, dest_ptr: u32) -> Result<(), Trap> {
let phase = self.context.phase();
let bytes = phase.into_bytes().map_err(ExecError::BytesRepr)?;
self.try_get_memory()?
.set(dest_ptr, &bytes)
.map_err(|e| ExecError::Interpreter(e.into()).into())
}
fn get_block_info(&self, field_idx: u8, dest_ptr: u32) -> Result<(), Trap> {
if field_idx == 0 {
return self.get_blocktime(dest_ptr);
}
let block_info = self.context.get_block_info();
let mut data: Vec<u8> = vec![];
if field_idx == 1 {
data = block_info
.block_height()
.into_bytes()
.map_err(ExecError::BytesRepr)?;
}
if field_idx == 2 {
data = block_info
.parent_block_hash()
.into_bytes()
.map_err(ExecError::BytesRepr)?;
}
if field_idx == 3 {
data = block_info
.state_hash()
.into_bytes()
.map_err(ExecError::BytesRepr)?;
}
if field_idx == 4 {
data = self
.context
.protocol_version()
.into_bytes()
.map_err(ExecError::BytesRepr)?;
}
if field_idx == 5 {
data = self
.context
.engine_config()
.enable_entity
.into_bytes()
.map_err(ExecError::BytesRepr)?;
}
if data.is_empty() {
Err(ExecError::InvalidImputedOperation.into())
} else {
Ok(self
.try_get_memory()?
.set(dest_ptr, &data)
.map_err(|e| ExecError::Interpreter(e.into()))?)
}
}
fn get_blocktime(&self, dest_ptr: u32) -> Result<(), Trap> {
let block_info = self.context.get_block_info();
let blocktime = block_info
.block_time()
.into_bytes()
.map_err(ExecError::BytesRepr)?;
self.try_get_memory()?
.set(dest_ptr, &blocktime)
.map_err(|e| ExecError::Interpreter(e.into()).into())
}
fn load_call_stack(
&mut self,
call_stack_len_ptr: u32,
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let call_stack: Vec<CallStackElement> = match self.try_get_stack() {
Ok(stack) => {
let caller = stack.call_stack_elements();
caller.iter().map_into().collect_vec()
}
Err(_error) => return Ok(Err(ApiError::Unhandled)),
};
let call_stack_len: u32 = match call_stack.len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
let call_stack_len_bytes = call_stack_len.to_le_bytes();
if let Err(error) = self
.try_get_memory()?
.set(call_stack_len_ptr, &call_stack_len_bytes)
{
return Err(ExecError::Interpreter(error.into()).into());
}
if call_stack_len == 0 {
return Ok(Ok(()));
}
let call_stack_cl_value = CLValue::from_t(call_stack).map_err(ExecError::CLValue)?;
let call_stack_cl_value_bytes_len: u32 =
match call_stack_cl_value.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
if let Err(error) = self.write_host_buffer(call_stack_cl_value) {
return Ok(Err(error));
}
let call_stack_cl_value_bytes_len_bytes = call_stack_cl_value_bytes_len.to_le_bytes();
if let Err(error) = self
.try_get_memory()?
.set(result_size_ptr, &call_stack_cl_value_bytes_len_bytes)
{
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn load_caller_information(
&mut self,
information: u8,
call_stack_len_ptr: u32,
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let caller_info = match CallerInformation::try_from(information) {
Ok(info) => info,
Err(error) => return Ok(Err(error)),
};
let caller = match caller_info {
CallerInformation::Initiator => {
let initiator_account_hash = self.context.get_initiator();
let caller = Caller::initiator(initiator_account_hash);
match CallerInfo::try_from(caller) {
Ok(caller_info) => {
vec![caller_info]
}
Err(_) => return Ok(Err(ApiError::CLTypeMismatch)),
}
}
CallerInformation::Immediate => match self.get_immediate_caller() {
Some(frame) => match CallerInfo::try_from(*frame) {
Ok(immediate_info) => {
vec![immediate_info]
}
Err(_) => return Ok(Err(ApiError::CLTypeMismatch)),
},
None => return Ok(Err(ApiError::Unhandled)),
},
CallerInformation::FullCallChain => match self.try_get_stack() {
Ok(call_stack) => {
let call_stack = call_stack.call_stack_elements().clone();
let mut ret = vec![];
for caller in call_stack {
match CallerInfo::try_from(caller) {
Ok(info) => ret.push(info),
Err(_) => return Ok(Err(ApiError::CLTypeMismatch)),
}
}
ret
}
Err(_) => return Ok(Err(ApiError::Unhandled)),
},
};
let call_stack_len: u32 = match caller.len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
let call_stack_len_bytes = call_stack_len.to_le_bytes();
if let Err(error) = self
.try_get_memory()?
.set(call_stack_len_ptr, &call_stack_len_bytes)
{
return Err(ExecError::Interpreter(error.into()).into());
}
if call_stack_len == 0 {
return Ok(Ok(()));
}
let call_stack_cl_value = CLValue::from_t(caller).map_err(ExecError::CLValue)?;
let call_stack_cl_value_bytes_len: u32 =
match call_stack_cl_value.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
if let Err(error) = self.write_host_buffer(call_stack_cl_value) {
return Ok(Err(error));
}
let call_stack_cl_value_bytes_len_bytes = call_stack_cl_value_bytes_len.to_le_bytes();
if let Err(error) = self
.try_get_memory()?
.set(result_size_ptr, &call_stack_cl_value_bytes_len_bytes)
{
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn ret(&mut self, value_ptr: u32, value_size: usize) -> Trap {
self.host_buffer = None;
let mem_get =
self.checked_memory_slice(value_ptr as usize, value_size, |data| data.to_vec());
match mem_get {
Ok(buf) => {
self.host_buffer = bytesrepr::deserialize_from_slice(buf).ok();
let urefs = match &self.host_buffer {
Some(buf) => utils::extract_urefs(buf),
None => Ok(vec![]),
};
match urefs {
Ok(urefs) => {
for uref in &urefs {
if let Err(error) = self.context.validate_uref(uref) {
return Trap::from(error);
}
}
ExecError::Ret(urefs).into()
}
Err(e) => e.into(),
}
}
Err(e) => e.into(),
}
}
fn is_system_contract(&self, hash_addr: HashAddr) -> Result<bool, ExecError> {
self.context.is_system_addressable_entity(&hash_addr)
}
fn get_named_argument<T: FromBytes + CLTyped>(
args: &RuntimeArgs,
name: &str,
) -> Result<T, ExecError> {
let arg: CLValue = args
.get(name)
.cloned()
.ok_or(ExecError::Revert(ApiError::MissingArgument))?;
arg.into_t()
.map_err(|_| ExecError::Revert(ApiError::InvalidArgument))
}
fn try_get_named_argument<T: FromBytes + CLTyped>(
args: &RuntimeArgs,
name: &str,
) -> Result<Option<T>, ExecError> {
match args.get(name) {
Some(arg) => {
let arg = arg
.clone()
.into_t()
.map_err(|_| ExecError::Revert(ApiError::InvalidArgument))?;
Ok(Some(arg))
}
None => Ok(None),
}
}
fn reverter<T: Into<ApiError>>(error: T) -> ExecError {
let api_error: ApiError = error.into();
match api_error {
ApiError::Mint(mint_error) if mint_error == mint::Error::GasLimit as u8 => {
ExecError::GasLimit
}
ApiError::AuctionError(auction_error)
if auction_error == auction::Error::GasLimit as u8 =>
{
ExecError::GasLimit
}
ApiError::HandlePayment(handle_payment_error)
if handle_payment_error == handle_payment::Error::GasLimit as u8 =>
{
ExecError::GasLimit
}
api_error => ExecError::Revert(api_error),
}
}
fn call_host_mint(
&mut self,
entry_point_name: &str,
runtime_args: &RuntimeArgs,
access_rights: ContextAccessRights,
stack: RuntimeStack,
) -> Result<CLValue, ExecError> {
let gas_counter = self.gas_counter();
let mint_hash = self.context.get_system_contract(MINT)?;
let mint_addr = EntityAddr::new_system(mint_hash.value());
let mint_key = if self.context.engine_config().enable_entity {
Key::AddressableEntity(EntityAddr::System(mint_hash.value()))
} else {
Key::Hash(mint_hash.value())
};
let mint_named_keys = self
.context
.state()
.borrow_mut()
.get_named_keys(mint_addr)?;
let mut named_keys = mint_named_keys;
let runtime_context = self.context.new_from_self(
mint_key,
EntryPointType::Called,
&mut named_keys,
access_rights,
runtime_args.to_owned(),
);
let mut mint_runtime = self.new_with_stack(runtime_context, stack);
let engine_config = self.context.engine_config();
let system_config = engine_config.system_config();
let mint_costs = system_config.mint_costs();
let result = match entry_point_name {
mint::METHOD_MINT => (|| {
mint_runtime.charge_system_contract_call(mint_costs.mint)?;
let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
let result: Result<URef, mint::Error> = mint_runtime.mint(amount);
if let Err(mint::Error::GasLimit) = result {
return Err(ExecError::GasLimit);
}
CLValue::from_t(result).map_err(Self::reverter)
})(),
mint::METHOD_REDUCE_TOTAL_SUPPLY => (|| {
mint_runtime.charge_system_contract_call(mint_costs.reduce_total_supply)?;
let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
let result: Result<(), mint::Error> = mint_runtime.reduce_total_supply(amount);
CLValue::from_t(result).map_err(Self::reverter)
})(),
mint::METHOD_BURN => (|| {
mint_runtime.charge_system_contract_call(mint_costs.burn)?;
let purse: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?;
let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
let result: Result<(), mint::Error> = mint_runtime.burn(purse, amount);
CLValue::from_t(result).map_err(Self::reverter)
})(),
mint::METHOD_CREATE => (|| {
mint_runtime.charge_system_contract_call(mint_costs.create)?;
let uref = mint_runtime.mint(U512::zero()).map_err(Self::reverter)?;
CLValue::from_t(uref).map_err(Self::reverter)
})(),
mint::METHOD_BALANCE => (|| {
mint_runtime.charge_system_contract_call(mint_costs.balance)?;
let uref: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?;
let maybe_balance: Option<U512> =
mint_runtime.balance(uref).map_err(Self::reverter)?;
CLValue::from_t(maybe_balance).map_err(Self::reverter)
})(),
mint::METHOD_TRANSFER => (|| {
mint_runtime.charge_system_contract_call(mint_costs.transfer)?;
let maybe_to: Option<AccountHash> =
Self::get_named_argument(runtime_args, mint::ARG_TO)?;
let source: URef = Self::get_named_argument(runtime_args, mint::ARG_SOURCE)?;
let target: URef = Self::get_named_argument(runtime_args, mint::ARG_TARGET)?;
let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
let id: Option<u64> = Self::get_named_argument(runtime_args, mint::ARG_ID)?;
let result: Result<(), mint::Error> =
mint_runtime.transfer(maybe_to, source, target, amount, id);
CLValue::from_t(result).map_err(Self::reverter)
})(),
mint::METHOD_READ_BASE_ROUND_REWARD => (|| {
mint_runtime.charge_system_contract_call(mint_costs.read_base_round_reward)?;
let result: U512 = mint_runtime
.read_base_round_reward()
.map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
mint::METHOD_MINT_INTO_EXISTING_PURSE => (|| {
mint_runtime.charge_system_contract_call(mint_costs.mint_into_existing_purse)?;
let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?;
let existing_purse: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?;
let result: Result<(), mint::Error> =
mint_runtime.mint_into_existing_purse(existing_purse, amount);
CLValue::from_t(result).map_err(Self::reverter)
})(),
_ => {
Ok(CLValue::unit())
}
};
self.gas(
mint_runtime
.gas_counter()
.checked_sub(gas_counter)
.unwrap_or(gas_counter),
)?;
let ret = result?;
self.context
.set_remaining_spending_limit(mint_runtime.context.remaining_spending_limit());
let urefs = utils::extract_urefs(&ret)?;
self.context.access_rights_extend(&urefs);
{
let transfers = self.context.transfers_mut();
mint_runtime.context.transfers().clone_into(transfers);
}
Ok(ret)
}
fn call_host_handle_payment(
&mut self,
entry_point_name: &str,
runtime_args: &RuntimeArgs,
access_rights: ContextAccessRights,
stack: RuntimeStack,
) -> Result<CLValue, ExecError> {
let gas_counter = self.gas_counter();
let handle_payment_hash = self.context.get_system_contract(HANDLE_PAYMENT)?;
let handle_payment_key = if self.context.engine_config().enable_entity {
Key::AddressableEntity(EntityAddr::System(handle_payment_hash.value()))
} else {
Key::Hash(handle_payment_hash.value())
};
let handle_payment_named_keys = self
.context
.state()
.borrow_mut()
.get_named_keys(EntityAddr::System(handle_payment_hash.value()))?;
let mut named_keys = handle_payment_named_keys;
let runtime_context = self.context.new_from_self(
handle_payment_key,
EntryPointType::Called,
&mut named_keys,
access_rights,
runtime_args.to_owned(),
);
let mut runtime = self.new_with_stack(runtime_context, stack);
let engine_config = self.context.engine_config();
let system_config = engine_config.system_config();
let handle_payment_costs = system_config.handle_payment_costs();
let result = match entry_point_name {
handle_payment::METHOD_GET_PAYMENT_PURSE => {
runtime.charge_system_contract_call(handle_payment_costs.get_payment_purse)?;
match self.context.maybe_payment_purse() {
Some(payment_purse) => CLValue::from_t(payment_purse).map_err(Self::reverter),
None => {
let payment_purse = runtime.get_payment_purse().map_err(Self::reverter)?;
self.context.set_payment_purse(payment_purse);
CLValue::from_t(payment_purse).map_err(Self::reverter)
}
}
}
handle_payment::METHOD_SET_REFUND_PURSE => (|| {
runtime.charge_system_contract_call(handle_payment_costs.set_refund_purse)?;
let purse: URef =
Self::get_named_argument(runtime_args, handle_payment::ARG_PURSE)?;
runtime.set_refund_purse(purse).map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
handle_payment::METHOD_GET_REFUND_PURSE => (|| {
runtime.charge_system_contract_call(handle_payment_costs.get_refund_purse)?;
let maybe_purse = runtime.get_refund_purse().map_err(Self::reverter)?;
CLValue::from_t(maybe_purse).map_err(Self::reverter)
})(),
_ => {
Ok(CLValue::unit())
}
};
self.gas(
runtime
.gas_counter()
.checked_sub(gas_counter)
.unwrap_or(gas_counter),
)?;
let ret = result?;
let urefs = utils::extract_urefs(&ret)?;
self.context.access_rights_extend(&urefs);
{
let transfers = self.context.transfers_mut();
runtime.context.transfers().clone_into(transfers);
}
Ok(ret)
}
fn call_host_auction(
&mut self,
entry_point_name: &str,
runtime_args: &RuntimeArgs,
access_rights: ContextAccessRights,
stack: RuntimeStack,
) -> Result<CLValue, ExecError> {
let gas_counter = self.gas_counter();
let auction_hash = self.context.get_system_contract(AUCTION)?;
let auction_key = if self.context.engine_config().enable_entity {
Key::AddressableEntity(EntityAddr::System(auction_hash.value()))
} else {
Key::Hash(auction_hash.value())
};
let auction_named_keys = self
.context
.state()
.borrow_mut()
.get_named_keys(EntityAddr::System(auction_hash.value()))?;
let mut named_keys = auction_named_keys;
let runtime_context = self.context.new_from_self(
auction_key,
EntryPointType::Called,
&mut named_keys,
access_rights,
runtime_args.to_owned(),
);
let mut runtime = self.new_with_stack(runtime_context, stack);
let engine_config = self.context.engine_config();
let system_config = engine_config.system_config();
let auction_costs = system_config.auction_costs();
let result = match entry_point_name {
auction::METHOD_GET_ERA_VALIDATORS => (|| {
runtime.charge_system_contract_call::<u64>(auction_costs.get_era_validators)?;
let result = runtime.get_era_validators().map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_ADD_BID => (|| {
runtime.charge_system_contract_call(auction_costs.add_bid)?;
let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
let delegation_rate =
Self::get_named_argument(runtime_args, auction::ARG_DELEGATION_RATE)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let global_minimum_delegation_amount =
self.context.engine_config().minimum_delegation_amount();
let minimum_delegation_amount = Self::try_get_named_argument(
runtime_args,
auction::ARG_MINIMUM_DELEGATION_AMOUNT,
)?;
let global_maximum_delegation_amount =
self.context.engine_config().maximum_delegation_amount();
let maximum_delegation_amount = Self::try_get_named_argument(
runtime_args,
auction::ARG_MAXIMUM_DELEGATION_AMOUNT,
)?;
let reserved_slots =
Self::try_get_named_argument(runtime_args, auction::ARG_RESERVED_SLOTS)?
.unwrap_or(0);
let max_delegators_per_validator =
self.context.engine_config().max_delegators_per_validator();
let minimum_delegation_rate = self.get_minimum_delegation_rate()?;
let minimum_bid_amount = self.context().engine_config().minimum_bid_amount();
let result = runtime
.add_bid(
public_key,
delegation_rate,
amount,
minimum_delegation_amount,
maximum_delegation_amount,
minimum_bid_amount,
max_delegators_per_validator,
reserved_slots,
global_minimum_delegation_amount,
global_maximum_delegation_amount,
minimum_delegation_rate,
)
.map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_WITHDRAW_BID => (|| {
runtime.charge_system_contract_call(auction_costs.withdraw_bid)?;
let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let min_bid_amount = self.context.engine_config().minimum_bid_amount();
let result = runtime
.withdraw_bid(public_key, amount, min_bid_amount)
.map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_DELEGATE => (|| {
runtime.charge_system_contract_call(auction_costs.delegate)?;
let delegator = {
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let uref: URef = match Self::get_named_argument(
runtime_args,
auction::ARG_DELEGATOR_PURSE,
) {
Ok(uref) => uref,
Err(err) => {
debug!(%err, "failed to get delegator purse argument");
return Err(err);
}
};
DelegatorKind::Purse(uref.addr())
}
}
};
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let max_delegators_per_validator =
self.context.engine_config().max_delegators_per_validator();
let result = runtime
.delegate(delegator, validator, amount, max_delegators_per_validator)
.map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_UNDELEGATE => (|| {
runtime.charge_system_contract_call(auction_costs.undelegate)?;
let delegator = {
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let uref: URef = match Self::get_named_argument(
runtime_args,
auction::ARG_DELEGATOR_PURSE,
) {
Ok(uref) => uref,
Err(err) => {
debug!(%err, "failed to get delegator purse argument");
return Err(err);
}
};
DelegatorKind::Purse(uref.addr())
}
}
};
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let result = runtime
.undelegate(delegator, validator, amount)
.map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_REDELEGATE => (|| {
runtime.charge_system_contract_call(auction_costs.redelegate)?;
let delegator = {
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let uref: URef = match Self::get_named_argument(
runtime_args,
auction::ARG_DELEGATOR_PURSE,
) {
Ok(uref) => uref,
Err(err) => {
debug!(%err, "failed to get delegator purse argument");
return Err(err);
}
};
DelegatorKind::Purse(uref.addr())
}
}
};
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let new_validator =
Self::get_named_argument(runtime_args, auction::ARG_NEW_VALIDATOR)?;
let result = runtime
.redelegate(delegator, validator, amount, new_validator)
.map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_RUN_AUCTION => (|| {
runtime.charge_system_contract_call(auction_costs.run_auction)?;
let era_end_timestamp_millis =
Self::get_named_argument(runtime_args, auction::ARG_ERA_END_TIMESTAMP_MILLIS)?;
let evicted_validators =
Self::get_named_argument(runtime_args, auction::ARG_EVICTED_VALIDATORS)?;
let max_delegators_per_validator =
self.context.engine_config().max_delegators_per_validator();
let minimum_bid_amount = self.context.engine_config().minimum_bid_amount();
runtime
.run_auction(
era_end_timestamp_millis,
evicted_validators,
max_delegators_per_validator,
true,
Ratio::new_raw(U512::from(1), U512::from(5)),
minimum_bid_amount,
)
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
auction::METHOD_SLASH => (|| {
runtime.charge_system_contract_call(auction_costs.slash)?;
let validator_public_keys =
Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR_PUBLIC_KEYS)?;
runtime
.slash(validator_public_keys)
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
auction::METHOD_DISTRIBUTE => (|| {
runtime.charge_system_contract_call(auction_costs.distribute)?;
let rewards_handling = self.context().engine_config().rewards_handling();
let rewards = Self::get_named_argument(runtime_args, auction::ARG_REWARDS_MAP)?;
let sustain_purse = match rewards_handling {
RewardsHandling::Standard => None,
RewardsHandling::Sustain { .. } => {
let sustain_purse = {
let mint_hash = self.context.get_system_contract(AUCTION)?;
match self
.context
.state()
.borrow_mut()
.get_named_keys(EntityAddr::System(mint_hash.value()))?
.get(MINT_SUSTAIN_PURSE_KEY)
{
Some(Key::URef(uref)) => Some(*uref),
Some(_) | None => None,
}
};
sustain_purse
}
};
runtime
.distribute(rewards, sustain_purse, rewards_handling)
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
auction::METHOD_READ_ERA_ID => (|| {
runtime.charge_system_contract_call(auction_costs.read_era_id)?;
let result = runtime.read_era_id().map_err(Self::reverter)?;
CLValue::from_t(result).map_err(Self::reverter)
})(),
auction::METHOD_ACTIVATE_BID => (|| {
runtime.charge_system_contract_call(auction_costs.activate_bid)?;
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
runtime
.activate_bid(validator, engine_config.minimum_bid_amount())
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
auction::METHOD_CHANGE_BID_PUBLIC_KEY => (|| {
runtime.charge_system_contract_call(auction_costs.change_bid_public_key)?;
let public_key = Self::get_named_argument(runtime_args, auction::ARG_PUBLIC_KEY)?;
let new_public_key =
Self::get_named_argument(runtime_args, auction::ARG_NEW_PUBLIC_KEY)?;
runtime
.change_bid_public_key(public_key, new_public_key)
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
auction::METHOD_ADD_RESERVATIONS => (|| {
runtime.charge_system_contract_call(auction_costs.add_reservations)?;
let reservations =
Self::get_named_argument(runtime_args, auction::ARG_RESERVATIONS)?;
let minimum_delegation_rate = self.get_minimum_delegation_rate()?;
runtime
.add_reservations(reservations, minimum_delegation_rate)
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
auction::METHOD_CANCEL_RESERVATIONS => (|| {
runtime.charge_system_contract_call(auction_costs.cancel_reservations)?;
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let delegators = Self::get_named_argument(runtime_args, auction::ARG_DELEGATORS)?;
let max_delegators_per_validator =
self.context.engine_config().max_delegators_per_validator();
runtime
.cancel_reservations(validator, delegators, max_delegators_per_validator)
.map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),
_ => {
Ok(CLValue::unit())
}
};
self.gas(
runtime
.gas_counter()
.checked_sub(gas_counter)
.unwrap_or(gas_counter),
)?;
let ret = result?;
let urefs = utils::extract_urefs(&ret)?;
self.context.access_rights_extend(&urefs);
{
let transfers = self.context.transfers_mut();
runtime.context.transfers().clone_into(transfers);
}
Ok(ret)
}
pub(crate) fn call_contract_with_stack(
&mut self,
contract_hash: AddressableEntityHash,
entry_point_name: &str,
args: RuntimeArgs,
stack: RuntimeStack,
) -> Result<CLValue, ExecError> {
self.stack = Some(stack);
self.call_contract(contract_hash, entry_point_name, args)
}
pub fn call_package_version_with_stack(
&mut self,
contract_package_hash: PackageHash,
protocol_version_major: Option<ProtocolVersionMajor>,
version: Option<EntityVersion>,
entry_point_name: String,
args: RuntimeArgs,
stack: RuntimeStack,
) -> Result<CLValue, ExecError> {
self.stack = Some(stack);
self.call_package_version(
contract_package_hash,
protocol_version_major,
version,
entry_point_name,
args,
)
}
pub(crate) fn execute_module_bytes(
&mut self,
module_bytes: &Bytes,
stack: RuntimeStack,
) -> Result<CLValue, ExecError> {
let protocol_version = self.context.protocol_version();
let engine_config = self.context.engine_config();
let wasm_config = engine_config.wasm_config();
#[cfg(feature = "test-support")]
let max_stack_height = wasm_config.v1().max_stack_height();
let module = preprocess(*wasm_config, module_bytes)?;
let (instance, memory) =
utils::instance_and_memory(module.clone(), protocol_version, engine_config)?;
self.memory = Some(memory);
self.module = Some(module);
self.stack = Some(stack);
self.context.set_args(utils::attenuate_uref_in_args(
self.context.args().clone(),
self.context
.runtime_footprint()
.borrow()
.main_purse()
.expect("line 1183")
.addr(),
AccessRights::WRITE,
)?);
let result = instance.invoke_export(DEFAULT_ENTRY_POINT_NAME, &[], self);
let error = match result {
Err(error) => error,
Ok(_) => {
return Ok(self.take_host_buffer().unwrap_or(CLValue::from_t(())?));
}
};
#[cfg(feature = "test-support")]
dump_runtime_stack_info(instance, max_stack_height);
if let Some(host_error) = error.as_host_error() {
let downcasted_error = host_error.downcast_ref::<ExecError>();
return match downcasted_error {
Some(ExecError::Ret(ref _ret_urefs)) => self
.take_host_buffer()
.ok_or(ExecError::ExpectedReturnValue),
Some(error) => Err(error.clone()),
None => Err(ExecError::Interpreter(host_error.to_string())),
};
}
Err(ExecError::Interpreter(error.into()))
}
pub fn call_contract(
&mut self,
contract_hash: AddressableEntityHash,
entry_point_name: &str,
args: RuntimeArgs,
) -> Result<CLValue, ExecError> {
let contract_hash = contract_hash.value();
let identifier = CallContractIdentifier::Contract { contract_hash };
self.execute_contract(identifier, entry_point_name, args)
}
pub fn call_versioned_contract(
&mut self,
contract_package_hash: PackageHash,
contract_version: Option<EntityVersion>,
entry_point_name: String,
args: RuntimeArgs,
) -> Result<CLValue, ExecError> {
self.call_package_version(
contract_package_hash,
None,
contract_version,
entry_point_name,
args,
)
}
pub fn call_package_version(
&mut self,
contract_package_hash: PackageHash,
protocol_version_major: Option<ProtocolVersionMajor>,
version: Option<EntityVersion>,
entry_point_name: String,
args: RuntimeArgs,
) -> Result<CLValue, ExecError> {
let contract_package_hash = contract_package_hash.value();
let identifier = CallContractIdentifier::ContractPackage {
contract_package_hash,
version,
protocol_version_major,
};
self.execute_contract(identifier, &entry_point_name, args)
}
fn get_protocol_version_for_entity_version(
&self,
entity_version: EntityVersion,
package: &Package,
) -> Result<EntityVersionKey, ExecError> {
let enabled_versions = package.enabled_versions();
let current_protocol_version_major = self.context.protocol_version().value().major;
let mut possible_versions = vec![];
for protocol_version_major in (1..=current_protocol_version_major).rev() {
let entity_version_key = EntityVersionKey::new(protocol_version_major, entity_version);
if enabled_versions.get(&entity_version_key).is_some() {
possible_versions.push(entity_version_key)
}
}
if possible_versions.is_empty() {
return Err(ExecError::NoMatchingEntityVersionKey);
}
if possible_versions.len() > 1
&& self
.context
.engine_config()
.trap_on_ambiguous_entity_version
{
return Err(ExecError::AmbiguousEntityVersion);
}
possible_versions.sort();
let entity_version_key = possible_versions.pop().unwrap();
Ok(entity_version_key)
}
fn get_key_from_entity_addr(&self, entity_addr: EntityAddr) -> Key {
if self.context().engine_config().enable_entity {
Key::AddressableEntity(entity_addr)
} else {
match entity_addr {
EntityAddr::System(system_hash_addr) => Key::Hash(system_hash_addr),
EntityAddr::Account(hash_addr) => Key::Account(AccountHash::new(hash_addr)),
EntityAddr::SmartContract(contract_hash_addr) => Key::Hash(contract_hash_addr),
}
}
}
fn get_context_key_for_contract_call(
&self,
entity_addr: EntityAddr,
entry_point: &EntityEntryPoint,
) -> Result<Key, ExecError> {
let current = self.context.entry_point_type();
let next = entry_point.entry_point_type();
match (current, next) {
(EntryPointType::Called, EntryPointType::Caller) => {
Err(ExecError::InvalidContext)
}
(EntryPointType::Factory, EntryPointType::Caller) => {
Err(ExecError::InvalidContext)
}
(EntryPointType::Caller, EntryPointType::Caller) => {
Ok(self.context.get_context_key())
}
(EntryPointType::Caller, EntryPointType::Called)
| (EntryPointType::Called, EntryPointType::Called) => {
Ok(self.get_key_from_entity_addr(entity_addr))
}
_ => {
Ok(self.get_key_from_entity_addr(entity_addr))
}
}
}
fn try_get_memory(&self) -> Result<&MemoryRef, ExecError> {
self.memory.as_ref().ok_or(ExecError::WasmPreprocessing(
PreprocessingError::MissingMemorySection,
))
}
fn try_get_module(&self) -> Result<&Module, ExecError> {
self.module.as_ref().ok_or(ExecError::WasmPreprocessing(
PreprocessingError::MissingModule,
))
}
fn try_get_stack(&self) -> Result<&RuntimeStack, ExecError> {
self.stack.as_ref().ok_or(ExecError::MissingRuntimeStack)
}
fn maybe_system_type(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
let is_mint = self.is_mint(hash_addr);
if is_mint.is_some() {
return is_mint;
};
let is_auction = self.is_auction(hash_addr);
if is_auction.is_some() {
return is_auction;
};
let is_handle = self.is_handle_payment(hash_addr);
if is_handle.is_some() {
return is_handle;
};
None
}
fn is_mint(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
let hash = match self.context.get_system_contract(MINT) {
Ok(hash) => hash,
Err(_) => {
error!("Failed to get system mint contract hash");
return None;
}
};
if hash.value() == hash_addr {
Some(SystemEntityType::Mint)
} else {
None
}
}
fn is_handle_payment(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
let hash = match self.context.get_system_contract(HANDLE_PAYMENT) {
Ok(hash) => hash,
Err(_) => {
error!("Failed to get system handle payment contract hash");
return None;
}
};
if hash.value() == hash_addr {
Some(SystemEntityType::HandlePayment)
} else {
None
}
}
fn is_auction(&self, hash_addr: HashAddr) -> Option<SystemEntityType> {
let hash = match self.context.get_system_contract(AUCTION) {
Ok(hash) => hash,
Err(_) => {
error!("Failed to get system auction contract hash");
return None;
}
};
if hash.value() == hash_addr {
Some(SystemEntityType::Auction)
} else {
None
}
}
fn execute_contract(
&mut self,
identifier: CallContractIdentifier,
entry_point_name: &str,
args: RuntimeArgs,
) -> Result<CLValue, ExecError> {
let (footprint, entity_addr, package) = match identifier {
CallContractIdentifier::Contract { contract_hash } => {
let entity_addr = if self.context.is_system_addressable_entity(&contract_hash)? {
EntityAddr::new_system(contract_hash)
} else {
EntityAddr::new_smart_contract(contract_hash)
};
let footprint = match self.context.read_gs(&Key::Hash(contract_hash))? {
Some(StoredValue::Contract(contract)) => {
if self.context.engine_config().enable_entity {
self.migrate_contract_and_contract_package(contract_hash)?;
};
let maybe_system_entity_type = self.maybe_system_type(contract_hash);
RuntimeFootprint::new_contract_footprint(
ContractHash::new(contract_hash),
contract,
maybe_system_entity_type,
)
}
Some(_) | None => {
if !self.context.engine_config().enable_entity {
return Err(ExecError::KeyNotFound(Key::Hash(contract_hash)));
}
let key = Key::AddressableEntity(entity_addr);
let entity = self.context.read_gs_typed::<AddressableEntity>(&key)?;
let entity_named_keys = self
.context
.state()
.borrow_mut()
.get_named_keys(entity_addr)?;
let entry_points = self.context.get_casper_vm_v1_entry_point(key)?;
RuntimeFootprint::new_entity_footprint(
entity_addr,
entity,
entity_named_keys,
entry_points,
)
}
};
let package_hash = footprint.package_hash().ok_or(ExecError::InvalidContext)?;
let package: Package = self.context.get_package(package_hash)?;
let is_calling_system_contract = self.is_system_contract(contract_hash)?;
let entity_hash = AddressableEntityHash::new(contract_hash);
let is_contract_enabled = package.is_entity_enabled(&entity_addr);
if !is_calling_system_contract && !is_contract_enabled {
return Err(ExecError::DisabledEntity(entity_hash));
}
(footprint, entity_addr, package)
}
CallContractIdentifier::ContractPackage {
contract_package_hash,
version,
protocol_version_major,
} => {
let package = self.context.get_package(contract_package_hash)?;
let entity_version_key = match (version, protocol_version_major) {
(Some(entity_version), Some(major)) => {
EntityVersionKey::new(major, entity_version)
}
(None, Some(major)) => package.current_entity_version_for(major),
(Some(entity_version), None) => {
match self.get_protocol_version_for_entity_version(entity_version, &package)
{
Ok(entity_version_key) => entity_version_key,
Err(err) => {
return Err(err);
}
}
}
(None, None) => match package.current_entity_version() {
Some(v) => v,
None => {
return Err(ExecError::NoActiveEntityVersions(
contract_package_hash.into(),
));
}
},
};
if package.is_version_missing(entity_version_key) {
return Err(ExecError::MissingEntityVersion(entity_version_key));
}
if !package.is_version_enabled(entity_version_key) {
return Err(ExecError::DisabledEntityVersion(entity_version_key));
}
let hash_addr = package
.lookup_entity_hash(entity_version_key)
.copied()
.ok_or(ExecError::MissingEntityVersion(entity_version_key))?
.value();
let entity_addr = if self.context.is_system_addressable_entity(&hash_addr)? {
EntityAddr::new_system(hash_addr)
} else {
EntityAddr::new_smart_contract(hash_addr)
};
let footprint = match self.context.read_gs(&Key::Hash(hash_addr))? {
Some(StoredValue::Contract(contract)) => {
if self.context.engine_config().enable_entity {
self.migrate_contract_and_contract_package(hash_addr)?;
};
let maybe_system_entity_type = self.maybe_system_type(hash_addr);
RuntimeFootprint::new_contract_footprint(
ContractHash::new(hash_addr),
contract,
maybe_system_entity_type,
)
}
Some(_) | None => {
if !self.context.engine_config().enable_entity {
return Err(ExecError::KeyNotFound(Key::Hash(hash_addr)));
}
let key = Key::AddressableEntity(entity_addr);
let entity = self.context.read_gs_typed::<AddressableEntity>(&key)?;
let entity_named_keys = self
.context
.state()
.borrow_mut()
.get_named_keys(entity_addr)?;
let entry_points = self.context.get_casper_vm_v1_entry_point(key)?;
RuntimeFootprint::new_entity_footprint(
entity_addr,
entity,
entity_named_keys,
entry_points,
)
}
};
(footprint, entity_addr, package)
}
};
if let EntityKind::Account(_) = footprint.entity_kind() {
return Err(ExecError::InvalidContext);
}
let entry_point = match footprint.entry_points().get(entry_point_name) {
Some(entry_point) => entry_point,
None => {
match footprint.entity_kind() {
EntityKind::System(_) => {
self.charge_system_contract_call(
self.context()
.engine_config()
.system_config()
.no_such_entrypoint(),
)?;
}
EntityKind::Account(_) => {}
EntityKind::SmartContract(_) => {}
}
return Err(ExecError::NoSuchMethod(entry_point_name.to_owned()));
}
};
let entry_point_type = entry_point.entry_point_type();
if self.context.engine_config().enable_entity && entry_point_type.is_invalid_context() {
return Err(ExecError::InvalidContext);
}
self.validate_entry_point_access(&package, entry_point_name, entry_point.access())?;
if self.context.engine_config().strict_argument_checking() {
let entry_point_args_lookup: BTreeMap<&str, &Parameter> = entry_point
.args()
.iter()
.map(|param| (param.name(), param))
.collect();
let args_lookup: BTreeMap<&str, &NamedArg> = args
.named_args()
.map(|named_arg| (named_arg.name(), named_arg))
.collect();
for (param_name, param) in entry_point_args_lookup {
if let Some(named_arg) = args_lookup.get(param_name) {
if param.cl_type() != named_arg.cl_value().cl_type() {
return Err(ExecError::type_mismatch(
param.cl_type().clone(),
named_arg.cl_value().cl_type().clone(),
));
}
} else if !param.cl_type().is_option() {
return Err(ExecError::MissingArgument {
name: param.name().to_string(),
});
}
}
}
let entity_hash = AddressableEntityHash::new(entity_addr.value());
if !self
.context
.engine_config()
.administrative_accounts()
.is_empty()
&& !package.is_entity_enabled(&entity_addr)
&& !self
.context
.is_system_addressable_entity(&entity_addr.value())?
{
return Err(ExecError::DisabledEntity(entity_hash));
}
let context_entity_key =
self.get_context_key_for_contract_call(entity_addr, entry_point)?;
let context_entity_hash = context_entity_key
.into_entity_hash_addr()
.ok_or(ExecError::UnexpectedKeyVariant(context_entity_key))?;
let (should_attenuate_urefs, should_validate_urefs) = {
let is_system_account =
self.context.get_initiator() == PublicKey::System.to_account_hash();
let is_caller_system_contract =
self.is_system_contract(self.context.access_rights().context_key())?;
let is_calling_system_contract = self.is_system_contract(context_entity_hash)?;
let should_attenuate_urefs =
!is_system_account && !is_caller_system_contract && !is_calling_system_contract;
let should_validate_urefs = !is_caller_system_contract || !is_calling_system_contract;
(should_attenuate_urefs, should_validate_urefs)
};
let runtime_args = if should_attenuate_urefs {
utils::attenuate_uref_in_args(
args,
self.context
.runtime_footprint()
.borrow()
.main_purse()
.expect("need purse for attenuation")
.addr(),
AccessRights::WRITE,
)?
} else {
args
};
let extended_access_rights = {
let mut all_urefs = vec![];
for arg in runtime_args.to_values() {
let urefs = utils::extract_urefs(arg)?;
if should_validate_urefs {
for uref in &urefs {
self.context.validate_uref(uref)?;
}
}
all_urefs.extend(urefs);
}
all_urefs
};
let (mut named_keys, access_rights) = match entry_point_type {
EntryPointType::Caller => {
let mut access_rights = self
.context
.runtime_footprint()
.borrow()
.extract_access_rights(context_entity_hash);
access_rights.extend(&extended_access_rights);
let named_keys = self
.context
.runtime_footprint()
.borrow()
.named_keys()
.clone();
(named_keys, access_rights)
}
EntryPointType::Called | EntryPointType::Factory => {
let mut access_rights = footprint.extract_access_rights(entity_hash.value());
access_rights.extend(&extended_access_rights);
let named_keys = footprint.named_keys().clone();
(named_keys, access_rights)
}
};
let stack = {
let mut stack = self.try_get_stack()?.clone();
let package_hash = match footprint.package_hash() {
Some(hash) => PackageHash::new(hash),
None => {
return Err(ExecError::UnexpectedStoredValueVariant);
}
};
let caller = if self.context.engine_config().enable_entity {
Caller::entity(package_hash, entity_addr)
} else {
Caller::smart_contract(
ContractPackageHash::new(package_hash.value()),
ContractHash::new(entity_addr.value()),
)
};
stack.push(caller)?;
stack
};
if let EntityKind::System(system_contract_type) = footprint.entity_kind() {
let entry_point_name = entry_point.name();
match system_contract_type {
SystemEntityType::Mint => {
return self.call_host_mint(
entry_point_name,
&runtime_args,
access_rights,
stack,
);
}
SystemEntityType::HandlePayment => {
return self.call_host_handle_payment(
entry_point_name,
&runtime_args,
access_rights,
stack,
);
}
SystemEntityType::Auction => {
return self.call_host_auction(
entry_point_name,
&runtime_args,
access_rights,
stack,
);
}
SystemEntityType::StandardPayment => {}
}
}
let module: Module = {
let byte_code_addr = footprint.wasm_hash().ok_or(ExecError::InvalidContext)?;
let byte_code_key = match footprint.entity_kind() {
EntityKind::System(_) | EntityKind::Account(_) => {
Key::ByteCode(ByteCodeAddr::Empty)
}
EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1) => {
if self.context.engine_config().enable_entity {
Key::ByteCode(ByteCodeAddr::new_wasm_addr(byte_code_addr))
} else {
Key::Hash(byte_code_addr)
}
}
EntityKind::SmartContract(runtime @ ContractRuntimeTag::VmCasperV2) => {
return Err(ExecError::IncompatibleRuntime(runtime));
}
};
let byte_code: ByteCode = match self.context.read_gs(&byte_code_key)? {
Some(StoredValue::ContractWasm(wasm)) => {
ByteCode::new(ByteCodeKind::V1CasperWasm, wasm.take_bytes())
}
Some(StoredValue::ByteCode(byte_code)) => byte_code,
Some(_) => {
return Err(ExecError::InvalidByteCode(ByteCodeHash::new(
byte_code_addr,
)))
}
None => return Err(ExecError::KeyNotFound(byte_code_key)),
};
casper_wasm::deserialize_buffer(byte_code.bytes())?
};
let context = self.context.new_from_self(
context_entity_key,
entry_point.entry_point_type(),
&mut named_keys,
access_rights,
runtime_args,
);
let (instance, memory) = utils::instance_and_memory(
module.clone(),
self.context.protocol_version(),
self.context.engine_config(),
)?;
let runtime = &mut Runtime::new_invocation_runtime(self, context, module, memory, stack);
let result = instance.invoke_export(entry_point.name(), &[], runtime);
self.context.set_gas_counter(runtime.context.gas_counter());
self.context
.set_emit_message_cost(runtime.context.emit_message_cost());
let transfers = self.context.transfers_mut();
runtime.context.transfers().clone_into(transfers);
match result {
Ok(_) => {
if self.context.entry_point_type() == EntryPointType::Caller
&& runtime.context.entry_point_type() == EntryPointType::Caller
{
*self.context.named_keys_mut() = runtime.context.named_keys().clone();
}
self.context
.set_remaining_spending_limit(runtime.context.remaining_spending_limit());
Ok(runtime.take_host_buffer().unwrap_or(CLValue::from_t(())?))
}
Err(error) => {
#[cfg(feature = "test-support")]
dump_runtime_stack_info(
instance,
self.context
.engine_config()
.wasm_config()
.v1()
.max_stack_height(),
);
if let Some(host_error) = error.as_host_error() {
let downcasted_error = host_error.downcast_ref::<ExecError>();
return match downcasted_error {
Some(ExecError::Ret(ref ret_urefs)) => {
self.context.access_rights_extend(ret_urefs);
if self.context.entry_point_type() == EntryPointType::Caller
&& runtime.context.entry_point_type() == EntryPointType::Caller
{
*self.context.named_keys_mut() =
runtime.context.named_keys().clone();
}
runtime
.take_host_buffer()
.ok_or(ExecError::ExpectedReturnValue)
}
Some(error) => Err(error.clone()),
None => Err(ExecError::Interpreter(host_error.to_string())),
};
}
Err(ExecError::Interpreter(error.into()))
}
}
}
fn call_contract_host_buffer(
&mut self,
contract_hash: AddressableEntityHash,
entry_point_name: &str,
args_bytes: &[u8],
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let args: RuntimeArgs = bytesrepr::deserialize_from_slice(args_bytes)?;
if let Some(payment_purse) = self.context.maybe_payment_purse() {
for named_arg in args.named_args() {
if utils::extract_urefs(named_arg.cl_value())?
.into_iter()
.any(|uref| uref.remove_access_rights() == payment_purse.remove_access_rights())
{
warn!("attempt to call_contract with payment purse");
return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
handle_payment::Error::AttemptToPersistPaymentPurse as u8,
))));
}
}
}
let result = self.call_contract(contract_hash, entry_point_name, args)?;
self.manage_call_contract_host_buffer(result_size_ptr, result)
}
fn call_versioned_contract_host_buffer(
&mut self,
contract_package_hash: PackageHash,
contract_version: Option<EntityVersion>,
entry_point_name: String,
args_bytes: &[u8],
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let args: RuntimeArgs = bytesrepr::deserialize_from_slice(args_bytes)?;
if let Some(payment_purse) = self.context.maybe_payment_purse() {
for named_arg in args.named_args() {
if utils::extract_urefs(named_arg.cl_value())?
.into_iter()
.any(|uref| uref.remove_access_rights() == payment_purse.remove_access_rights())
{
warn!("attempt to call_versioned_contract with payment purse");
return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
handle_payment::Error::AttemptToPersistPaymentPurse as u8,
))));
}
}
}
let result = self.call_versioned_contract(
contract_package_hash,
contract_version,
entry_point_name,
args,
)?;
self.manage_call_contract_host_buffer(result_size_ptr, result)
}
fn call_package_version_host_buffer(
&mut self,
contract_package_hash: PackageHash,
protocol_version_major: Option<ProtocolVersionMajor>,
contract_version: Option<EntityVersion>,
entry_point_name: String,
args_bytes: &[u8],
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let args: RuntimeArgs = bytesrepr::deserialize_from_slice(args_bytes)?;
if let Some(payment_purse) = self.context.maybe_payment_purse() {
for named_arg in args.named_args() {
if utils::extract_urefs(named_arg.cl_value())?
.into_iter()
.any(|uref| uref.remove_access_rights() == payment_purse.remove_access_rights())
{
warn!("attempt to call_versioned_contract with payment purse");
return Err(Into::into(ExecError::Revert(ApiError::HandlePayment(
handle_payment::Error::AttemptToPersistPaymentPurse as u8,
))));
}
}
}
let result = self.call_package_version(
contract_package_hash,
protocol_version_major,
contract_version,
entry_point_name,
args,
)?;
self.manage_call_contract_host_buffer(result_size_ptr, result)
}
fn check_host_buffer(&mut self) -> Result<(), ApiError> {
if !self.can_write_to_host_buffer() {
Err(ApiError::HostBufferFull)
} else {
Ok(())
}
}
fn manage_call_contract_host_buffer(
&mut self,
result_size_ptr: u32,
result: CLValue,
) -> Result<Result<(), ApiError>, ExecError> {
let result_size: u32 = match result.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
if result_size != 0 {
if let Err(error) = self.write_host_buffer(result) {
return Ok(Err(error));
}
}
let result_size_bytes = result_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(result_size_ptr, &result_size_bytes)
{
return Err(ExecError::Interpreter(error.into()));
}
Ok(Ok(()))
}
fn load_named_keys(
&mut self,
total_keys_ptr: u32,
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let total_keys: u32 = match self.context.named_keys().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
let total_keys_bytes = total_keys.to_le_bytes();
if let Err(error) = self
.try_get_memory()?
.set(total_keys_ptr, &total_keys_bytes)
{
return Err(ExecError::Interpreter(error.into()).into());
}
if total_keys == 0 {
return Ok(Ok(()));
}
let named_keys =
CLValue::from_t(self.context.named_keys().clone()).map_err(ExecError::CLValue)?;
let length: u32 = match named_keys.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
};
if let Err(error) = self.write_host_buffer(named_keys) {
return Ok(Err(error));
}
let length_bytes = length.to_le_bytes();
if let Err(error) = self.try_get_memory()?.set(result_size_ptr, &length_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn create_contract_package(
&mut self,
is_locked: PackageStatus,
) -> Result<(ContractPackage, URef), ExecError> {
let access_key = self.context.new_unit_uref()?;
let package_status = match is_locked {
PackageStatus::Locked => ContractPackageStatus::Locked,
PackageStatus::Unlocked => ContractPackageStatus::Unlocked,
};
let contract_package = ContractPackage::new(
access_key,
ContractVersions::default(),
DisabledVersions::default(),
Groups::default(),
package_status,
);
Ok((contract_package, access_key))
}
fn create_package(&mut self, is_locked: PackageStatus) -> Result<(Package, URef), ExecError> {
let access_key = self.context.new_unit_uref()?;
let contract_package = Package::new(
EntityVersions::new(),
BTreeSet::new(),
Groups::new(),
is_locked,
);
Ok((contract_package, access_key))
}
fn create_contract_package_at_hash(
&mut self,
lock_status: PackageStatus,
) -> Result<([u8; 32], [u8; 32]), ExecError> {
let addr = self.context.new_hash_address()?;
let access_key = if self.context.engine_config().enable_entity {
let (package, access_key) = self.create_package(lock_status)?;
self.context
.metered_write_gs_unsafe(Key::SmartContract(addr), package)?;
access_key
} else {
let (package, access_key) = self.create_contract_package(lock_status)?;
self.context
.metered_write_gs_unsafe(Key::Hash(addr), package)?;
access_key
};
Ok((addr, access_key.addr()))
}
fn create_contract_user_group_by_contract_package(
&mut self,
contract_package_hash: PackageHash,
label: String,
num_new_urefs: u32,
mut existing_urefs: BTreeSet<URef>,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
let mut contract_package: ContractPackage = self
.context
.get_validated_contract_package(contract_package_hash.value())?;
let groups = contract_package.groups_mut();
let new_group = Group::new(label);
if groups.contains(&new_group) {
return Ok(Err(addressable_entity::Error::GroupAlreadyExists.into()));
}
if groups.len() >= (addressable_entity::MAX_GROUPS as usize) {
return Ok(Err(addressable_entity::Error::MaxGroupsExceeded.into()));
}
let total_urefs: usize =
groups.total_urefs() + (num_new_urefs as usize) + existing_urefs.len();
if total_urefs > addressable_entity::MAX_TOTAL_UREFS {
let err = addressable_entity::Error::MaxTotalURefsExceeded;
return Ok(Err(ApiError::ContractHeader(err as u8)));
}
let mut new_urefs = Vec::with_capacity(num_new_urefs as usize);
for _ in 0..num_new_urefs {
let u = self.context.new_unit_uref()?;
new_urefs.push(u);
}
for u in new_urefs.iter().cloned() {
existing_urefs.insert(u);
}
groups.insert(new_group, existing_urefs);
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let new_urefs_value = CLValue::from_t(new_urefs)?;
let value_size = new_urefs_value.inner_bytes().len();
if let Err(err) = self.write_host_buffer(new_urefs_value) {
return Ok(Err(err));
}
let output_size_bytes = value_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(output_size_ptr, &output_size_bytes)
{
return Err(ExecError::Interpreter(error.into()));
}
self.context.metered_write_gs_unsafe(
ContractPackageHash::new(contract_package_hash.value()),
contract_package,
)?;
Ok(Ok(()))
}
fn create_contract_user_group(
&mut self,
contract_package_hash: PackageHash,
label: String,
num_new_urefs: u32,
mut existing_urefs: BTreeSet<URef>,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if !self.context.engine_config().enable_entity {
return self.create_contract_user_group_by_contract_package(
contract_package_hash,
label,
num_new_urefs,
existing_urefs,
output_size_ptr,
);
};
let mut contract_package: Package =
self.context.get_validated_package(contract_package_hash)?;
let groups = contract_package.groups_mut();
let new_group = Group::new(label);
if groups.contains(&new_group) {
return Ok(Err(addressable_entity::Error::GroupAlreadyExists.into()));
}
if groups.len() >= (addressable_entity::MAX_GROUPS as usize) {
return Ok(Err(addressable_entity::Error::MaxGroupsExceeded.into()));
}
let total_urefs: usize =
groups.total_urefs() + (num_new_urefs as usize) + existing_urefs.len();
if total_urefs > addressable_entity::MAX_TOTAL_UREFS {
let err = addressable_entity::Error::MaxTotalURefsExceeded;
return Ok(Err(ApiError::ContractHeader(err as u8)));
}
let mut new_urefs = Vec::with_capacity(num_new_urefs as usize);
for _ in 0..num_new_urefs {
let u = self.context.new_unit_uref()?;
new_urefs.push(u);
}
for u in new_urefs.iter().cloned() {
existing_urefs.insert(u);
}
groups.insert(new_group, existing_urefs);
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let new_urefs_value = CLValue::from_t(new_urefs)?;
let value_size = new_urefs_value.inner_bytes().len();
if let Err(err) = self.write_host_buffer(new_urefs_value) {
return Ok(Err(err));
}
let output_size_bytes = value_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(output_size_ptr, &output_size_bytes)
{
return Err(ExecError::Interpreter(error.into()));
}
self.context
.metered_write_gs_unsafe(contract_package_hash, contract_package)?;
Ok(Ok(()))
}
#[allow(clippy::too_many_arguments)]
fn add_contract_version(
&mut self,
package_hash: PackageHash,
version_ptr: u32,
entry_points: EntryPoints,
named_keys: NamedKeys,
message_topics: BTreeMap<String, MessageTopicOperation>,
output_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if self.context.engine_config().enable_entity {
self.add_contract_version_by_package(
package_hash,
version_ptr,
entry_points,
named_keys,
message_topics,
output_ptr,
)
} else {
self.add_contract_version_by_contract_package(
package_hash.value(),
version_ptr,
entry_points,
named_keys,
message_topics,
output_ptr,
)
}
}
#[allow(clippy::too_many_arguments)]
fn add_contract_version_by_contract_package(
&mut self,
contract_package_hash: HashAddr,
version_ptr: u32,
entry_points: EntryPoints,
mut named_keys: NamedKeys,
message_topics: BTreeMap<String, MessageTopicOperation>,
output_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if !self.context.install_upgrade_allowed() {
return Ok(Err(ApiError::NotAllowedToAddContractVersion));
}
self.context
.validate_key(&Key::Hash(contract_package_hash))?;
let mut contract_package: ContractPackage = self
.context
.get_validated_contract_package(contract_package_hash)?;
let version = contract_package.current_contract_version();
if contract_package.is_locked() && version.is_some() {
return Err(ExecError::LockedEntity(PackageHash::new(
contract_package_hash,
)));
}
for (_, key) in named_keys.iter() {
self.context.validate_key(key)?
}
let contract_wasm_hash = self.context.new_hash_address()?;
let contract_wasm = {
let module_bytes = self.get_module_from_entry_points(&entry_points)?;
ContractWasm::new(module_bytes)
};
let contract_hash_addr: HashAddr = self.context.new_hash_address()?;
let contract_entity_addr = EntityAddr::SmartContract(contract_hash_addr);
let protocol_version = self.context.protocol_version();
let major = protocol_version.value().major;
let maybe_previous_hash =
if let Some(previous_contract_hash) = contract_package.current_contract_hash() {
let previous_contract: Contract =
self.context.read_gs_typed(&previous_contract_hash.into())?;
let previous_named_keys = previous_contract.take_named_keys();
named_keys.append(previous_named_keys);
Some(EntityAddr::SmartContract(previous_contract_hash.value()))
} else {
None
};
if let Err(err) = self.carry_forward_message_topics(
maybe_previous_hash,
contract_entity_addr,
message_topics,
)? {
return Ok(Err(err));
};
let contract_package_hash = ContractPackageHash::new(contract_package_hash);
let contract = Contract::new(
contract_package_hash,
contract_wasm_hash.into(),
named_keys,
entry_points.into(),
protocol_version,
);
let insert_contract_result =
contract_package.insert_contract_version(major, contract_hash_addr.into());
self.context
.metered_write_gs_unsafe(Key::Hash(contract_wasm_hash), contract_wasm)?;
self.context
.metered_write_gs_unsafe(Key::Hash(contract_hash_addr), contract)?;
self.context
.metered_write_gs_unsafe(Key::Hash(contract_package_hash.value()), contract_package)?;
{
let hash_bytes = match contract_hash_addr.to_bytes() {
Ok(bytes) => bytes,
Err(error) => return Ok(Err(error.into())),
};
if let Err(error) = self.try_get_memory()?.set(output_ptr, &hash_bytes) {
return Err(ExecError::Interpreter(error.into()));
}
let version_value: u32 = insert_contract_result.contract_version();
let version_bytes = version_value.to_le_bytes();
if let Err(error) = self.try_get_memory()?.set(version_ptr, &version_bytes) {
return Err(ExecError::Interpreter(error.into()));
}
}
Ok(Ok(()))
}
#[allow(clippy::too_many_arguments)]
fn add_contract_version_by_package(
&mut self,
package_hash: PackageHash,
version_ptr: u32,
entry_points: EntryPoints,
mut named_keys: NamedKeys,
message_topics: BTreeMap<String, MessageTopicOperation>,
output_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if !self.context.install_upgrade_allowed() {
return Ok(Err(ApiError::NotAllowedToAddContractVersion));
}
if entry_points.contains_stored_session() {
return Err(ExecError::InvalidEntryPointType);
}
let mut package = self.context.get_package(package_hash.value())?;
if package.is_locked() {
return Err(ExecError::LockedEntity(package_hash));
}
let (
main_purse,
previous_named_keys,
action_thresholds,
associated_keys,
previous_hash_addr,
) = self.new_version_entity_parts(&package)?;
let byte_code_hash = self.context.new_hash_address()?;
let hash_addr = self.context.new_hash_address()?;
let entity_addr = EntityAddr::SmartContract(hash_addr);
if let Err(err) =
self.carry_forward_message_topics(previous_hash_addr, entity_addr, message_topics)?
{
return Ok(Err(err));
};
let protocol_version = self.context.protocol_version();
let insert_entity_version_result =
package.insert_entity_version(protocol_version.value().major, entity_addr);
let byte_code = {
let module_bytes = self.get_module_from_entry_points(&entry_points)?;
ByteCode::new(ByteCodeKind::V1CasperWasm, module_bytes)
};
self.context.metered_write_gs_unsafe(
Key::ByteCode(ByteCodeAddr::new_wasm_addr(byte_code_hash)),
byte_code,
)?;
let entity_addr = EntityAddr::new_smart_contract(hash_addr);
{
for (_, key) in named_keys.iter() {
self.context.validate_key(key)?;
}
named_keys.append(previous_named_keys);
for (name, key) in named_keys.iter() {
let named_key_value =
StoredValue::NamedKey(NamedKeyValue::from_concrete_values(*key, name.clone())?);
let named_key_addr = NamedKeyAddr::new_from_string(entity_addr, name.clone())?;
self.context
.metered_write_gs_unsafe(Key::NamedKey(named_key_addr), named_key_value)?;
}
self.context.write_entry_points(entity_addr, entry_points)?;
}
let entity = AddressableEntity::new(
package_hash,
byte_code_hash.into(),
protocol_version,
main_purse,
associated_keys,
action_thresholds,
EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1),
);
let entity_key = Key::AddressableEntity(entity_addr);
self.context.metered_write_gs_unsafe(entity_key, entity)?;
self.context
.metered_write_gs_unsafe(package_hash, package)?;
{
let hash_bytes = match hash_addr.to_bytes() {
Ok(bytes) => bytes,
Err(error) => return Ok(Err(error.into())),
};
if let Err(error) = self.try_get_memory()?.set(output_ptr, &hash_bytes) {
return Err(ExecError::Interpreter(error.into()));
}
let version_value: u32 = insert_entity_version_result.entity_version();
let version_bytes = version_value.to_le_bytes();
if let Err(error) = self.try_get_memory()?.set(version_ptr, &version_bytes) {
return Err(ExecError::Interpreter(error.into()));
}
}
Ok(Ok(()))
}
fn carry_forward_message_topics(
&mut self,
previous_entity_addr: Option<EntityAddr>,
entity_addr: EntityAddr,
message_topics: BTreeMap<String, MessageTopicOperation>,
) -> Result<Result<(), ApiError>, ExecError> {
let mut previous_message_topics = match previous_entity_addr {
Some(previous_hash) => self.context.get_message_topics(previous_hash)?,
None => MessageTopics::default(),
};
let max_topics_per_contract = self
.context
.engine_config()
.wasm_config()
.messages_limits()
.max_topics_per_contract();
let topics_to_add = message_topics
.iter()
.filter(|(_, operation)| match operation {
MessageTopicOperation::Add => true,
});
if previous_message_topics.len() + topics_to_add.clone().count()
> max_topics_per_contract as usize
{
return Ok(Err(ApiError::from(MessageTopicError::MaxTopicsExceeded)));
}
for (new_topic, _) in topics_to_add {
let topic_name_hash = cryptography::blake2b(new_topic.as_bytes()).into();
if let Err(e) = previous_message_topics.add_topic(new_topic.as_str(), topic_name_hash) {
return Ok(Err(e.into()));
}
}
for (topic_name, topic_hash) in previous_message_topics.iter() {
let topic_key = Key::message_topic(entity_addr, *topic_hash);
let block_time = self.context.get_block_info().block_time();
let summary = StoredValue::MessageTopic(MessageTopicSummary::new(
0,
block_time,
topic_name.clone(),
));
self.context.metered_write_gs_unsafe(topic_key, summary)?;
}
Ok(Ok(()))
}
fn new_version_entity_parts(
&mut self,
package: &Package,
) -> Result<
(
URef,
NamedKeys,
ActionThresholds,
AssociatedKeys,
Option<EntityAddr>,
),
ExecError,
> {
if let Some(previous_entity_hash) = package.current_entity_hash() {
let previous_entity_key = Key::AddressableEntity(previous_entity_hash);
let (mut previous_entity, requires_purse_creation) =
self.context.get_contract_entity(previous_entity_key)?;
let action_thresholds = previous_entity.action_thresholds().clone();
let associated_keys = previous_entity.associated_keys().clone();
if !previous_entity.can_upgrade_with(self.context.authorization_keys()) {
let account_hash = self.context.get_initiator();
let access_key = match self
.context
.read_gs(&Key::Hash(previous_entity.package_hash().value()))?
{
Some(StoredValue::ContractPackage(contract_package)) => {
contract_package.access_key()
}
Some(StoredValue::CLValue(cl_value)) => {
let (_key, uref) = cl_value
.into_t::<(Key, URef)>()
.map_err(ExecError::CLValue)?;
uref
}
Some(_other) => return Err(ExecError::UnexpectedStoredValueVariant),
None => {
return Err(ExecError::UpgradeAuthorizationFailure);
}
};
let has_access = self.context.validate_uref(&access_key).is_ok();
if has_access && !associated_keys.contains_key(&account_hash) {
previous_entity.add_associated_key(
account_hash,
*action_thresholds.upgrade_management(),
)?;
} else {
return Err(ExecError::UpgradeAuthorizationFailure);
}
}
let main_purse = if requires_purse_creation {
self.create_purse()?
} else {
previous_entity.main_purse()
};
let associated_keys = previous_entity.associated_keys().clone();
let previous_named_keys = self.context.get_named_keys(previous_entity_key)?;
return Ok((
main_purse,
previous_named_keys,
action_thresholds,
associated_keys,
Some(previous_entity_hash),
));
}
Ok((
self.create_purse()?,
NamedKeys::new(),
ActionThresholds::default(),
AssociatedKeys::new(self.context.get_initiator(), Weight::new(1)),
None,
))
}
fn disable_contract_version(
&mut self,
contract_package_hash: PackageHash,
contract_hash: AddressableEntityHash,
) -> Result<Result<(), ApiError>, ExecError> {
if self.context.engine_config().enable_entity {
let contract_package_key = Key::SmartContract(contract_package_hash.value());
self.context.validate_key(&contract_package_key)?;
let mut contract_package: Package =
self.context.get_validated_package(contract_package_hash)?;
if contract_package.is_locked() {
return Err(ExecError::LockedEntity(contract_package_hash));
}
if let Err(err) = contract_package
.disable_entity_version(EntityAddr::SmartContract(contract_hash.value()))
{
return Ok(Err(err.into()));
}
self.context
.metered_write_gs_unsafe(contract_package_key, contract_package)?;
} else {
let contract_package_key = Key::Hash(contract_package_hash.value());
self.context.validate_key(&contract_package_key)?;
let mut contract_package: ContractPackage = self
.context
.get_validated_contract_package(contract_package_hash.value())?;
if contract_package.is_locked() {
return Err(ExecError::LockedEntity(PackageHash::new(
contract_package_hash.value(),
)));
}
let contract_hash = ContractHash::new(contract_hash.value());
if let Err(err) = contract_package.disable_contract_version(contract_hash) {
return Ok(Err(err.into()));
}
self.context
.metered_write_gs_unsafe(contract_package_key, contract_package)?;
}
Ok(Ok(()))
}
fn enable_contract_version(
&mut self,
contract_package_hash: PackageHash,
contract_hash: AddressableEntityHash,
) -> Result<Result<(), ApiError>, ExecError> {
if self.context.engine_config().enable_entity {
let contract_package_key = Key::SmartContract(contract_package_hash.value());
self.context.validate_key(&contract_package_key)?;
let mut contract_package: Package =
self.context.get_validated_package(contract_package_hash)?;
if contract_package.is_locked() {
return Err(ExecError::LockedEntity(contract_package_hash));
}
if let Err(err) =
contract_package.enable_version(EntityAddr::SmartContract(contract_hash.value()))
{
return Ok(Err(err.into()));
}
self.context
.metered_write_gs_unsafe(contract_package_key, contract_package)?;
} else {
let contract_package_key = Key::Hash(contract_package_hash.value());
self.context.validate_key(&contract_package_key)?;
let mut contract_package: ContractPackage = self
.context
.get_validated_contract_package(contract_package_hash.value())?;
if contract_package.is_locked() {
return Err(ExecError::LockedEntity(PackageHash::new(
contract_package_hash.value(),
)));
}
let contract_hash = ContractHash::new(contract_hash.value());
if let Err(err) = contract_package.enable_contract_version(contract_hash) {
return Ok(Err(err.into()));
}
self.context
.metered_write_gs_unsafe(contract_package_key, contract_package)?;
}
Ok(Ok(()))
}
fn function_address(&mut self, hash_bytes: [u8; 32], dest_ptr: u32) -> Result<(), Trap> {
self.try_get_memory()?
.set(dest_ptr, &hash_bytes)
.map_err(|e| ExecError::Interpreter(e.into()).into())
}
fn new_uref(&mut self, uref_ptr: u32, value_ptr: u32, value_size: u32) -> Result<(), Trap> {
let cl_value = self.cl_value_from_mem(value_ptr, value_size)?; let uref = self.context.new_uref(StoredValue::CLValue(cl_value))?;
self.try_get_memory()?
.set(uref_ptr, &uref.into_bytes().map_err(ExecError::BytesRepr)?)
.map_err(|e| ExecError::Interpreter(e.into()).into())
}
fn write(
&mut self,
key_ptr: u32,
key_size: u32,
value_ptr: u32,
value_size: u32,
) -> Result<(), Trap> {
let key = self.key_from_mem(key_ptr, key_size)?;
let cl_value = self.cl_value_from_mem(value_ptr, value_size)?;
self.context
.metered_write_gs(key, cl_value)
.map_err(Into::into)
}
fn record_transfer(
&mut self,
maybe_to: Option<AccountHash>,
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<(), ExecError> {
if self.context.get_context_key() != self.context.get_system_entity_key(MINT)? {
return Err(ExecError::InvalidContext);
}
if self.context.phase() != Phase::Session {
return Ok(());
}
let txn_hash = self.context.get_transaction_hash();
let from = InitiatorAddr::AccountHash(self.context.get_initiator());
let fee = Gas::from(
self.context
.engine_config()
.system_config()
.mint_costs()
.transfer,
);
let transfer = Transfer::V2(TransferV2::new(
txn_hash, from, maybe_to, source, target, amount, fee, id,
));
self.context.transfers_mut().push(transfer);
Ok(())
}
fn record_era_info(&mut self, era_info: EraInfo) -> Result<(), ExecError> {
if self.context.get_initiator() != PublicKey::System.to_account_hash() {
return Err(ExecError::InvalidContext);
}
if self.context.get_context_key() != self.context.get_system_entity_key(AUCTION)? {
return Err(ExecError::InvalidContext);
}
if self.context.phase() != Phase::Session {
return Ok(());
}
self.context.write_era_info(Key::EraSummary, era_info);
Ok(())
}
fn add(
&mut self,
key_ptr: u32,
key_size: u32,
value_ptr: u32,
value_size: u32,
) -> Result<(), Trap> {
let key = self.key_from_mem(key_ptr, key_size)?;
let cl_value = self.cl_value_from_mem(value_ptr, value_size)?;
self.context
.metered_add_gs(key, cl_value)
.map_err(Into::into)
}
fn read(
&mut self,
key_ptr: u32,
key_size: u32,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let key = self.key_from_mem(key_ptr, key_size)?;
let cl_value = match self.context.read_gs(&key)? {
Some(stored_value) => {
CLValue::try_from(stored_value).map_err(ExecError::TypeMismatch)?
}
None => return Ok(Err(ApiError::ValueNotFound)),
};
let value_size: u32 = match cl_value.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
};
if let Err(error) = self.write_host_buffer(cl_value) {
return Ok(Err(error));
}
let value_bytes = value_size.to_le_bytes(); if let Err(error) = self.try_get_memory()?.set(output_size_ptr, &value_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn revert(&mut self, status: u32) -> Trap {
ExecError::Revert(status.into()).into()
}
fn can_manage_keys(&self) -> bool {
if self
.context
.engine_config()
.administrative_accounts()
.is_empty()
{
return self
.context
.runtime_footprint()
.borrow()
.can_manage_keys_with(self.context.authorization_keys());
}
if self
.context
.engine_config()
.is_administrator(&self.context.get_initiator())
{
return true;
}
self.context.is_authorized_by_admin()
}
fn add_associated_key(
&mut self,
account_hash_ptr: u32,
account_hash_size: usize,
weight_value: u8,
) -> Result<i32, Trap> {
let account_hash = {
let source_serialized = self.bytes_from_mem(account_hash_ptr, account_hash_size)?;
let source: AccountHash = bytesrepr::deserialize_from_slice(source_serialized)
.map_err(ExecError::BytesRepr)?;
source
};
let weight = Weight::new(weight_value);
if !self.can_manage_keys() {
return Ok(AddKeyFailure::PermissionDenied as i32);
}
match self.context.add_associated_key(account_hash, weight) {
Ok(_) => Ok(0),
Err(ExecError::AddKeyFailure(e)) => Ok(e as i32),
Err(e) => Err(e.into()),
}
}
fn remove_associated_key(
&mut self,
account_hash_ptr: u32,
account_hash_size: usize,
) -> Result<i32, Trap> {
let account_hash = {
let source_serialized = self.bytes_from_mem(account_hash_ptr, account_hash_size)?;
let source: AccountHash = bytesrepr::deserialize_from_slice(source_serialized)
.map_err(ExecError::BytesRepr)?;
source
};
if !self.can_manage_keys() {
return Ok(RemoveKeyFailure::PermissionDenied as i32);
}
match self.context.remove_associated_key(account_hash) {
Ok(_) => Ok(0),
Err(ExecError::RemoveKeyFailure(e)) => Ok(e as i32),
Err(e) => Err(e.into()),
}
}
fn update_associated_key(
&mut self,
account_hash_ptr: u32,
account_hash_size: usize,
weight_value: u8,
) -> Result<i32, Trap> {
let account_hash = {
let source_serialized = self.bytes_from_mem(account_hash_ptr, account_hash_size)?;
let source: AccountHash = bytesrepr::deserialize_from_slice(source_serialized)
.map_err(ExecError::BytesRepr)?;
source
};
let weight = Weight::new(weight_value);
if !self.can_manage_keys() {
return Ok(UpdateKeyFailure::PermissionDenied as i32);
}
match self.context.update_associated_key(account_hash, weight) {
Ok(_) => Ok(0),
Err(ExecError::UpdateKeyFailure(e)) => Ok(e as i32),
Err(e) => Err(e.into()),
}
}
fn set_action_threshold(
&mut self,
action_type_value: u32,
threshold_value: u8,
) -> Result<i32, Trap> {
if !self.can_manage_keys() {
return Ok(SetThresholdFailure::PermissionDeniedError as i32);
}
match ActionType::try_from(action_type_value) {
Ok(action_type) => {
let threshold = Weight::new(threshold_value);
match self.context.set_action_threshold(action_type, threshold) {
Ok(_) => Ok(0),
Err(ExecError::SetThresholdFailure(e)) => Ok(e as i32),
Err(error) => Err(error.into()),
}
}
Err(_) => Err(Trap::Code(TrapCode::Unreachable)),
}
}
fn get_mint_hash(&self) -> Result<AddressableEntityHash, ExecError> {
self.context.get_system_contract(MINT)
}
fn get_handle_payment_hash(&self) -> Result<AddressableEntityHash, ExecError> {
self.context.get_system_contract(HANDLE_PAYMENT)
}
fn get_standard_payment_hash(&self) -> Result<AddressableEntityHash, ExecError> {
self.context.get_system_contract(STANDARD_PAYMENT)
}
fn get_auction_hash(&self) -> Result<AddressableEntityHash, ExecError> {
self.context.get_system_contract(AUCTION)
}
fn mint_read_base_round_reward(
&mut self,
mint_contract_hash: AddressableEntityHash,
) -> Result<U512, ExecError> {
let gas_counter = self.gas_counter();
let call_result = self.call_contract(
mint_contract_hash,
mint::METHOD_READ_BASE_ROUND_REWARD,
RuntimeArgs::default(),
);
self.set_gas_counter(gas_counter);
let reward = call_result?.into_t()?;
Ok(reward)
}
fn mint_mint(
&mut self,
mint_contract_hash: AddressableEntityHash,
amount: U512,
) -> Result<URef, ExecError> {
let gas_counter = self.gas_counter();
let runtime_args = {
let mut runtime_args = RuntimeArgs::new();
runtime_args.insert(mint::ARG_AMOUNT, amount)?;
runtime_args
};
let call_result = self.call_contract(mint_contract_hash, mint::METHOD_MINT, runtime_args);
self.set_gas_counter(gas_counter);
let result: Result<URef, mint::Error> = call_result?.into_t()?;
Ok(result.map_err(system::Error::from)?)
}
fn mint_reduce_total_supply(
&mut self,
mint_contract_hash: AddressableEntityHash,
amount: U512,
) -> Result<(), ExecError> {
let gas_counter = self.gas_counter();
let runtime_args = {
let mut runtime_args = RuntimeArgs::new();
runtime_args.insert(mint::ARG_AMOUNT, amount)?;
runtime_args
};
let call_result = self.call_contract(
mint_contract_hash,
mint::METHOD_REDUCE_TOTAL_SUPPLY,
runtime_args,
);
self.set_gas_counter(gas_counter);
let result: Result<(), mint::Error> = call_result?.into_t()?;
Ok(result.map_err(system::Error::from)?)
}
fn mint_create(
&mut self,
mint_contract_hash: AddressableEntityHash,
) -> Result<URef, ExecError> {
let result =
self.call_contract(mint_contract_hash, mint::METHOD_CREATE, RuntimeArgs::new());
let purse = result?.into_t()?;
Ok(purse)
}
fn create_purse(&mut self) -> Result<URef, ExecError> {
let _scoped_host_function_flag = self.host_function_flag.enter_host_function_scope();
self.mint_create(self.get_mint_hash()?)
}
fn mint_transfer(
&mut self,
mint_contract_hash: AddressableEntityHash,
to: Option<AccountHash>,
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<Result<(), mint::Error>, ExecError> {
self.context.validate_uref(&source)?;
let args_values = {
let mut runtime_args = RuntimeArgs::new();
runtime_args.insert(mint::ARG_TO, to)?;
runtime_args.insert(mint::ARG_SOURCE, source)?;
runtime_args.insert(mint::ARG_TARGET, target)?;
runtime_args.insert(mint::ARG_AMOUNT, amount)?;
runtime_args.insert(mint::ARG_ID, id)?;
runtime_args
};
let gas_counter = self.gas_counter();
let call_result =
self.call_contract(mint_contract_hash, mint::METHOD_TRANSFER, args_values);
self.set_gas_counter(gas_counter);
Ok(call_result?.into_t()?)
}
fn transfer_to_new_account(
&mut self,
source: URef,
target: AccountHash,
amount: U512,
id: Option<u64>,
) -> Result<TransferResult, ExecError> {
let mint_contract_hash = self.get_mint_hash()?;
let allow_unrestricted_transfers =
self.context.engine_config().allow_unrestricted_transfers();
if !allow_unrestricted_transfers
&& self.context.get_initiator() != PublicKey::System.to_account_hash()
&& !self
.context
.engine_config()
.is_administrator(&self.context.get_initiator())
&& !self.context.engine_config().is_administrator(&target)
{
return Err(ExecError::DisabledUnrestrictedTransfers);
}
if amount > self.available_balance(source)?.unwrap_or_default() {
return Ok(Err(mint::Error::InsufficientFunds.into()));
}
let target_purse = self.mint_create(mint_contract_hash)?;
if source == target_purse {
return Ok(Err(mint::Error::EqualSourceAndTarget.into()));
}
let result = self.mint_transfer(
mint_contract_hash,
Some(target),
source,
target_purse.with_access_rights(AccessRights::ADD),
amount,
id,
);
self.context
.remove_access(target_purse.addr(), target_purse.access_rights());
match result? {
Ok(()) => {
let main_purse = target_purse;
if !self.context.engine_config().enable_entity {
let account = Account::create(target, NamedKeys::new(), target_purse);
self.context.metered_write_gs_unsafe(
Key::Account(target),
StoredValue::Account(account),
)?;
return Ok(Ok(TransferredTo::NewAccount));
}
let protocol_version = self.context.protocol_version();
let byte_code_hash = ByteCodeHash::default();
let entity_hash = AddressableEntityHash::new(target.value());
let package_hash = PackageHash::new(self.context.new_hash_address()?);
let associated_keys = AssociatedKeys::new(target, Weight::new(1));
let entity = AddressableEntity::new(
package_hash,
byte_code_hash,
protocol_version,
main_purse,
associated_keys,
ActionThresholds::default(),
EntityKind::Account(target),
);
let package = {
let mut package = Package::new(
EntityVersions::default(),
BTreeSet::default(),
Groups::default(),
PackageStatus::Locked,
);
package.insert_entity_version(
protocol_version.value().major,
EntityAddr::Account(target.value()),
);
package
};
let entity_key: Key = entity.entity_key(entity_hash);
self.context
.metered_write_gs_unsafe(entity_key, StoredValue::AddressableEntity(entity))?;
let contract_package_key: Key = package_hash.into();
self.context.metered_write_gs_unsafe(
contract_package_key,
StoredValue::SmartContract(package),
)?;
let contract_by_account = CLValue::from_t(entity_key)?;
let target_key = Key::Account(target);
self.context.metered_write_gs_unsafe(
target_key,
StoredValue::CLValue(contract_by_account),
)?;
Ok(Ok(TransferredTo::NewAccount))
}
Err(mint_error) => Ok(Err(mint_error.into())),
}
}
fn transfer_to_existing_account(
&mut self,
to: Option<AccountHash>,
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<TransferResult, ExecError> {
let mint_contract_key = self.get_mint_hash()?;
match self.mint_transfer(mint_contract_key, to, source, target, amount, id)? {
Ok(()) => Ok(Ok(TransferredTo::ExistingAccount)),
Err(error) => Ok(Err(error.into())),
}
}
fn transfer_to_account(
&mut self,
target: AccountHash,
amount: U512,
id: Option<u64>,
) -> Result<TransferResult, ExecError> {
let source = self.context.get_main_purse()?;
self.transfer_from_purse_to_account_hash(source, target, amount, id)
}
fn transfer_from_purse_to_account_hash(
&mut self,
source: URef,
target: AccountHash,
amount: U512,
id: Option<u64>,
) -> Result<TransferResult, ExecError> {
let _scoped_host_function_flag = self.host_function_flag.enter_host_function_scope();
let target_key = Key::Account(target);
match self.context.read_gs(&target_key)? {
None => {
self.transfer_to_new_account(source, target, amount, id)
}
Some(StoredValue::CLValue(entity_key_value)) => {
let entity_key = CLValue::into_t::<Key>(entity_key_value)?;
let target_uref = if let Some(StoredValue::AddressableEntity(entity)) =
self.context.read_gs(&entity_key)?
{
entity.main_purse_add_only()
} else {
let contract_hash = if let Some(entity_hash) = entity_key
.into_entity_hash_addr()
.map(AddressableEntityHash::new)
{
entity_hash
} else {
return Err(ExecError::UnexpectedKeyVariant(entity_key));
};
return Err(ExecError::InvalidEntity(contract_hash));
};
if source.with_access_rights(AccessRights::ADD) == target_uref {
return Ok(Ok(TransferredTo::ExistingAccount));
}
let granted_access = self.context.grant_access(target_uref);
let transfer_result = self.transfer_to_existing_account(
Some(target),
source,
target_uref,
amount,
id,
);
if let GrantedAccess::Granted {
uref_addr,
newly_granted_access_rights,
} = granted_access
{
self.context
.remove_access(uref_addr, newly_granted_access_rights)
}
transfer_result
}
Some(StoredValue::Account(account)) => {
self.transfer_from_purse_to_account(source, &account, amount, id)
}
Some(_) => {
Err(ExecError::AccountNotFound(target_key))
}
}
}
fn transfer_from_purse_to_account(
&mut self,
source: URef,
target_account: &Account,
amount: U512,
id: Option<u64>,
) -> Result<TransferResult, ExecError> {
let target_uref = target_account.main_purse_add_only();
if source.with_access_rights(AccessRights::ADD) == target_uref {
return Ok(Ok(TransferredTo::ExistingAccount));
}
let granted_access = self.context.grant_access(target_uref);
let transfer_result = self.transfer_to_existing_account(
Some(target_account.account_hash()),
source,
target_uref,
amount,
id,
);
if let GrantedAccess::Granted {
uref_addr,
newly_granted_access_rights,
} = granted_access
{
self.context
.remove_access(uref_addr, newly_granted_access_rights)
}
transfer_result
}
fn transfer_from_purse_to_purse(
&mut self,
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<Result<(), mint::Error>, ExecError> {
self.context.validate_uref(&source)?;
let mint_contract_key = self.get_mint_hash()?;
match self.mint_transfer(mint_contract_key, None, source, target, amount, id)? {
Ok(()) => Ok(Ok(())),
Err(mint_error) => Ok(Err(mint_error)),
}
}
fn total_balance(&mut self, purse: URef) -> Result<U512, ExecError> {
match self.context.total_balance(&purse) {
Ok(motes) => Ok(motes.value()),
Err(err) => Err(err),
}
}
fn available_balance(&mut self, purse: URef) -> Result<Option<U512>, ExecError> {
match self.context.available_balance(&purse) {
Ok(motes) => Ok(Some(motes.value())),
Err(err) => Err(err),
}
}
fn get_balance_host_buffer(
&mut self,
purse_ptr: u32,
purse_size: usize,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let purse: URef = {
let bytes = self.bytes_from_mem(purse_ptr, purse_size)?;
match bytesrepr::deserialize_from_slice(bytes) {
Ok(purse) => purse,
Err(error) => return Ok(Err(error.into())),
}
};
let balance = match self.available_balance(purse)? {
Some(balance) => balance,
None => return Ok(Err(ApiError::InvalidPurse)),
};
let balance_cl_value = match CLValue::from_t(balance) {
Ok(cl_value) => cl_value,
Err(error) => return Ok(Err(error.into())),
};
let balance_size = balance_cl_value.inner_bytes().len() as i32;
if let Err(error) = self.write_host_buffer(balance_cl_value) {
return Ok(Err(error));
}
let balance_size_bytes = balance_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(output_size_ptr, &balance_size_bytes)
{
return Err(ExecError::Interpreter(error.into()));
}
Ok(Ok(()))
}
fn get_system_contract(
&mut self,
system_contract_index: u32,
dest_ptr: u32,
_dest_size: u32,
) -> Result<Result<(), ApiError>, Trap> {
let hash: AddressableEntityHash = match SystemEntityType::try_from(system_contract_index) {
Ok(SystemEntityType::Mint) => self.get_mint_hash()?,
Ok(SystemEntityType::HandlePayment) => self.get_handle_payment_hash()?,
Ok(SystemEntityType::StandardPayment) => self.get_standard_payment_hash()?,
Ok(SystemEntityType::Auction) => self.get_auction_hash()?,
Err(error) => return Ok(Err(error)),
};
match self.try_get_memory()?.set(dest_ptr, hash.as_ref()) {
Ok(_) => Ok(Ok(())),
Err(error) => Err(ExecError::Interpreter(error.into()).into()),
}
}
pub fn take_host_buffer(&mut self) -> Option<CLValue> {
self.host_buffer.take()
}
fn can_write_to_host_buffer(&self) -> bool {
self.host_buffer.is_none()
}
fn write_host_buffer(&mut self, data: CLValue) -> Result<(), ApiError> {
match self.host_buffer {
Some(_) => return Err(ApiError::HostBufferFull),
None => self.host_buffer = Some(data),
}
Ok(())
}
fn read_host_buffer(
&mut self,
dest_ptr: u32,
dest_size: usize,
bytes_written_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
let (_cl_type, serialized_value) = match self.take_host_buffer() {
None => return Ok(Err(ApiError::HostBufferEmpty)),
Some(cl_value) => cl_value.destructure(),
};
if serialized_value.len() > u32::MAX as usize {
return Ok(Err(ApiError::OutOfMemory));
}
if serialized_value.len() > dest_size {
return Ok(Err(ApiError::BufferTooSmall));
}
let sliced_buf = &serialized_value[..cmp::min(dest_size, serialized_value.len())];
if let Err(error) = self.try_get_memory()?.set(dest_ptr, sliced_buf) {
return Err(ExecError::Interpreter(error.into()));
}
let bytes_written: u32 = sliced_buf
.len()
.try_into()
.expect("Size of buffer should fit within limit");
let bytes_written_data = bytes_written.to_le_bytes();
if let Err(error) = self
.try_get_memory()?
.set(bytes_written_ptr, &bytes_written_data)
{
return Err(ExecError::Interpreter(error.into()));
}
Ok(Ok(()))
}
#[cfg(feature = "test-support")]
fn print(&mut self, text_ptr: u32, text_size: u32) -> Result<(), Trap> {
let text = self.string_from_mem(text_ptr, text_size)?;
println!("{}", text); Ok(())
}
fn get_named_arg_size(
&mut self,
name_ptr: u32,
name_size: usize,
size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
let name_bytes = self.bytes_from_mem(name_ptr, name_size)?;
let name = String::from_utf8_lossy(&name_bytes);
let arg_size: u32 = match self.context.args().get(&name) {
Some(arg) if arg.inner_bytes().len() > u32::MAX as usize => {
return Ok(Err(ApiError::OutOfMemory));
}
Some(arg) => {
arg.inner_bytes()
.len()
.try_into()
.expect("Should fit within the range")
}
None => return Ok(Err(ApiError::MissingArgument)),
};
let arg_size_bytes = arg_size.to_le_bytes();
if let Err(e) = self.try_get_memory()?.set(size_ptr, &arg_size_bytes) {
return Err(ExecError::Interpreter(e.into()).into());
}
Ok(Ok(()))
}
fn get_named_arg(
&mut self,
name_ptr: u32,
name_size: usize,
output_ptr: u32,
output_size: usize,
) -> Result<Result<(), ApiError>, Trap> {
let name_bytes = self.bytes_from_mem(name_ptr, name_size)?;
let name = String::from_utf8_lossy(&name_bytes);
let arg = match self.context.args().get(&name) {
Some(arg) => arg,
None => return Ok(Err(ApiError::MissingArgument)),
};
if arg.inner_bytes().len() > output_size {
return Ok(Err(ApiError::OutOfMemory));
}
if let Err(error) = self
.try_get_memory()?
.set(output_ptr, &arg.inner_bytes()[..output_size])
{
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn validate_entry_point_access(
&self,
package: &Package,
name: &str,
access: &EntryPointAccess,
) -> Result<(), ExecError> {
match access {
EntryPointAccess::Public => Ok(()),
EntryPointAccess::Groups(group_names) => {
if group_names.is_empty() {
return Err(ExecError::InvalidContext);
}
let find_result = group_names.iter().find(|&group_name| {
package
.groups()
.get(group_name)
.and_then(|urefs| {
urefs
.iter()
.find(|&uref| self.context.validate_uref(uref).is_ok())
})
.is_some()
});
if find_result.is_none() {
return Err(ExecError::InvalidContext);
}
Ok(())
}
EntryPointAccess::Template => Err(ExecError::TemplateMethod(name.to_string())),
}
}
fn remove_contract_user_group(
&mut self,
package_key: PackageHash,
label: Group,
) -> Result<Result<(), ApiError>, ExecError> {
if self.context.engine_config().enable_entity {
let mut package: Package = self.context.get_validated_package(package_key)?;
let group_to_remove = Group::new(label);
if !package.groups().contains(&group_to_remove) {
return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into()));
}
let versions = package.versions();
for entity_hash in versions.contract_hashes() {
let entry_points = {
self.context
.get_casper_vm_v1_entry_point(Key::AddressableEntity(*entity_hash))?
};
for entry_point in entry_points.take_entry_points() {
match entry_point.access() {
EntryPointAccess::Public | EntryPointAccess::Template => {
continue;
}
EntryPointAccess::Groups(groups) => {
if groups.contains(&group_to_remove) {
return Ok(Err(addressable_entity::Error::GroupInUse.into()));
}
}
}
}
}
if !package.remove_group(&group_to_remove) {
return Ok(Err(addressable_entity::Error::GroupInUse.into()));
}
self.context.metered_write_gs_unsafe(package_key, package)?;
} else {
let mut contract_package = self
.context
.get_validated_contract_package(package_key.value())?;
let group_to_remove = Group::new(label);
if !contract_package.groups().contains(&group_to_remove) {
return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into()));
}
for (_version, contract_hash) in contract_package.versions().iter() {
let entry_points = {
self.context
.get_casper_vm_v1_entry_point(Key::contract_entity_key(
AddressableEntityHash::new(contract_hash.value()),
))?
};
for entry_point in entry_points.take_entry_points() {
match entry_point.access() {
EntryPointAccess::Public | EntryPointAccess::Template => {
continue;
}
EntryPointAccess::Groups(groups) => {
if groups.contains(&group_to_remove) {
return Ok(Err(addressable_entity::Error::GroupInUse.into()));
}
}
}
}
}
if !contract_package.remove_group(&group_to_remove) {
return Ok(Err(addressable_entity::Error::GroupInUse.into()));
}
self.context.metered_write_gs_unsafe(
ContractPackageHash::new(package_key.value()),
contract_package,
)?;
}
Ok(Ok(()))
}
#[allow(clippy::too_many_arguments)]
fn provision_contract_user_group_uref(
&mut self,
package_ptr: u32,
package_size: u32,
label_ptr: u32,
label_size: u32,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, ExecError> {
let contract_package_hash = self.t_from_mem(package_ptr, package_size)?;
let label: String = self.t_from_mem(label_ptr, label_size)?;
let new_uref = if self.context.engine_config().enable_entity {
let mut contract_package = self.context.get_validated_package(contract_package_hash)?;
let groups = contract_package.groups_mut();
let group_label = Group::new(label);
if groups.total_urefs() + 1 > addressable_entity::MAX_TOTAL_UREFS {
return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
}
let group = match groups.get_mut(&group_label) {
Some(group) if group.len() + 1 > addressable_entity::MAX_GROUPS as usize => {
return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
}
Some(group) => group,
None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
};
let new_uref = self.context.new_unit_uref()?;
if !group.insert(new_uref) {
return Ok(Err(addressable_entity::Error::URefAlreadyExists.into()));
}
self.context
.metered_write_gs_unsafe(contract_package_hash, contract_package)?;
new_uref
} else {
let mut contract_package = self
.context
.get_validated_contract_package(contract_package_hash.value())?;
let groups = contract_package.groups_mut();
let group_label = Group::new(label);
if groups.total_urefs() + 1 > addressable_entity::MAX_TOTAL_UREFS {
return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
}
let group = match groups.get_mut(&group_label) {
Some(group) if group.len() + 1 > addressable_entity::MAX_GROUPS as usize => {
return Ok(Err(addressable_entity::Error::MaxTotalURefsExceeded.into()));
}
Some(group) => group,
None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
};
let new_uref = self.context.new_unit_uref()?;
if !group.insert(new_uref) {
return Ok(Err(addressable_entity::Error::URefAlreadyExists.into()));
}
self.context.metered_write_gs_unsafe(
ContractPackageHash::new(contract_package_hash.value()),
contract_package,
)?;
new_uref
};
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let new_uref_value = CLValue::from_t(new_uref)?;
let value_size = new_uref_value.inner_bytes().len();
if let Err(err) = self.write_host_buffer(new_uref_value) {
return Ok(Err(err));
}
let output_size_bytes = value_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(output_size_ptr, &output_size_bytes)
{
return Err(ExecError::Interpreter(error.into()));
}
Ok(Ok(()))
}
#[allow(clippy::too_many_arguments)]
fn remove_contract_user_group_urefs(
&mut self,
package_ptr: u32,
package_size: u32,
label_ptr: u32,
label_size: u32,
urefs_ptr: u32,
urefs_size: u32,
) -> Result<Result<(), ApiError>, ExecError> {
let contract_package_hash: PackageHash = self.t_from_mem(package_ptr, package_size)?;
let label: String = self.t_from_mem(label_ptr, label_size)?;
let urefs: BTreeSet<URef> = self.t_from_mem(urefs_ptr, urefs_size)?;
if self.context.engine_config().enable_entity {
let mut contract_package = self.context.get_validated_package(contract_package_hash)?;
let groups = contract_package.groups_mut();
let group_label = Group::new(label);
let group = match groups.get_mut(&group_label) {
Some(group) => group,
None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
};
if urefs.is_empty() {
return Ok(Ok(()));
}
for uref in urefs {
if !group.remove(&uref) {
return Ok(Err(addressable_entity::Error::UnableToRemoveURef.into()));
}
}
self.context
.metered_write_gs_unsafe(contract_package_hash, contract_package)?;
} else {
let contract_package_hash = ContractPackageHash::new(contract_package_hash.value());
let mut contract_package = self
.context
.get_validated_contract_package(contract_package_hash.value())?;
let groups = contract_package.groups_mut();
let group_label = Group::new(label);
let group = match groups.get_mut(&group_label) {
Some(group) => group,
None => return Ok(Err(addressable_entity::Error::GroupDoesNotExist.into())),
};
if urefs.is_empty() {
return Ok(Ok(()));
}
for uref in urefs {
if !group.remove(&uref) {
return Ok(Err(addressable_entity::Error::UnableToRemoveURef.into()));
}
}
self.context
.metered_write_gs_unsafe(contract_package_hash, contract_package)?;
}
Ok(Ok(()))
}
fn charge_host_function_call<T>(
&mut self,
host_function: &HostFunction<T>,
weights: T,
) -> Result<(), Trap>
where
T: AsRef<[HostFunctionCost]> + Copy,
{
let cost = host_function
.calculate_gas_cost(weights)
.ok_or(ExecError::GasLimit)?; self.gas(cost)?;
Ok(())
}
fn new_dictionary(&mut self, output_size_ptr: u32) -> Result<Result<(), ApiError>, ExecError> {
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let new_uref = self.context.new_unit_uref()?;
let new_uref_value = CLValue::from_t(new_uref)?;
let value_size = new_uref_value.inner_bytes().len();
if let Err(err) = self.write_host_buffer(new_uref_value) {
return Ok(Err(err));
}
let output_size_bytes = value_size.to_le_bytes(); if let Err(error) = self
.try_get_memory()?
.set(output_size_ptr, &output_size_bytes)
{
return Err(ExecError::Interpreter(error.into()));
}
Ok(Ok(()))
}
fn dictionary_get(
&mut self,
uref_ptr: u32,
uref_size: u32,
dictionary_item_key_bytes_ptr: u32,
dictionary_item_key_bytes_size: u32,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if let Err(err) = self.check_host_buffer() {
return Ok(Err(err));
}
let uref: URef = self.t_from_mem(uref_ptr, uref_size)?;
let dictionary_item_key = self.checked_memory_slice(
dictionary_item_key_bytes_ptr as usize,
dictionary_item_key_bytes_size as usize,
|utf8_bytes| std::str::from_utf8(utf8_bytes).map(ToOwned::to_owned),
)?;
let dictionary_item_key = if let Ok(item_key) = dictionary_item_key {
item_key
} else {
return Ok(Err(ApiError::InvalidDictionaryItemKey));
};
let cl_value = match self.context.dictionary_get(uref, &dictionary_item_key)? {
Some(cl_value) => cl_value,
None => return Ok(Err(ApiError::ValueNotFound)),
};
let value_size: u32 = match cl_value.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
};
if let Err(error) = self.write_host_buffer(cl_value) {
return Ok(Err(error));
}
let value_bytes = value_size.to_le_bytes(); if let Err(error) = self.try_get_memory()?.set(output_size_ptr, &value_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn dictionary_read(
&mut self,
key_ptr: u32,
key_size: u32,
output_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let dictionary_key = self.key_from_mem(key_ptr, key_size)?;
let cl_value = match self.context.dictionary_read(dictionary_key)? {
Some(cl_value) => cl_value,
None => return Ok(Err(ApiError::ValueNotFound)),
};
let value_size: u32 = match cl_value.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::BufferTooSmall)),
};
if let Err(error) = self.write_host_buffer(cl_value) {
return Ok(Err(error));
}
let value_bytes = value_size.to_le_bytes(); if let Err(error) = self.try_get_memory()?.set(output_size_ptr, &value_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn dictionary_put(
&mut self,
uref_ptr: u32,
uref_size: u32,
key_ptr: u32,
key_size: u32,
value_ptr: u32,
value_size: u32,
) -> Result<Result<(), ApiError>, Trap> {
let uref: URef = self.t_from_mem(uref_ptr, uref_size)?;
let dictionary_item_key_bytes = {
if (key_size as usize) > DICTIONARY_ITEM_KEY_MAX_LENGTH {
return Ok(Err(ApiError::DictionaryItemKeyExceedsLength));
}
self.checked_memory_slice(key_ptr as usize, key_size as usize, |data| {
std::str::from_utf8(data).map(ToOwned::to_owned)
})?
};
let dictionary_item_key = if let Ok(item_key) = dictionary_item_key_bytes {
item_key
} else {
return Ok(Err(ApiError::InvalidDictionaryItemKey));
};
let cl_value = self.cl_value_from_mem(value_ptr, value_size)?;
if let Err(e) = self
.context
.dictionary_put(uref, &dictionary_item_key, cl_value)
{
return Err(Trap::from(e));
}
Ok(Ok(()))
}
fn is_system_immediate_caller(&self) -> Result<bool, ExecError> {
let immediate_caller = match self.get_immediate_caller() {
Some(call_stack_element) => call_stack_element,
None => {
return Ok(false);
}
};
match immediate_caller {
Caller::Initiator { account_hash } => {
Ok(account_hash == &PublicKey::System.to_account_hash())
}
Caller::SmartContract { contract_hash, .. } => Ok(self
.context
.is_system_addressable_entity(&contract_hash.value())?),
Caller::Entity { entity_addr, .. } => Ok(self
.context
.is_system_addressable_entity(&entity_addr.value())?),
}
}
fn load_authorization_keys(
&mut self,
len_ptr: u32,
result_size_ptr: u32,
) -> Result<Result<(), ApiError>, Trap> {
if !self.can_write_to_host_buffer() {
return Ok(Err(ApiError::HostBufferFull));
}
let authorization_keys = Vec::from_iter(self.context.authorization_keys().clone());
let total_keys: u32 = match authorization_keys.len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
let total_keys_bytes = total_keys.to_le_bytes();
if let Err(error) = self.try_get_memory()?.set(len_ptr, &total_keys_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
if total_keys == 0 {
return Ok(Ok(()));
}
let authorization_keys = CLValue::from_t(authorization_keys).map_err(ExecError::CLValue)?;
let length: u32 = match authorization_keys.inner_bytes().len().try_into() {
Ok(value) => value,
Err(_) => return Ok(Err(ApiError::OutOfMemory)),
};
if let Err(error) = self.write_host_buffer(authorization_keys) {
return Ok(Err(error));
}
let length_bytes = length.to_le_bytes();
if let Err(error) = self.try_get_memory()?.set(result_size_ptr, &length_bytes) {
return Err(ExecError::Interpreter(error.into()).into());
}
Ok(Ok(()))
}
fn prune(&mut self, key: Key) {
self.context.prune_gs_unsafe(key);
}
pub(crate) fn migrate_contract_and_contract_package(
&mut self,
hash_addr: HashAddr,
) -> Result<AddressableEntity, ExecError> {
let protocol_version = self.context.protocol_version();
let contract = self.context.get_contract(ContractHash::new(hash_addr))?;
let package_hash = contract.contract_package_hash();
self.context
.migrate_package(package_hash, protocol_version)?;
let entity_hash = AddressableEntityHash::new(hash_addr);
let key = Key::contract_entity_key(entity_hash);
self.context.read_gs_typed(&key)
}
fn add_message_topic(&mut self, topic_name: &str) -> Result<Result<(), ApiError>, ExecError> {
let topic_hash = cryptography::blake2b(topic_name).into();
self.context
.add_message_topic(topic_name, topic_hash)
.map(|ret| ret.map_err(ApiError::from))
}
fn emit_message(
&mut self,
topic_name: &str,
message: MessagePayload,
) -> Result<Result<(), ApiError>, Trap> {
let entity_addr = self.context.context_key_to_entity_addr()?;
let topic_name_hash = cryptography::blake2b(topic_name).into();
let topic_key = Key::Message(MessageAddr::new_topic_addr(entity_addr, topic_name_hash));
let Some(StoredValue::MessageTopic(prev_topic_summary)) =
self.context.read_gs(&topic_key)?
else {
return Ok(Err(ApiError::MessageTopicNotRegistered));
};
let current_blocktime = self.context.get_block_info().block_time();
let topic_message_index = if prev_topic_summary.blocktime() != current_blocktime {
for index in 1..prev_topic_summary.message_count() {
self.context
.prune_gs_unsafe(Key::message(entity_addr, topic_name_hash, index));
}
0
} else {
prev_topic_summary.message_count()
};
let block_message_index: u64 = match self
.context
.read_gs(&Key::BlockGlobal(BlockGlobalAddr::MessageCount))?
{
Some(stored_value) => {
let (prev_block_time, prev_count): (BlockTime, u64) = CLValue::into_t(
CLValue::try_from(stored_value).map_err(ExecError::TypeMismatch)?,
)
.map_err(ExecError::CLValue)?;
if prev_block_time == current_blocktime {
prev_count
} else {
0
}
}
None => 0,
};
let Some(topic_message_count) = topic_message_index.checked_add(1) else {
return Ok(Err(ApiError::MessageTopicFull));
};
let Some(block_message_count) = block_message_index.checked_add(1) else {
return Ok(Err(ApiError::MaxMessagesPerBlockExceeded));
};
self.context.metered_emit_message(
topic_key,
current_blocktime,
block_message_count,
topic_message_count,
Message::new(
entity_addr,
message,
topic_name.to_string(),
topic_name_hash,
topic_message_index,
block_message_index,
),
)?;
Ok(Ok(()))
}
fn get_minimum_delegation_rate(&self) -> Result<u8, ExecError> {
let auction_contract_hash = self.context.get_system_contract(AUCTION)?;
let auction_named_keys = self
.context
.state()
.borrow_mut()
.get_named_keys(EntityAddr::System(auction_contract_hash.value()))?;
let minimum_delegation_rate_key =
auction_named_keys.get(MINIMUM_DELEGATION_RATE_KEY).ok_or(
ExecError::NamedKeyNotFound(MINIMUM_DELEGATION_RATE_KEY.to_string()),
)?;
let stored_value = self
.context
.state()
.borrow_mut()
.read(minimum_delegation_rate_key)?
.ok_or(ExecError::KeyNotFound(*minimum_delegation_rate_key))?;
if let StoredValue::CLValue(cl_value) = stored_value {
let minimum_delegation_rate: u8 = cl_value.into_t().map_err(ExecError::CLValue)?;
Ok(minimum_delegation_rate)
} else {
Err(ExecError::UnexpectedStoredValueVariant)
}
}
}
#[cfg(feature = "test-support")]
fn dump_runtime_stack_info(instance: casper_wasmi::ModuleRef, max_stack_height: u32) {
let globals = instance.globals();
let Some(current_runtime_call_stack_height) = globals.last() else {
return;
};
if let RuntimeValue::I32(current_runtime_call_stack_height) =
current_runtime_call_stack_height.get()
{
if current_runtime_call_stack_height > max_stack_height as i32 {
eprintln!("runtime stack overflow, current={current_runtime_call_stack_height}, max={max_stack_height}");
}
};
}