pub mod balance;
pub mod chainspec_registry;
pub mod checksum_registry;
pub mod deploy_item;
pub mod engine_config;
pub mod era_validators;
mod error;
pub mod executable_deploy_item;
pub mod execute_request;
pub mod execution_effect;
pub mod execution_result;
pub mod genesis;
pub mod get_bids;
pub mod op;
mod prune;
pub mod query;
pub mod run_genesis_request;
pub mod step;
pub mod system_contract_registry;
mod transfer;
pub mod upgrade;
use log::info;
use std::{
cell::RefCell,
collections::{BTreeMap, BTreeSet},
convert::TryFrom,
rc::Rc,
};
use num::Zero;
use num_rational::Ratio;
use once_cell::sync::Lazy;
use tracing::{debug, error, trace, warn};
use casper_hashing::Digest;
use casper_types::{
account::{Account, AccountHash},
bytesrepr::ToBytes,
contracts::NamedKeys,
system::{
auction::{
EraValidators, ARG_ERA_END_TIMESTAMP_MILLIS, ARG_EVICTED_VALIDATORS,
ARG_REWARD_FACTORS, ARG_VALIDATOR_PUBLIC_KEYS, AUCTION_DELAY_KEY,
LOCKED_FUNDS_PERIOD_KEY, SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY, UNBONDING_DELAY_KEY,
VALIDATOR_SLOTS_KEY,
},
handle_payment::{self, ACCUMULATION_PURSE_KEY},
mint::{self, ROUND_SEIGNIORAGE_RATE_KEY},
AUCTION, HANDLE_PAYMENT, MINT, STANDARD_PAYMENT,
},
AccessRights, ApiError, BlockTime, CLValue, ContractHash, DeployHash, DeployInfo, Gas, Key,
KeyTag, Motes, Phase, ProtocolVersion, PublicKey, RuntimeArgs, StoredValue, URef, U512,
};
pub use self::{
balance::{BalanceRequest, BalanceResult},
chainspec_registry::ChainspecRegistry,
checksum_registry::ChecksumRegistry,
deploy_item::DeployItem,
engine_config::{
EngineConfig, EngineConfigBuilder, DEFAULT_MAX_QUERY_DEPTH,
DEFAULT_MAX_RUNTIME_CALL_STACK_HEIGHT,
},
era_validators::{GetEraValidatorsError, GetEraValidatorsRequest},
error::Error,
executable_deploy_item::{ExecutableDeployItem, ExecutableDeployItemIdentifier},
execute_request::ExecuteRequest,
execution::Error as ExecError,
execution_result::{ExecutionResult, ForcedTransferResult},
genesis::{ExecConfig, GenesisAccount, GenesisConfig, GenesisSuccess},
get_bids::{GetBidsRequest, GetBidsResult},
prune::{PruneConfig, PruneResult},
query::{QueryRequest, QueryResult},
run_genesis_request::RunGenesisRequest,
step::{RewardItem, SlashItem, StepError, StepRequest, StepSuccess},
system_contract_registry::SystemContractRegistry,
transfer::{TransferArgs, TransferRuntimeArgsBuilder, TransferTargetMode},
upgrade::{UpgradeConfig, UpgradeSuccess},
};
use self::{engine_config::FeeHandling, transfer::NewTransferTargetMode};
use crate::{
core::{
engine_state::{
executable_deploy_item::ExecutionKind,
execution_result::{ExecutionResultBuilder, ExecutionResults},
genesis::GenesisInstaller,
upgrade::{ProtocolUpgradeError, SystemUpgrader},
},
execution::{self, DirectSystemContractCall, Executor},
runtime::RuntimeStack,
tracking_copy::{TrackingCopy, TrackingCopyExt},
},
shared::{additive_map::AdditiveMap, newtypes::CorrelationId, transform::Transform},
storage::{
global_state::{
lmdb::LmdbGlobalState, scratch::ScratchGlobalState, CommitProvider, StateProvider,
StateReader,
},
trie::{merkle_proof::TrieMerkleProof, TrieRaw},
trie_store::operations::DeleteResult,
},
system::auction,
};
pub const MAX_PAYMENT_AMOUNT: u64 = 2_500_000_000;
pub static MAX_PAYMENT: Lazy<U512> = Lazy::new(|| U512::from(MAX_PAYMENT_AMOUNT));
pub const WASMLESS_TRANSFER_FIXED_GAS_PRICE: u64 = 1;
#[derive(Debug)]
pub struct EngineState<S> {
config: EngineConfig,
state: S,
}
impl EngineState<ScratchGlobalState> {
pub fn into_inner(self) -> ScratchGlobalState {
self.state
}
}
impl EngineState<LmdbGlobalState> {
pub fn get_state(&self) -> &LmdbGlobalState {
&self.state
}
pub fn flush_environment(&self) -> Result<(), lmdb::Error> {
if self.state.environment.is_manual_sync_enabled() {
self.state.environment.sync()?;
}
Ok(())
}
pub fn get_scratch_engine_state(&self) -> EngineState<ScratchGlobalState> {
EngineState {
config: self.config.clone(),
state: self.state.create_scratch(),
}
}
pub fn write_scratch_to_db(
&self,
state_root_hash: Digest,
scratch_global_state: ScratchGlobalState,
) -> Result<Digest, Error> {
let stored_values = scratch_global_state.into_inner();
self.state
.put_stored_values(CorrelationId::new(), state_root_hash, stored_values)
.map_err(Into::into)
}
}
impl<S> EngineState<S>
where
S: StateProvider + CommitProvider,
S::Error: Into<execution::Error>,
{
pub fn new(state: S, config: EngineConfig) -> EngineState<S> {
EngineState { config, state }
}
pub fn config(&self) -> &EngineConfig {
&self.config
}
pub fn update_config(&mut self, new_config: EngineConfig) {
self.config = new_config
}
pub fn commit_genesis(
&self,
correlation_id: CorrelationId,
genesis_config_hash: Digest,
protocol_version: ProtocolVersion,
ee_config: &ExecConfig,
chainspec_registry: ChainspecRegistry,
) -> Result<GenesisSuccess, Error> {
let initial_root_hash = self.state.empty_root();
let tracking_copy = match self.tracking_copy(initial_root_hash) {
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
Ok(None) => panic!("state has not been initialized properly"),
Err(error) => return Err(error),
};
let mut genesis_installer: GenesisInstaller<S> = GenesisInstaller::new(
genesis_config_hash,
protocol_version,
correlation_id,
ee_config.clone(),
tracking_copy,
);
genesis_installer.install(chainspec_registry)?;
let execution_effect = genesis_installer.finalize();
let post_state_hash = self
.state
.commit(
correlation_id,
initial_root_hash,
execution_effect.transforms.to_owned(),
)
.map_err(Into::<execution::Error>::into)?;
Ok(GenesisSuccess {
post_state_hash,
execution_effect,
})
}
pub fn commit_upgrade(
&self,
correlation_id: CorrelationId,
upgrade_config: UpgradeConfig,
) -> Result<UpgradeSuccess, Error> {
let pre_state_hash = upgrade_config.pre_state_hash();
let tracking_copy = match self.tracking_copy(pre_state_hash)? {
Some(tracking_copy) => Rc::new(RefCell::new(tracking_copy)),
None => return Err(Error::RootNotFound(pre_state_hash)),
};
let current_protocol_version = upgrade_config.current_protocol_version();
let new_protocol_version = upgrade_config.new_protocol_version();
let upgrade_check_result =
current_protocol_version.check_next_version(&new_protocol_version);
if upgrade_check_result.is_invalid() {
return Err(Error::InvalidProtocolVersion(new_protocol_version));
}
let registry = if let Ok(registry) = tracking_copy
.borrow_mut()
.get_system_contracts(correlation_id)
{
registry
} else {
let upgrade_registry = upgrade_config
.global_state_update()
.get(&Key::SystemContractRegistry)
.ok_or_else(|| {
error!("Registry is absent in upgrade config");
Error::ProtocolUpgrade(ProtocolUpgradeError::FailedToCreateSystemRegistry)
})?
.to_owned();
if let StoredValue::CLValue(cl_registry) = upgrade_registry {
CLValue::into_t::<SystemContractRegistry>(cl_registry).map_err(|error| {
let error_msg = format!("Conversion to system registry failed: {:?}", error);
error!("{}", error_msg);
Error::Bytesrepr(error_msg)
})?
} else {
error!("Failed to create registry as StoreValue in upgrade config is not CLValue");
return Err(Error::ProtocolUpgrade(
ProtocolUpgradeError::FailedToCreateSystemRegistry,
));
}
};
let mint_hash = registry.get(MINT).ok_or_else(|| {
error!("Missing system mint contract hash");
Error::MissingSystemContractHash(MINT.to_string())
})?;
let auction_hash = registry.get(AUCTION).ok_or_else(|| {
error!("Missing system auction contract hash");
Error::MissingSystemContractHash(AUCTION.to_string())
})?;
let standard_payment_hash = registry.get(STANDARD_PAYMENT).ok_or_else(|| {
error!("Missing system standard payment contract hash");
Error::MissingSystemContractHash(STANDARD_PAYMENT.to_string())
})?;
let handle_payment_hash = registry.get(HANDLE_PAYMENT).ok_or_else(|| {
error!("Missing system handle payment contract hash");
Error::MissingSystemContractHash(HANDLE_PAYMENT.to_string())
})?;
let cl_value_chainspec_registry =
CLValue::from_t(upgrade_config.chainspec_registry().clone())
.map_err(|error| Error::Bytesrepr(error.to_string()))?;
tracking_copy.borrow_mut().write(
Key::ChainspecRegistry,
StoredValue::CLValue(cl_value_chainspec_registry),
);
let system_upgrader: SystemUpgrader<S> = SystemUpgrader::new(
new_protocol_version,
current_protocol_version,
tracking_copy.clone(),
);
system_upgrader
.create_accumulation_purse_if_required(
correlation_id,
handle_payment_hash,
&self.config,
)
.map_err(Error::ProtocolUpgrade)?;
system_upgrader
.refresh_system_contracts(
correlation_id,
mint_hash,
auction_hash,
handle_payment_hash,
standard_payment_hash,
)
.map_err(Error::ProtocolUpgrade)?;
if let Some(new_validator_slots) = upgrade_config.new_validator_slots() {
let auction_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, *auction_hash)?;
let validator_slots_key = auction_contract.named_keys()[VALIDATOR_SLOTS_KEY];
let value = StoredValue::CLValue(
CLValue::from_t(new_validator_slots)
.map_err(|_| Error::Bytesrepr("new_validator_slots".to_string()))?,
);
tracking_copy.borrow_mut().write(validator_slots_key, value);
}
if let Some(new_auction_delay) = upgrade_config.new_auction_delay() {
debug!(%new_auction_delay, "Auction delay changed as part of the upgrade");
let auction_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, *auction_hash)?;
let auction_delay_key = auction_contract.named_keys()[AUCTION_DELAY_KEY];
let value = StoredValue::CLValue(
CLValue::from_t(new_auction_delay)
.map_err(|_| Error::Bytesrepr("new_auction_delay".to_string()))?,
);
tracking_copy.borrow_mut().write(auction_delay_key, value);
}
if let Some(new_locked_funds_period) = upgrade_config.new_locked_funds_period_millis() {
let auction_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, *auction_hash)?;
let locked_funds_period_key = auction_contract.named_keys()[LOCKED_FUNDS_PERIOD_KEY];
let value = StoredValue::CLValue(
CLValue::from_t(new_locked_funds_period)
.map_err(|_| Error::Bytesrepr("new_locked_funds_period".to_string()))?,
);
tracking_copy
.borrow_mut()
.write(locked_funds_period_key, value);
}
if let Some(new_round_seigniorage_rate) = upgrade_config.new_round_seigniorage_rate() {
let new_round_seigniorage_rate: Ratio<U512> = {
let (numer, denom) = new_round_seigniorage_rate.into();
Ratio::new(numer.into(), denom.into())
};
let mint_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, *mint_hash)?;
let locked_funds_period_key = mint_contract.named_keys()[ROUND_SEIGNIORAGE_RATE_KEY];
let value = StoredValue::CLValue(
CLValue::from_t(new_round_seigniorage_rate)
.map_err(|_| Error::Bytesrepr("new_round_seigniorage_rate".to_string()))?,
);
tracking_copy
.borrow_mut()
.write(locked_funds_period_key, value);
}
if let Some(new_unbonding_delay) = upgrade_config.new_unbonding_delay() {
let auction_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, *auction_hash)?;
let unbonding_delay_key = auction_contract.named_keys()[UNBONDING_DELAY_KEY];
let value = StoredValue::CLValue(
CLValue::from_t(new_unbonding_delay)
.map_err(|_| Error::Bytesrepr("new_unbonding_delay".to_string()))?,
);
tracking_copy.borrow_mut().write(unbonding_delay_key, value);
}
for (key, value) in upgrade_config.global_state_update() {
let is_unit_value = value.is_unit_cl_value();
if key.into_hash().is_some() && is_unit_value {
info!(
"found marker record for package overwrite {}",
key.to_formatted_string()
);
{
let contract_package =
match tracking_copy.borrow_mut().read(correlation_id, key) {
Ok(Some(StoredValue::ContractPackage(contract_package))) => {
contract_package
}
Ok(_) | Err(_) => {
error!(
"error in retrieving contract package: {}",
key.to_formatted_string()
);
continue;
}
};
for contract_hash in contract_package.versions().values() {
let contract_key = Key::Hash(contract_hash.value());
let mut contract = match tracking_copy
.borrow_mut()
.read(correlation_id, &contract_key)
{
Ok(Some(StoredValue::Contract(contract))) => contract,
Ok(_) | Err(_) => {
error!(
"error in retrieving contract : {}",
contract_key.to_formatted_string()
);
continue;
}
};
contract.clear_named_keys();
tracking_copy
.borrow_mut()
.write(contract_key, StoredValue::Contract(contract));
}
};
} else {
info!("update for key: {}", key.to_formatted_string());
tracking_copy.borrow_mut().write(*key, value.clone());
}
}
let execution_effect = tracking_copy.borrow().effect();
let post_state_hash = self
.state
.commit(
correlation_id,
pre_state_hash,
execution_effect.transforms.to_owned(),
)
.map_err(Into::into)?;
Ok(UpgradeSuccess {
post_state_hash,
execution_effect,
})
}
pub fn commit_prune(
&self,
correlation_id: CorrelationId,
prune_config: PruneConfig,
) -> Result<PruneResult, Error> {
let state_root_hash = prune_config.pre_state_hash();
match self.tracking_copy(state_root_hash)? {
None => return Ok(PruneResult::RootNotFound),
Some(_tracking_copy) => {}
};
let keys_to_delete = prune_config.keys_to_prune();
if keys_to_delete.is_empty() {
return Ok(PruneResult::Success {
post_state_hash: state_root_hash,
});
}
match self
.state
.delete_keys(correlation_id, state_root_hash, keys_to_delete)
{
Ok(DeleteResult::Deleted(post_state_hash)) => {
Ok(PruneResult::Success { post_state_hash })
}
Ok(DeleteResult::DoesNotExist) => Ok(PruneResult::DoesNotExist),
Ok(DeleteResult::RootNotFound) => Ok(PruneResult::RootNotFound),
Err(error) => Err(Error::Exec(error.into())),
}
}
pub fn tracking_copy(&self, hash: Digest) -> Result<Option<TrackingCopy<S::Reader>>, Error> {
match self.state.checkout(hash).map_err(Into::into)? {
Some(tc) => Ok(Some(TrackingCopy::new(tc))),
None => Ok(None),
}
}
pub fn run_query(
&self,
correlation_id: CorrelationId,
query_request: QueryRequest,
) -> Result<QueryResult, Error> {
let tracking_copy = match self.tracking_copy(query_request.state_hash())? {
Some(tracking_copy) => Rc::new(RefCell::new(tracking_copy)),
None => return Ok(QueryResult::RootNotFound),
};
let tracking_copy = tracking_copy.borrow();
Ok(tracking_copy
.query(
correlation_id,
self.config(),
query_request.key(),
query_request.path(),
)
.map_err(|err| Error::Exec(err.into()))?
.into())
}
pub fn run_execute(
&self,
correlation_id: CorrelationId,
mut exec_request: ExecuteRequest,
) -> Result<ExecutionResults, Error> {
let executor = Executor::new(self.config().clone());
let deploys = exec_request.take_deploys();
let mut results = ExecutionResults::with_capacity(deploys.len());
for deploy_item in deploys {
let result = match deploy_item.session {
ExecutableDeployItem::Transfer { .. } => self.transfer(
correlation_id,
&executor,
exec_request.protocol_version,
exec_request.parent_state_hash,
BlockTime::new(exec_request.block_time),
deploy_item,
exec_request.proposer.clone(),
),
_ => self.deploy(
correlation_id,
&executor,
exec_request.protocol_version,
exec_request.parent_state_hash,
BlockTime::new(exec_request.block_time),
deploy_item,
exec_request.proposer.clone(),
),
};
match result {
Ok(result) => results.push_back(result),
Err(error) => {
return Err(error);
}
};
}
Ok(results)
}
fn get_authorized_account(
&self,
correlation_id: CorrelationId,
account_hash: AccountHash,
authorization_keys: &BTreeSet<AccountHash>,
tracking_copy: Rc<RefCell<TrackingCopy<<S as StateProvider>::Reader>>>,
) -> Result<Account, Error> {
let account: Account = match tracking_copy
.borrow_mut()
.get_account(correlation_id, account_hash)
{
Ok(account) => account,
Err(_) => {
return Err(error::Error::Authorization);
}
};
let admin_set = self.config().administrative_accounts();
if !admin_set.is_empty() && admin_set.intersection(authorization_keys).next().is_some() {
return Ok(account);
}
if !account.can_authorize(authorization_keys) {
return Err(error::Error::Authorization);
}
if !account.can_deploy_with(authorization_keys) {
return Err(execution::Error::DeploymentAuthorizationFailure.into());
}
Ok(account)
}
pub fn get_purse_balance(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
purse_uref: URef,
) -> Result<BalanceResult, Error> {
let tracking_copy = match self.tracking_copy(state_hash)? {
Some(tracking_copy) => tracking_copy,
None => return Ok(BalanceResult::RootNotFound),
};
let purse_balance_key =
tracking_copy.get_purse_balance_key(correlation_id, purse_uref.into())?;
let (balance, proof) =
tracking_copy.get_purse_balance_with_proof(correlation_id, purse_balance_key)?;
let proof = Box::new(proof);
let motes = balance.value();
Ok(BalanceResult::Success { motes, proof })
}
#[allow(clippy::too_many_arguments)]
pub fn transfer(
&self,
correlation_id: CorrelationId,
executor: &Executor,
protocol_version: ProtocolVersion,
prestate_hash: Digest,
blocktime: BlockTime,
deploy_item: DeployItem,
proposer: PublicKey,
) -> Result<ExecutionResult, Error> {
let tracking_copy = match self.tracking_copy(prestate_hash) {
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
Ok(None) => return Err(Error::RootNotFound(prestate_hash)),
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
};
let account_hash = deploy_item.address;
let authorization_keys = deploy_item.authorization_keys;
let account = match self.get_authorized_account(
correlation_id,
account_hash,
&authorization_keys,
Rc::clone(&tracking_copy),
) {
Ok(account) => account,
Err(e) => return Ok(ExecutionResult::precondition_failure(e)),
};
let system_account = match tracking_copy
.borrow_mut()
.read_account(correlation_id, PublicKey::System.to_account_hash())
{
Ok(account) => account,
Err(error) => return Ok(ExecutionResult::precondition_failure(error.into())),
};
let system_contract_registry = tracking_copy
.borrow_mut()
.get_system_contracts(correlation_id)?;
let handle_payment_contract_hash = system_contract_registry
.get(HANDLE_PAYMENT)
.ok_or_else(|| {
error!("Missing system handle payment contract hash");
Error::MissingSystemContractHash(HANDLE_PAYMENT.to_string())
})?;
let handle_payment_contract = match tracking_copy
.borrow_mut()
.get_contract(correlation_id, *handle_payment_contract_hash)
{
Ok(contract) => contract,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error.into()));
}
};
let mut handle_payment_access_rights =
handle_payment_contract.extract_access_rights(*handle_payment_contract_hash);
let gas_limit = Gas::new(U512::from(std::u64::MAX));
let wasmless_transfer_gas_cost = Gas::new(U512::from(
self.config().system_config().wasmless_transfer_cost(),
));
let wasmless_transfer_motes = match Motes::from_gas(
wasmless_transfer_gas_cost,
WASMLESS_TRANSFER_FIXED_GAS_PRICE,
) {
Some(motes) => motes,
None => {
return Ok(ExecutionResult::precondition_failure(
Error::GasConversionOverflow,
))
}
};
let rewards_target_purse =
match self.get_rewards_purse(correlation_id, proposer, prestate_hash) {
Ok(target_purse) => target_purse,
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
};
let rewards_target_purse_balance_key = {
match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, rewards_target_purse.into())
{
Ok(balance_key) => balance_key,
Err(error) => return Ok(ExecutionResult::precondition_failure(Error::Exec(error))),
}
};
let account_main_purse = account.main_purse();
let account_main_purse_balance_key = match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, account_main_purse.into())
{
Ok(balance_key) => balance_key,
Err(error) => return Ok(ExecutionResult::precondition_failure(Error::Exec(error))),
};
let account_main_purse_balance = match tracking_copy
.borrow_mut()
.get_purse_balance(correlation_id, account_main_purse_balance_key)
{
Ok(balance_key) => balance_key,
Err(error) => return Ok(ExecutionResult::precondition_failure(Error::Exec(error))),
};
if account_main_purse_balance < wasmless_transfer_motes {
return Ok(ExecutionResult::precondition_failure(
Error::InsufficientPayment,
));
}
let make_charged_execution_failure = |error| match ExecutionResult::new_payment_code_error(
error,
wasmless_transfer_motes,
account_main_purse_balance,
wasmless_transfer_gas_cost,
account_main_purse_balance_key,
rewards_target_purse_balance_key,
) {
Ok(execution_result) => execution_result,
Err(error) => ExecutionResult::precondition_failure(error),
};
let mut runtime_args_builder =
TransferRuntimeArgsBuilder::new(deploy_item.session.args().clone());
let transfer_target_mode = match runtime_args_builder
.resolve_transfer_target_mode(correlation_id, Rc::clone(&tracking_copy))
{
Ok(transfer_target_mode) => transfer_target_mode,
Err(error) => return Ok(make_charged_execution_failure(error)),
};
if !self.config.allow_unrestricted_transfers()
&& !self.config.is_administrator(&account_hash)
{
match transfer_target_mode {
NewTransferTargetMode::ExistingAccount {
target_account_hash,
..
}
| NewTransferTargetMode::CreateAccount(target_account_hash) => {
let is_target_system_account =
target_account_hash == PublicKey::System.to_account_hash();
let is_target_administrator =
self.config.is_administrator(&target_account_hash);
if !(is_target_system_account || is_target_administrator) {
return Ok(make_charged_execution_failure(
execution::Error::DisabledUnrestrictedTransfers.into(),
));
}
}
NewTransferTargetMode::PurseExists(_) => {
return Ok(make_charged_execution_failure(
execution::Error::DisabledUnrestrictedTransfers.into(),
));
}
}
}
match transfer_target_mode {
NewTransferTargetMode::ExistingAccount { .. }
| NewTransferTargetMode::PurseExists(_) => {
}
NewTransferTargetMode::CreateAccount(account_hash) => {
let create_purse_stack = self.get_new_system_call_stack();
let (maybe_uref, execution_result): (Option<URef>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::CreatePurse,
RuntimeArgs::new(), &account,
authorization_keys.clone(),
blocktime,
deploy_item.deploy_hash,
gas_limit,
protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Session,
create_purse_stack,
U512::zero(),
);
match maybe_uref {
Some(main_purse) => {
let new_account = {
let named_keys = NamedKeys::default();
Account::create(account_hash, named_keys, main_purse)
};
tracking_copy.borrow_mut().write(
Key::Account(account_hash),
StoredValue::Account(new_account),
);
}
None => {
let error = execution_result
.take_error()
.unwrap_or(Error::InsufficientPayment);
return Ok(make_charged_execution_failure(error));
}
}
}
}
let transfer_args =
match runtime_args_builder.build(&account, correlation_id, Rc::clone(&tracking_copy)) {
Ok(transfer_args) => transfer_args,
Err(error) => return Ok(make_charged_execution_failure(error)),
};
let payment_uref;
let payment_result = {
let source_uref = transfer_args.source();
let source_purse_balance = if source_uref != account_main_purse {
let source_purse_balance_key = match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, Key::URef(source_uref))
{
Ok(purse_balance_key) => purse_balance_key,
Err(error) => return Ok(make_charged_execution_failure(Error::Exec(error))),
};
match tracking_copy
.borrow_mut()
.get_purse_balance(correlation_id, source_purse_balance_key)
{
Ok(purse_balance) => purse_balance,
Err(error) => return Ok(make_charged_execution_failure(Error::Exec(error))),
}
} else {
account_main_purse_balance
};
let transfer_amount_motes = Motes::new(transfer_args.amount());
match wasmless_transfer_motes.checked_add(transfer_amount_motes) {
Some(total_amount) if source_purse_balance < total_amount => {
return Ok(make_charged_execution_failure(Error::InsufficientPayment));
}
None => {
return Ok(make_charged_execution_failure(Error::InsufficientPayment));
}
Some(_) => {}
}
let get_payment_purse_stack = self.get_new_system_call_stack();
let (maybe_payment_uref, get_payment_purse_result): (Option<URef>, ExecutionResult) =
executor.call_system_contract(
DirectSystemContractCall::GetPaymentPurse,
RuntimeArgs::default(),
&account,
authorization_keys.clone(),
blocktime,
deploy_item.deploy_hash,
gas_limit,
protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Payment,
get_payment_purse_stack,
U512::zero(),
);
payment_uref = match maybe_payment_uref {
Some(payment_uref) => payment_uref,
None => return Ok(make_charged_execution_failure(Error::InsufficientPayment)),
};
if let Some(error) = get_payment_purse_result.take_error() {
return Ok(make_charged_execution_failure(error));
}
let new_transfer_args = TransferArgs::new(
transfer_args.to(),
transfer_args.source(),
payment_uref,
wasmless_transfer_motes.value(),
transfer_args.arg_id(),
);
let runtime_args = match RuntimeArgs::try_from(new_transfer_args) {
Ok(runtime_args) => runtime_args,
Err(error) => return Ok(make_charged_execution_failure(Error::Exec(error.into()))),
};
let transfer_to_payment_purse_stack = self.get_new_system_call_stack();
let (actual_result, payment_result): (Option<Result<(), u8>>, ExecutionResult) =
executor.call_system_contract(
DirectSystemContractCall::Transfer,
runtime_args,
&account,
authorization_keys.clone(),
blocktime,
deploy_item.deploy_hash,
gas_limit,
protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Payment,
transfer_to_payment_purse_stack,
wasmless_transfer_motes.value(),
);
if let Some(error) = payment_result.as_error().cloned() {
return Ok(make_charged_execution_failure(error));
}
let transfer_result = match actual_result {
Some(Ok(())) => Ok(()),
Some(Err(mint_error)) => match mint::Error::try_from(mint_error) {
Ok(mint_error) => Err(ApiError::from(mint_error)),
Err(_) => Err(ApiError::Transfer),
},
None => Err(ApiError::Transfer),
};
if let Err(error) = transfer_result {
return Ok(make_charged_execution_failure(Error::Exec(
ExecError::Revert(error),
)));
}
let payment_purse_balance = {
let payment_purse_balance_key = match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, Key::URef(payment_uref))
{
Ok(payment_purse_balance_key) => payment_purse_balance_key,
Err(error) => return Ok(make_charged_execution_failure(Error::Exec(error))),
};
match tracking_copy
.borrow_mut()
.get_purse_balance(correlation_id, payment_purse_balance_key)
{
Ok(payment_purse_balance) => payment_purse_balance,
Err(error) => return Ok(make_charged_execution_failure(Error::Exec(error))),
}
};
let payment_gas =
match Gas::from_motes(payment_purse_balance, WASMLESS_TRANSFER_FIXED_GAS_PRICE) {
Some(gas) => gas,
None => {
return Ok(make_charged_execution_failure(Error::GasConversionOverflow))
}
};
debug_assert_eq!(payment_gas, wasmless_transfer_gas_cost);
payment_result.with_cost(payment_gas)
};
let runtime_args = match RuntimeArgs::try_from(transfer_args) {
Ok(runtime_args) => runtime_args,
Err(error) => {
return Ok(make_charged_execution_failure(
ExecError::from(error).into(),
))
}
};
let transfer_stack = self.get_new_system_call_stack();
let (_, mut session_result): (Option<Result<(), u8>>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::Transfer,
runtime_args,
&account,
authorization_keys.clone(),
blocktime,
deploy_item.deploy_hash,
gas_limit,
protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Session,
transfer_stack,
transfer_args.amount(),
);
session_result = session_result.with_cost(Gas::default());
let finalize_result = {
let handle_payment_args = {
let finalize_cost_motes = {
debug_assert_eq!(payment_result.cost(), wasmless_transfer_gas_cost);
wasmless_transfer_motes
};
let account = deploy_item.address;
let maybe_runtime_args = RuntimeArgs::try_new(|args| {
args.insert(handle_payment::ARG_AMOUNT, finalize_cost_motes.value())?;
args.insert(handle_payment::ARG_ACCOUNT, account)?;
args.insert(handle_payment::ARG_TARGET, rewards_target_purse)?;
Ok(())
});
match maybe_runtime_args {
Ok(runtime_args) => runtime_args,
Err(error) => {
let exec_error = ExecError::from(error);
return Ok(ExecutionResult::precondition_failure(exec_error.into()));
}
}
};
let tc = tracking_copy.borrow();
let finalization_tc = Rc::new(RefCell::new(tc.fork()));
let finalize_payment_stack = self.get_new_system_call_stack();
handle_payment_access_rights.extend(&[payment_uref, rewards_target_purse]);
let (_ret, finalize_result): (Option<()>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::FinalizePayment,
handle_payment_args,
&system_account,
authorization_keys,
blocktime,
deploy_item.deploy_hash,
gas_limit,
protocol_version,
correlation_id,
finalization_tc,
Phase::FinalizePayment,
finalize_payment_stack,
U512::from(self.config().system_config().wasmless_transfer_cost()),
);
finalize_result
};
{
let transfers = session_result.transfers();
let cost = wasmless_transfer_gas_cost.value();
let deploy_info = DeployInfo::new(
deploy_item.deploy_hash,
transfers,
account.account_hash(),
account.main_purse(),
cost,
);
tracking_copy.borrow_mut().write(
Key::DeployInfo(deploy_item.deploy_hash),
StoredValue::DeployInfo(deploy_info),
);
}
if session_result.is_success() {
session_result = session_result.with_journal(tracking_copy.borrow().execution_journal())
}
let mut execution_result_builder = ExecutionResultBuilder::new();
execution_result_builder.set_payment_execution_result(payment_result);
execution_result_builder.set_session_execution_result(session_result);
execution_result_builder.set_finalize_execution_result(finalize_result);
let execution_result = execution_result_builder
.build()
.expect("ExecutionResultBuilder not initialized properly");
Ok(execution_result)
}
#[allow(clippy::too_many_arguments)]
pub fn deploy(
&self,
correlation_id: CorrelationId,
executor: &Executor,
protocol_version: ProtocolVersion,
prestate_hash: Digest,
blocktime: BlockTime,
deploy_item: DeployItem,
proposer: PublicKey,
) -> Result<ExecutionResult, Error> {
let tracking_copy = match self.tracking_copy(prestate_hash) {
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
Ok(None) => return Err(Error::RootNotFound(prestate_hash)),
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
};
let authorization_keys = deploy_item.authorization_keys;
let account = {
let account_hash = deploy_item.address;
match self.get_authorized_account(
correlation_id,
account_hash,
&authorization_keys,
Rc::clone(&tracking_copy),
) {
Ok(account) => account,
Err(e) => return Ok(ExecutionResult::precondition_failure(e)),
}
};
let system_account = match tracking_copy
.borrow_mut()
.read_account(correlation_id, PublicKey::System.to_account_hash())
{
Ok(account) => account,
Err(error) => return Ok(ExecutionResult::precondition_failure(error.into())),
};
let payment = deploy_item.payment;
let session = deploy_item.session;
let deploy_hash = deploy_item.deploy_hash;
let session_args = session.args().clone();
let session_execution_kind = match ExecutionKind::new(
Rc::clone(&tracking_copy),
account.named_keys(),
session,
correlation_id,
&protocol_version,
Phase::Session,
) {
Ok(execution_kind) => execution_kind,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error));
}
};
let account_main_purse_balance_key: Key = {
let account_key = Key::URef(account.main_purse());
match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, account_key)
{
Ok(key) => key,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error.into()));
}
}
};
let account_main_purse_balance: Motes = match tracking_copy
.borrow_mut()
.get_purse_balance(correlation_id, account_main_purse_balance_key)
{
Ok(balance) => balance,
Err(error) => return Ok(ExecutionResult::precondition_failure(error.into())),
};
let max_payment_cost = Motes::new(*MAX_PAYMENT);
if account_main_purse_balance < max_payment_cost {
return Ok(ExecutionResult::precondition_failure(
Error::InsufficientPayment,
));
}
let system_contract_registry = tracking_copy
.borrow_mut()
.get_system_contracts(correlation_id)?;
let handle_payment_contract_hash = system_contract_registry
.get(HANDLE_PAYMENT)
.ok_or_else(|| {
error!("Missing system handle payment contract hash");
Error::MissingSystemContractHash(HANDLE_PAYMENT.to_string())
})?;
let handle_payment_contract = match tracking_copy
.borrow_mut()
.get_contract(correlation_id, *handle_payment_contract_hash)
{
Ok(contract) => contract,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error.into()));
}
};
let payment_purse_key = match handle_payment_contract
.named_keys()
.get(handle_payment::PAYMENT_PURSE_KEY)
{
Some(key) => *key,
None => return Ok(ExecutionResult::precondition_failure(Error::Deploy)),
};
let payment_purse_uref = payment_purse_key
.into_uref()
.ok_or(Error::InvalidKeyVariant)?;
let purse_balance_key = match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, payment_purse_key)
{
Ok(key) => key,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error.into()));
}
};
let rewards_target_purse =
match self.get_rewards_purse(correlation_id, proposer, prestate_hash) {
Ok(target_purse) => target_purse,
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
};
let rewards_target_purse_balance_key = {
match tracking_copy
.borrow_mut()
.get_purse_balance_key(correlation_id, rewards_target_purse.into())
{
Ok(key) => key,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error.into()));
}
}
};
let mut execution_result_builder = execution_result::ExecutionResultBuilder::new();
let payment_result = {
let payment_gas_limit = match Gas::from_motes(max_payment_cost, deploy_item.gas_price) {
Some(gas) => gas,
None => {
return Ok(ExecutionResult::precondition_failure(
Error::GasConversionOverflow,
))
}
};
let phase = Phase::Payment;
let payment_stack = RuntimeStack::from_account_hash(
deploy_item.address,
self.config.max_runtime_call_stack_height() as usize,
);
let payment_access_rights = account.extract_access_rights();
let mut payment_named_keys = account.named_keys().clone();
let payment_args = payment.args().clone();
if payment.is_standard_payment(phase) {
executor.exec_standard_payment(
payment_args,
Key::Account(account.account_hash()),
&account,
&mut payment_named_keys,
payment_access_rights,
authorization_keys.clone(),
blocktime,
deploy_hash,
payment_gas_limit,
protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
phase,
payment_stack,
)
} else {
let payment_execution_kind = match ExecutionKind::new(
Rc::clone(&tracking_copy),
account.named_keys(),
payment,
correlation_id,
&protocol_version,
phase,
) {
Ok(execution_kind) => execution_kind,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error));
}
};
executor.exec(
payment_execution_kind,
payment_args,
&account,
&mut payment_named_keys,
payment_access_rights,
authorization_keys.clone(),
blocktime,
deploy_hash,
payment_gas_limit,
protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
phase,
payment_stack,
)
}
};
log_execution_result("payment result", &payment_result);
if should_charge_for_errors_in_wasm(&payment_result) {
let error = payment_result
.as_error()
.cloned()
.unwrap_or(Error::InsufficientPayment);
match ExecutionResult::new_payment_code_error(
error,
max_payment_cost,
account_main_purse_balance,
payment_result.cost(),
account_main_purse_balance_key,
rewards_target_purse_balance_key,
) {
Ok(execution_result) => return Ok(execution_result),
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
}
}
let payment_result_cost = payment_result.cost();
let payment_purse_balance: Motes = {
match tracking_copy
.borrow_mut()
.get_purse_balance(correlation_id, purse_balance_key)
{
Ok(balance) => balance,
Err(error) => {
return Ok(ExecutionResult::precondition_failure(error.into()));
}
}
};
if let Some(forced_transfer) =
payment_result.check_forced_transfer(payment_purse_balance, deploy_item.gas_price)
{
let error = match forced_transfer {
ForcedTransferResult::InsufficientPayment => Error::InsufficientPayment,
ForcedTransferResult::GasConversionOverflow => Error::GasConversionOverflow,
ForcedTransferResult::PaymentFailure => payment_result
.take_error()
.unwrap_or(Error::InsufficientPayment),
};
let gas_cost = match Gas::from_motes(max_payment_cost, deploy_item.gas_price) {
Some(gas) => gas,
None => {
return Ok(ExecutionResult::precondition_failure(
Error::GasConversionOverflow,
))
}
};
match ExecutionResult::new_payment_code_error(
error,
max_payment_cost,
account_main_purse_balance,
gas_cost,
account_main_purse_balance_key,
rewards_target_purse_balance_key,
) {
Ok(execution_result) => return Ok(execution_result),
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
}
};
execution_result_builder.set_payment_execution_result(payment_result);
let post_payment_tracking_copy = tracking_copy.borrow();
let session_tracking_copy = Rc::new(RefCell::new(post_payment_tracking_copy.fork()));
let session_stack = RuntimeStack::from_account_hash(
deploy_item.address,
self.config.max_runtime_call_stack_height() as usize,
);
let session_access_rights = account.extract_access_rights();
let mut session_named_keys = account.named_keys().clone();
let mut session_result = {
let session_gas_limit: Gas =
match Gas::from_motes(payment_purse_balance, deploy_item.gas_price)
.and_then(|gas| gas.checked_sub(payment_result_cost))
{
Some(gas) => gas,
None => {
return Ok(ExecutionResult::precondition_failure(
Error::GasConversionOverflow,
))
}
};
executor.exec(
session_execution_kind,
session_args,
&account,
&mut session_named_keys,
session_access_rights,
authorization_keys.clone(),
blocktime,
deploy_hash,
session_gas_limit,
protocol_version,
correlation_id,
Rc::clone(&session_tracking_copy),
Phase::Session,
session_stack,
)
};
log_execution_result("session result", &session_result);
{
let transfers = session_result.transfers();
let cost = payment_result_cost.value() + session_result.cost().value();
let deploy_info = DeployInfo::new(
deploy_hash,
transfers,
account.account_hash(),
account.main_purse(),
cost,
);
session_tracking_copy.borrow_mut().write(
Key::DeployInfo(deploy_hash),
StoredValue::DeployInfo(deploy_info),
);
}
if (session_result.cost().is_zero() && payment_purse_balance < max_payment_cost)
|| should_charge_for_errors_in_wasm(&session_result)
{
let error = session_result
.as_error()
.cloned()
.unwrap_or(Error::InsufficientPayment);
match ExecutionResult::new_payment_code_error(
error,
max_payment_cost,
account_main_purse_balance,
session_result.cost(),
account_main_purse_balance_key,
rewards_target_purse_balance_key,
) {
Ok(execution_result) => return Ok(execution_result),
Err(error) => return Ok(ExecutionResult::precondition_failure(error)),
}
}
let post_session_rc = if session_result.is_failure() {
Rc::new(RefCell::new(post_payment_tracking_copy.fork()))
} else {
session_result =
session_result.with_journal(session_tracking_copy.borrow().execution_journal());
session_tracking_copy
};
execution_result_builder.set_session_execution_result(session_result);
let finalize_result: ExecutionResult = {
let post_session_tc = post_session_rc.borrow();
let finalization_tc = Rc::new(RefCell::new(post_session_tc.fork()));
let handle_payment_args = {
let finalize_cost_motes = match Motes::from_gas(
execution_result_builder.total_cost(),
deploy_item.gas_price,
) {
Some(motes) => motes,
None => {
return Ok(ExecutionResult::precondition_failure(
Error::GasConversionOverflow,
))
}
};
let maybe_runtime_args = RuntimeArgs::try_new(|args| {
args.insert(handle_payment::ARG_AMOUNT, finalize_cost_motes.value())?;
args.insert(handle_payment::ARG_ACCOUNT, account.account_hash())?;
args.insert(handle_payment::ARG_TARGET, rewards_target_purse)?;
Ok(())
});
match maybe_runtime_args {
Ok(runtime_args) => runtime_args,
Err(error) => {
let exec_error = ExecError::from(error);
return Ok(ExecutionResult::precondition_failure(exec_error.into()));
}
}
};
let system_contract_registry = finalization_tc
.borrow_mut()
.get_system_contracts(correlation_id)?;
let handle_payment_contract_hash = system_contract_registry
.get(HANDLE_PAYMENT)
.ok_or_else(|| {
error!("Missing system handle payment contract hash");
Error::MissingSystemContractHash(HANDLE_PAYMENT.to_string())
})?;
let handle_payment_contract = match finalization_tc
.borrow_mut()
.get_contract(correlation_id, *handle_payment_contract_hash)
{
Ok(info) => info,
Err(error) => return Ok(ExecutionResult::precondition_failure(error.into())),
};
let mut handle_payment_access_rights =
handle_payment_contract.extract_access_rights(*handle_payment_contract_hash);
handle_payment_access_rights.extend(&[payment_purse_uref, rewards_target_purse]);
let gas_limit = Gas::new(U512::MAX);
let handle_payment_stack = self.get_new_system_call_stack();
let (_ret, finalize_result): (Option<()>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::FinalizePayment,
handle_payment_args,
&system_account,
authorization_keys,
blocktime,
deploy_hash,
gas_limit,
protocol_version,
correlation_id,
finalization_tc,
Phase::FinalizePayment,
handle_payment_stack,
U512::zero(),
);
finalize_result
};
execution_result_builder.set_finalize_execution_result(finalize_result);
let ret = execution_result_builder
.build()
.expect("ExecutionResultBuilder not initialized properly");
Ok(ret)
}
fn get_rewards_purse(
&self,
correlation_id: CorrelationId,
proposer: PublicKey,
prestate_hash: Digest,
) -> Result<URef, Error> {
let tracking_copy = match self.tracking_copy(prestate_hash) {
Err(error) => return Err(error),
Ok(None) => return Err(Error::RootNotFound(prestate_hash)),
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
};
match self.config.fee_handling() {
FeeHandling::PayToProposer => {
let proposer_account: Account = match tracking_copy
.borrow_mut()
.get_account(correlation_id, AccountHash::from(&proposer))
{
Ok(account) => account,
Err(error) => return Err(error.into()),
};
Ok(proposer_account.main_purse())
}
FeeHandling::Accumulate => {
let handle_payment_hash =
self.get_handle_payment_hash(correlation_id, prestate_hash)?;
let handle_payment_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, handle_payment_hash)?;
let accumulation_purse_uref = match handle_payment_contract
.named_keys()
.get(ACCUMULATION_PURSE_KEY)
{
Some(Key::URef(accumulation_purse)) => accumulation_purse,
Some(_) | None => {
error!(
"fee handling is configured to accumulate but handle payment does not \
have accumulation purse"
);
return Err(Error::FailedToRetrieveAccumulationPurse);
}
};
Ok(*accumulation_purse_uref)
}
FeeHandling::Burn => Ok(URef::default()),
}
}
pub fn apply_effect(
&self,
correlation_id: CorrelationId,
pre_state_hash: Digest,
effects: AdditiveMap<Key, Transform>,
) -> Result<Digest, Error> {
self.state
.commit(correlation_id, pre_state_hash, effects)
.map_err(|err| Error::Exec(err.into()))
}
pub fn get_trie_full(
&self,
correlation_id: CorrelationId,
trie_key: Digest,
) -> Result<Option<TrieRaw>, Error>
where
Error: From<S::Error>,
{
Ok(self.state.get_trie_full(correlation_id, &trie_key)?)
}
pub fn put_trie_if_all_children_present(
&self,
correlation_id: CorrelationId,
trie_bytes: &[u8],
) -> Result<Digest, Error>
where
Error: From<S::Error>,
{
let missing_children = self.state.missing_children(correlation_id, trie_bytes)?;
if missing_children.is_empty() {
Ok(self.state.put_trie(correlation_id, trie_bytes)?)
} else {
Err(Error::MissingTrieNodeChildren(missing_children))
}
}
pub fn get_era_validators(
&self,
correlation_id: CorrelationId,
system_contract_registry: Option<SystemContractRegistry>,
get_era_validators_request: GetEraValidatorsRequest,
) -> Result<EraValidators, GetEraValidatorsError> {
let state_root_hash = get_era_validators_request.state_hash();
let system_contract_registry = match system_contract_registry {
Some(system_contract_registry) => system_contract_registry,
None => match self.get_system_contract_registry(correlation_id, state_root_hash) {
Ok(system_contract_registry) => system_contract_registry,
Err(error) => {
error!(%state_root_hash, %error, "unable to get era validators");
return Err(error.into());
}
},
};
let auction_hash = system_contract_registry
.get(AUCTION)
.copied()
.ok_or_else(|| Error::MissingSystemContractHash(AUCTION.to_string()))?;
let query_request = QueryRequest::new(
state_root_hash,
auction_hash.into(),
vec![SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY.to_string()],
);
let snapshot = match self.run_query(correlation_id, query_request)? {
QueryResult::RootNotFound => return Err(GetEraValidatorsError::RootNotFound),
QueryResult::ValueNotFound(error) => {
error!(%error, "unexpected query failure; value not found");
return Err(GetEraValidatorsError::EraValidatorsMissing);
}
QueryResult::CircularReference(error) => {
error!(%error, "unexpected query failure; circular reference");
return Err(GetEraValidatorsError::UnexpectedQueryFailure);
}
QueryResult::DepthLimit { depth } => {
error!(%depth, "unexpected query failure; depth limit exceeded");
return Err(GetEraValidatorsError::UnexpectedQueryFailure);
}
QueryResult::Success { value, proofs: _ } => {
let cl_value = match value.as_cl_value() {
Some(snapshot_cl_value) => snapshot_cl_value.clone(),
None => {
error!("unexpected query failure; seigniorage recipients snapshot is not a CLValue");
return Err(GetEraValidatorsError::UnexpectedQueryFailure);
}
};
cl_value.into_t().map_err(|cl_value_error| {
error!(%cl_value_error, "unexpected query failure; unable to parse seigniorage recipients");
GetEraValidatorsError::CLValue
})?
}
};
let era_validators_result = auction::detail::era_validators_from_snapshot(snapshot);
Ok(era_validators_result)
}
pub fn get_bids(
&self,
correlation_id: CorrelationId,
get_bids_request: GetBidsRequest,
) -> Result<GetBidsResult, Error> {
let tracking_copy = match self.tracking_copy(get_bids_request.state_hash())? {
Some(tracking_copy) => Rc::new(RefCell::new(tracking_copy)),
None => return Ok(GetBidsResult::RootNotFound),
};
let mut tracking_copy = tracking_copy.borrow_mut();
let bid_keys = tracking_copy
.get_keys(correlation_id, &KeyTag::Bid)
.map_err(|err| Error::Exec(err.into()))?;
let mut bids = BTreeMap::new();
for key in bid_keys.iter() {
if let Some(StoredValue::Bid(bid)) =
tracking_copy.get(correlation_id, key).map_err(Into::into)?
{
bids.insert(bid.validator_public_key().clone(), *bid);
};
}
Ok(GetBidsResult::Success { bids })
}
pub fn commit_step(
&self,
correlation_id: CorrelationId,
step_request: StepRequest,
) -> Result<StepSuccess, StepError> {
let state_root_hash = step_request.pre_state_hash;
let tracking_copy = match self.tracking_copy(state_root_hash) {
Err(error) => return Err(StepError::TrackingCopyError(error)),
Ok(None) => return Err(StepError::RootNotFound(state_root_hash)),
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
};
let executor = Executor::new(self.config().clone());
let virtual_system_account = {
let purse = URef::new(Default::default(), AccessRights::READ_ADD_WRITE);
Account::create(
PublicKey::System.to_account_hash(),
NamedKeys::default(),
purse,
)
};
let authorization_keys = {
let mut ret = BTreeSet::new();
ret.insert(PublicKey::System.to_account_hash());
ret
};
let gas_limit = Gas::new(U512::from(std::u64::MAX));
let deploy_hash = {
let mut bytes = step_request.era_end_timestamp_millis.into_bytes()?;
bytes.append(&mut step_request.next_era_id.into_bytes()?);
DeployHash::new(Digest::hash(&bytes).value())
};
let distribute_accumulated_fees_stack = self.get_new_system_call_stack();
let (_, execution_result): (Option<()>, ExecutionResult) = executor.call_system_contract(
DirectSystemContractCall::DistributeAccumulatedFees,
RuntimeArgs::default(),
&virtual_system_account,
authorization_keys.clone(),
BlockTime::default(),
deploy_hash,
gas_limit,
step_request.protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Session,
distribute_accumulated_fees_stack,
U512::zero(),
);
if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::DistributeAccumulatedFeesError(exec_error));
}
let reward_factors = match step_request.reward_factors() {
Ok(reward_factors) => reward_factors,
Err(error) => {
error!(
"failed to deserialize reward factors: {}",
error.to_string()
);
return Err(StepError::BytesRepr(error));
}
};
let reward_args = RuntimeArgs::try_new(|args| {
args.insert(ARG_REWARD_FACTORS, reward_factors)?;
Ok(())
})?;
let distribute_rewards_stack = self.get_new_system_call_stack();
let (_, execution_result): (Option<()>, ExecutionResult) = executor.call_system_contract(
DirectSystemContractCall::DistributeRewards,
reward_args,
&virtual_system_account,
authorization_keys.clone(),
BlockTime::default(),
deploy_hash,
gas_limit,
step_request.protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Session,
distribute_rewards_stack,
U512::zero(),
);
if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::DistributeError(exec_error));
}
let slashed_validators: Vec<PublicKey> = step_request.slashed_validators();
if !slashed_validators.is_empty() {
let slash_args = {
let mut runtime_args = RuntimeArgs::new();
runtime_args
.insert(ARG_VALIDATOR_PUBLIC_KEYS, slashed_validators)
.map_err(|e| Error::Exec(e.into()))?;
runtime_args
};
let slash_stack = self.get_new_system_call_stack();
let (_, execution_result): (Option<()>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::Slash,
slash_args,
&virtual_system_account,
authorization_keys.clone(),
BlockTime::default(),
deploy_hash,
gas_limit,
step_request.protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Session,
slash_stack,
U512::zero(),
);
if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::SlashingError(exec_error));
}
}
let run_auction_args = RuntimeArgs::try_new(|args| {
args.insert(
ARG_ERA_END_TIMESTAMP_MILLIS,
step_request.era_end_timestamp_millis,
)?;
args.insert(
ARG_EVICTED_VALIDATORS,
step_request
.evict_items
.iter()
.map(|item| item.validator_id.clone())
.collect::<Vec<PublicKey>>(),
)?;
Ok(())
})?;
let run_auction_stack = self.get_new_system_call_stack();
let (_, execution_result): (Option<()>, ExecutionResult) = executor.call_system_contract(
DirectSystemContractCall::RunAuction,
run_auction_args,
&virtual_system_account,
authorization_keys,
BlockTime::default(),
deploy_hash,
gas_limit,
step_request.protocol_version,
correlation_id,
Rc::clone(&tracking_copy),
Phase::Session,
run_auction_stack,
U512::zero(),
);
if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::AuctionError(exec_error));
}
let execution_effect = tracking_copy.borrow().effect();
let execution_journal = tracking_copy.borrow().execution_journal();
let post_state_hash = self
.state
.commit(
correlation_id,
step_request.pre_state_hash,
execution_effect.transforms,
)
.map_err(Into::into)?;
Ok(StepSuccess {
post_state_hash,
execution_journal,
})
}
pub fn get_balance(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
public_key: PublicKey,
) -> Result<BalanceResult, Error> {
let tracking_copy = match self.tracking_copy(state_hash) {
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
Ok(None) => return Ok(BalanceResult::RootNotFound),
Err(error) => return Err(error),
};
let account_addr = public_key.to_account_hash();
let account = match tracking_copy
.borrow_mut()
.get_account(correlation_id, account_addr)
{
Ok(account) => account,
Err(error) => return Err(error.into()),
};
let main_purse_balance_key = {
let main_purse = account.main_purse();
match tracking_copy
.borrow()
.get_purse_balance_key(correlation_id, main_purse.into())
{
Ok(balance_key) => balance_key,
Err(error) => return Err(error.into()),
}
};
let (account_balance, proof) = match tracking_copy
.borrow()
.get_purse_balance_with_proof(correlation_id, main_purse_balance_key)
{
Ok((balance, proof)) => (balance, proof),
Err(error) => return Err(error.into()),
};
let proof = Box::new(proof);
let motes = account_balance.value();
Ok(BalanceResult::Success { motes, proof })
}
pub fn get_system_contract_registry(
&self,
correlation_id: CorrelationId,
state_root_hash: Digest,
) -> Result<SystemContractRegistry, Error> {
let tracking_copy = match self.tracking_copy(state_root_hash)? {
None => return Err(Error::RootNotFound(state_root_hash)),
Some(tracking_copy) => Rc::new(RefCell::new(tracking_copy)),
};
let result = tracking_copy
.borrow_mut()
.get_system_contracts(correlation_id)
.map_err(|error| {
warn!(%error, "Failed to retrieve system contract registry");
Error::MissingSystemContractRegistry
});
result
}
pub fn get_system_mint_hash(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
) -> Result<ContractHash, Error> {
let registry = self.get_system_contract_registry(correlation_id, state_hash)?;
let mint_hash = registry.get(MINT).ok_or_else(|| {
error!("Missing system mint contract hash");
Error::MissingSystemContractHash(MINT.to_string())
})?;
Ok(*mint_hash)
}
pub fn get_system_auction_hash(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
) -> Result<ContractHash, Error> {
let registry = self.get_system_contract_registry(correlation_id, state_hash)?;
let auction_hash = registry.get(AUCTION).ok_or_else(|| {
error!("Missing system auction contract hash");
Error::MissingSystemContractHash(AUCTION.to_string())
})?;
Ok(*auction_hash)
}
pub fn get_handle_payment_hash(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
) -> Result<ContractHash, Error> {
let registry = self.get_system_contract_registry(correlation_id, state_hash)?;
let handle_payment = registry.get(HANDLE_PAYMENT).ok_or_else(|| {
error!("Missing system handle payment contract hash");
Error::MissingSystemContractHash(HANDLE_PAYMENT.to_string())
})?;
Ok(*handle_payment)
}
pub fn get_standard_payment_hash(
&self,
correlation_id: CorrelationId,
state_hash: Digest,
) -> Result<ContractHash, Error> {
let registry = self.get_system_contract_registry(correlation_id, state_hash)?;
let standard_payment = registry.get(STANDARD_PAYMENT).ok_or_else(|| {
error!("Missing system standard payment contract hash");
Error::MissingSystemContractHash(STANDARD_PAYMENT.to_string())
})?;
Ok(*standard_payment)
}
fn get_new_system_call_stack(&self) -> RuntimeStack {
let max_height = self.config.max_runtime_call_stack_height() as usize;
RuntimeStack::new_system_call_stack(max_height)
}
pub fn get_checksum_registry(
&self,
correlation_id: CorrelationId,
state_root_hash: Digest,
) -> Result<Option<ChecksumRegistry>, Error> {
let tracking_copy = match self.tracking_copy(state_root_hash)? {
None => return Err(Error::RootNotFound(state_root_hash)),
Some(tracking_copy) => Rc::new(RefCell::new(tracking_copy)),
};
let maybe_checksum_registry = tracking_copy
.borrow_mut()
.get_checksum_registry(correlation_id)
.map_err(Error::Exec);
maybe_checksum_registry
}
pub fn get_checksum_registry_proof(
&self,
correlation_id: CorrelationId,
state_root_hash: Digest,
) -> Result<TrieMerkleProof<Key, StoredValue>, Error> {
let tracking_copy = match self.tracking_copy(state_root_hash)? {
None => return Err(Error::RootNotFound(state_root_hash)),
Some(tracking_copy) => Rc::new(RefCell::new(tracking_copy)),
};
let key = Key::ChecksumRegistry;
let maybe_proof = tracking_copy
.borrow_mut()
.reader()
.read_with_proof(correlation_id, &key)
.map_err(Into::into)?;
maybe_proof.ok_or(Error::MissingChecksumRegistry)
}
}
fn log_execution_result(preamble: &'static str, result: &ExecutionResult) {
trace!("{}: {:?}", preamble, result);
match result {
ExecutionResult::Success {
transfers,
cost,
execution_journal,
} => {
debug!(
%cost,
transfer_count=%transfers.len(),
journal_entries=%execution_journal.len(),
"{}: execution success",
preamble
);
}
ExecutionResult::Failure {
error,
transfers,
cost,
execution_journal,
} => {
debug!(
%error,
%cost,
transfer_count=%transfers.len(),
journal_entries=%execution_journal.len(),
"{}: execution failure",
preamble
);
}
}
}
fn should_charge_for_errors_in_wasm(execution_result: &ExecutionResult) -> bool {
match execution_result {
ExecutionResult::Failure {
error,
transfers: _,
cost: _,
execution_journal: _,
} => match error {
Error::Exec(err) => match err {
ExecError::WasmPreprocessing(_) | ExecError::UnsupportedWasmStart => true,
ExecError::Storage(_)
| ExecError::InvalidContractWasm(_)
| ExecError::WasmOptimizer
| ExecError::ParityWasm(_)
| ExecError::Interpreter(_)
| ExecError::BytesRepr(_)
| ExecError::NamedKeyNotFound(_)
| ExecError::KeyNotFound(_)
| ExecError::AccountNotFound(_)
| ExecError::TypeMismatch(_)
| ExecError::InvalidAccess { .. }
| ExecError::ForgedReference(_)
| ExecError::URefNotFound(_)
| ExecError::FunctionNotFound(_)
| ExecError::GasLimit
| ExecError::Ret(_)
| ExecError::Resolver(_)
| ExecError::Revert(_)
| ExecError::AddKeyFailure(_)
| ExecError::RemoveKeyFailure(_)
| ExecError::UpdateKeyFailure(_)
| ExecError::SetThresholdFailure(_)
| ExecError::SystemContract(_)
| ExecError::DeploymentAuthorizationFailure
| ExecError::ExpectedReturnValue
| ExecError::UnexpectedReturnValue
| ExecError::InvalidContext
| ExecError::IncompatibleProtocolMajorVersion { .. }
| ExecError::CLValue(_)
| ExecError::HostBufferEmpty
| ExecError::NoActiveContractVersions(_)
| ExecError::InvalidContractVersion(_)
| ExecError::NoSuchMethod(_)
| ExecError::KeyIsNotAURef(_)
| ExecError::UnexpectedStoredValueVariant
| ExecError::LockedContract(_)
| ExecError::InvalidContractPackage(_)
| ExecError::InvalidContract(_)
| ExecError::MissingArgument { .. }
| ExecError::DictionaryItemKeyExceedsLength
| ExecError::MissingSystemContractRegistry
| ExecError::MissingSystemContractHash(_)
| ExecError::RuntimeStackOverflow
| ExecError::ValueTooLarge
| ExecError::MissingRuntimeStack
| ExecError::DisabledContract(_)
| ExecError::DisabledUnrestrictedTransfers => false,
},
Error::WasmPreprocessing(_) => true,
Error::WasmSerialization(_) => true,
Error::RootNotFound(_)
| Error::InvalidProtocolVersion(_)
| Error::Genesis(_)
| Error::Storage(_)
| Error::Authorization
| Error::InsufficientPayment
| Error::GasConversionOverflow
| Error::Deploy
| Error::Finalization
| Error::Bytesrepr(_)
| Error::Mint(_)
| Error::InvalidKeyVariant
| Error::ProtocolUpgrade(_)
| Error::InvalidDeployItemVariant(_)
| Error::CommitError(_)
| Error::MissingSystemContractRegistry
| Error::MissingSystemContractHash(_)
| Error::MissingChecksumRegistry
| Error::RuntimeStackOverflow
| Error::FailedToGetWithdrawKeys
| Error::FailedToGetStoredWithdraws
| Error::FailedToGetWithdrawPurses
| Error::FailedToRetrieveUnbondingDelay
| Error::FailedToRetrieveEraId
| Error::MissingTrieNodeChildren(_)
| Error::FailedToRetrieveAccumulationPurse => false,
},
ExecutionResult::Success { .. } => false,
}
}