use casper_engine_test_support::genesis_config_builder::GenesisConfigBuilder;
use odra_core::casper_types::system::auction::{
BidAddr, BidKind, DelegationRate, DelegatorKind, ARG_DELEGATION_RATE, ARG_ENTRY_POINT,
ARG_EVICTED_VALIDATORS, ARG_MINIMUM_DELEGATION_AMOUNT, ARG_PUBLIC_KEY, ARG_REWARDS_MAP,
BLOCK_REWARD, METHOD_ADD_BID, METHOD_DISTRIBUTE, METHOD_RUN_AUCTION, METHOD_WITHDRAW_BID
};
use odra_core::casper_types::{
AddressableEntity, AddressableEntityHash, EntityAddr, GenesisConfig, GenesisValidator,
HashAddr, NamedKeys, Package, PackageHash, ProtocolVersion
};
use odra_core::consts::*;
use odra_core::prelude::*;
use std::cell::RefCell;
use std::env;
use std::hash::Hash;
use std::path::PathBuf;
use casper_engine_test_support::{
ChainspecConfig, DeployItemBuilder, EntityWithNamedKeys, ExecuteRequestBuilder,
LmdbWasmTestBuilder, WasmTestBuilder, ARG_AMOUNT, DEFAULT_ACCOUNTS, DEFAULT_AUCTION_DELAY,
DEFAULT_CHAINSPEC_REGISTRY, DEFAULT_EXEC_CONFIG, DEFAULT_GENESIS_CONFIG_HASH,
DEFAULT_GENESIS_TIMESTAMP_MILLIS, DEFAULT_LOCKED_FUNDS_PERIOD_MILLIS, DEFAULT_PAYMENT,
DEFAULT_PROTOCOL_VERSION, DEFAULT_ROUND_SEIGNIORAGE_RATE, DEFAULT_SYSTEM_CONFIG,
DEFAULT_UNBONDING_DELAY, DEFAULT_VALIDATOR_SLOTS, DEFAULT_WASM_CONFIG, SYSTEM_ADDR
};
use casper_event_standard::try_full_name_from_bytes;
use casper_execution_engine::{engine_state, execution};
use casper_storage::data_access_layer::{DataAccessLayer, GenesisRequest, RewardItem, StepRequest};
use odra_core::{casper_event_standard, DeployReport, GasReport};
use std::rc::Rc;
use odra_core::casper_event_standard::Schemas;
use odra_core::casper_types::account::{Account, AccountHash};
use odra_core::casper_types::bytesrepr::{Bytes, ToBytes};
use odra_core::casper_types::contract_messages::MessagePayload;
use odra_core::casper_types::contracts::{ContractHash, ContractPackageHash};
use odra_core::casper_types::{
bytesrepr::FromBytes, CLTyped, GenesisAccount, PublicKey, RuntimeArgs, U512
};
use odra_core::casper_types::{
runtime_args, ApiError, BlockTime, Contract, Key, Motes, SecretKey, StoredValue, URef
};
use odra_core::consts;
use odra_core::consts::*;
use odra_core::crypto::generate_key_pairs;
use odra_core::entry_point_callback::EntryPointsCaller;
use odra_core::prelude::*;
use odra_core::EventError;
use odra_core::VmError;
use odra_core::CASPER_ERROR_GENERIC_NAME;
use odra_core::{
host::{HostContext, HostEnv},
CallDef, ContractEnv
};
pub struct CasperVm {
accounts: Vec<Address>,
validators: Vec<GenesisAccount>,
removed_validators: Vec<PublicKey>,
key_pairs: BTreeMap<Address, (SecretKey, PublicKey)>,
messages: BTreeMap<EntityAddr, Vec<MessagePayload>>,
active_account: Address,
context: LmdbWasmTestBuilder,
block_time: u64,
calls_counter: u32,
error: Option<OdraError>,
attached_value: U512,
gas_used: BTreeMap<AccountHash, U512>,
gas_report: GasReport
}
impl CasperVm {
pub fn new() -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new_instance()))
}
pub fn package_hash_from_name(&self, name: &str) -> PackageHash {
let named_keys = self
.context
.get_named_keys_by_account_hash(self.active_account_hash());
let key: &Key = named_keys.get(name).unwrap();
PackageHash::from(key.into_package_hash().unwrap().value())
}
pub fn set_caller(&mut self, caller: Address) {
self.active_account = caller;
}
pub fn get_caller(&self) -> Address {
self.active_account
}
pub fn get_account(&self, index: usize) -> Address {
self.accounts[index]
}
pub fn get_validator(&self, index: usize) -> PublicKey {
self.validators
.get(index)
.unwrap_or_else(|| panic!("Not enough validators: {}", index))
.public_key()
}
pub fn advance_block_time(&mut self, time_diff_millis: u64) {
self.block_time += time_diff_millis
}
pub fn advance_with_auctions(&mut self, time_diff_millis: u64) {
let time_between_auctions = self.auction_delay();
let num_auctions = time_diff_millis / time_between_auctions;
for _ in 0..num_auctions {
let mut step_request_builder = self.context.step_request_builder();
let mut rewards = BTreeMap::new();
for validator in &self.validators {
if self.removed_validators.contains(&validator.public_key()) {
continue;
}
rewards.insert(validator.public_key(), vec![U512::from(BLOCK_REWARD)]);
let reward_item = RewardItem::new(validator.public_key(), BLOCK_REWARD);
step_request_builder = step_request_builder.with_reward_item(reward_item);
}
let step_request = step_request_builder.build();
self.context.step(step_request);
self.context.advance_era();
self.advance_block_time(time_between_auctions);
self.context
.distribute(None, DEFAULT_PROTOCOL_VERSION, rewards, self.block_time);
}
let remaining_time = time_diff_millis % time_between_auctions;
self.advance_block_time(remaining_time);
}
pub fn auction_delay(&mut self) -> u64 {
self.context
.get_auction_delay()
.saturating_mul(self.context.chainspec().core_config.era_duration.millis())
}
pub fn unbonding_delay(&mut self) -> u64 {
self.context
.get_unbonding_delay()
.saturating_mul(self.context.chainspec().core_config.era_duration.millis())
}
pub fn delegated_amount(&mut self, delegator: Address, validator: PublicKey) -> U512 {
let purse_uref = self.get_main_purse(delegator);
let account_hash = validator.to_account_hash();
let bid = self
.context
.get_bids()
.into_iter()
.find(|bid| bid.validator_public_key() == validator && bid.is_delegator());
match bid {
None => U512::zero(),
Some(bid_kind) => bid_kind.staked_amount().unwrap_or_default()
}
}
fn total_delegated_amount(&mut self, validator: PublicKey) -> U512 {
let bids = self
.context
.get_bids()
.into_iter()
.filter(|bid| bid.validator_public_key() == validator)
.collect::<Vec<_>>();
bids.iter().fold(U512::zero(), |acc, bid| {
acc + bid.staked_amount().unwrap_or_default()
})
}
pub fn remove_validator(&mut self, validator: PublicKey) {
let amount = self.total_delegated_amount(validator.clone());
let withdraw_request = ExecuteRequestBuilder::contract_call_by_hash(
validator.to_account_hash(),
self.context.get_auction_contract_hash(),
METHOD_WITHDRAW_BID,
runtime_args! {
ARG_PUBLIC_KEY => validator.clone(),
ARG_AMOUNT => amount,
}
)
.build();
self.context
.exec(withdraw_request)
.commit()
.expect_success();
self.removed_validators.push(validator.clone());
}
fn get_main_purse(&self, address: Address) -> URef {
match address {
Address::Account(account) => {
let account = self.context.get_account(account).unwrap_or_else(|| {
panic!("Account not found while getting entity addr: {:?}", account)
});
account.main_purse()
}
Address::Contract(contract) => self
.get_contract_main_purse(PackageHash::new(contract.value()))
.unwrap_or_else(|| {
panic!(
"Contract purse not found while getting entity addr: {:?}",
contract
)
})
}
}
pub fn block_time(&self) -> u64 {
self.block_time
}
pub fn get_event(&self, contract_address: &Address, index: u32) -> Result<Bytes, EventError> {
let package_hash = contract_address.as_package_hash().unwrap();
let dictionary_seed_uref = self.package_named_key(package_hash, EVENTS);
match dictionary_seed_uref {
None => Err(EventError::CouldntExtractEventData),
Some(uref) => Ok(self.get_dict_value(*uref.as_uref().unwrap(), &index.to_string()))
}
}
pub fn get_native_event(
&self,
contract_address: &Address,
index: u32
) -> Result<Bytes, EventError> {
let messages = self
.messages
.get(&self.get_contract_entity_addr(contract_address))
.ok_or(EventError::IndexOutOfBounds)?;
let message = messages
.get(index as usize)
.ok_or(EventError::IndexOutOfBounds)?;
match message {
MessagePayload::String(_) => Err(EventError::CouldntExtractEventData),
MessagePayload::Bytes(b) => Ok(b.clone())
}
}
pub fn get_events_count(&self, contract_address: &Address) -> Result<u32, EventError> {
let package_hash = contract_address.as_package_hash();
if package_hash.is_none() {
return Err(EventError::TriedToQueryEventForNonContract);
}
Ok(self.events_length(package_hash.unwrap()))
}
pub fn get_native_events_count(&self, contract_address: &Address) -> Result<u32, EventError> {
let messages = self
.messages
.get(&self.get_contract_entity_addr(contract_address))
.ok_or(EventError::IndexOutOfBounds)?;
Ok(messages.len() as u32)
}
pub fn attach_value(&mut self, amount: U512) {
self.attached_value = amount;
}
pub fn call_contract(
&mut self,
address: &Address,
call_def: CallDef,
use_proxy: bool
) -> Bytes {
self.error = None;
let hash = address
.as_contract_package_hash()
.expect("Contract hash expected");
let deploy_item = if use_proxy {
let session_code =
include_bytes!("../../resources/proxy_caller_with_return.wasm").to_vec();
let args_bytes: Vec<u8> = call_def
.args()
.to_bytes()
.expect("Should serialize to bytes");
let entry_point = call_def.entry_point();
let args = runtime_args! {
PACKAGE_HASH_ARG => hash,
ENTRY_POINT_ARG => entry_point,
ARGS_ARG => Bytes::from(args_bytes),
ATTACHED_VALUE_ARG => call_def.amount(),
AMOUNT_ARG => call_def.amount(),
};
DeployItemBuilder::new()
.with_standard_payment(runtime_args! { ARG_AMOUNT => *DEFAULT_PAYMENT})
.with_authorization_keys(&[self.active_account_hash()])
.with_address(self.active_account_hash())
.with_session_bytes(session_code, args)
.with_deploy_hash(self.next_hash())
.build()
} else {
DeployItemBuilder::new()
.with_standard_payment(runtime_args! { ARG_AMOUNT => *DEFAULT_PAYMENT})
.with_authorization_keys(&[self.active_account_hash()])
.with_address(self.active_account_hash())
.with_stored_versioned_contract_by_hash(
hash.value(),
None,
call_def.entry_point(),
call_def.args().clone()
)
.with_deploy_hash(self.next_hash())
.build()
};
let execute_request = ExecuteRequestBuilder::from_deploy_item(&deploy_item)
.with_block_time(self.block_time)
.build();
self.context.exec(execute_request).commit();
self.collect_gas();
self.gas_report.push(DeployReport::ContractCall {
gas: self.last_call_contract_gas_cost(),
contract_address: *address,
call_def: call_def.clone()
});
self.collect_messages();
self.attached_value = U512::zero();
if let Some(error) = self.context.get_error() {
let odra_error = parse_error(error);
self.error = Some(odra_error.clone());
self.panic_with_error(odra_error, call_def.entry_point(), hash);
} else {
self.get_active_account_result()
}
}
fn collect_messages(&mut self) {
let messages = self.context.get_last_exec_result().unwrap();
let messages = messages.messages();
messages.iter().for_each(|message| {
let payload = message.payload().clone();
self.messages
.entry(*message.entity_addr())
.or_default()
.push(payload);
});
}
fn get_contract_entity_addr(&self, address: &Address) -> EntityAddr {
match address {
Address::Account(_) => {
panic!(
"Account address passed instead of contract address: {:?}",
address
)
}
Address::Contract(contract) => {
let package = self
.context
.get_package(PackageHash::new(contract.value()))
.unwrap_or_else(|| {
panic!(
"Contract package not found while getting entity addr: {:?}",
contract
)
});
package.current_entity_hash().unwrap_or_else(|| {
panic!(
"Current entity hash not found while getting entity addr: {:?}",
contract
)
})
}
}
}
fn get_addressable_entity_from_entity_addr(
&self,
entity_addr: &EntityAddr
) -> AddressableEntity {
let query_result = self
.context
.query(None, Key::AddressableEntity(*entity_addr), &[])
.unwrap();
if let StoredValue::AddressableEntity(entity) = query_result {
entity
} else {
panic!(
"Stored value is not an addressable entity: {:?}",
query_result
);
}
}
pub fn new_contract(
&mut self,
name: &str,
init_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> Address {
let wasm_path = format!("{}.wasm", name);
let package_hash_key_name: String = init_args
.get(PACKAGE_HASH_KEY_NAME_ARG)
.unwrap()
.clone()
.into_t()
.unwrap();
let result = self.deploy_wasm(&wasm_path, &init_args);
if let Some(error) = result {
let odra_error = parse_error(error);
self.error = Some(odra_error.clone());
panic!("Revert: Contract deploy failed {:?}", odra_error);
} else {
let package_hash = self.package_hash_from_name(&package_hash_key_name);
self.collect_messages();
package_hash.into()
}
}
pub fn upgrade_contract(
&mut self,
name: &str,
contract_to_upgrade: Address,
upgrade_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> Address {
let wasm_path = format!("{}.wasm", name);
let result = self.deploy_wasm(&wasm_path, &upgrade_args);
if let Some(error) = result {
let odra_error = parse_error(error);
self.error = Some(odra_error.clone());
panic!("Revert: Contract deploy failed {:?}", odra_error);
} else {
self.collect_messages();
contract_to_upgrade
}
}
pub fn active_account_hash(&self) -> AccountHash {
*self.active_account.as_account_hash().unwrap()
}
pub fn balance_of(&self, address: &Address) -> U512 {
match address {
Address::Account(account_hash) => self.get_account_cspr_balance(account_hash),
Address::Contract(package_hash) => {
self.get_contract_cspr_balance(&address.as_package_hash().unwrap())
}
}
}
pub fn transfer(&mut self, to: Address, amount: U512) -> OdraResult<()> {
let deploy_item = DeployItemBuilder::new()
.with_transfer_args(runtime_args! {
"amount" => amount,
"target" => to,
"id" => Some(0u64),
})
.with_authorization_keys(&[self.active_account_hash()])
.with_address(self.active_account_hash())
.with_deploy_hash(self.next_hash())
.build();
let execute_request = ExecuteRequestBuilder::from_deploy_item(&deploy_item)
.with_block_time(self.block_time)
.build();
self.context.exec(execute_request).commit();
if let Some(error) = self.context.get_error() {
let odra_error = parse_error(error);
Err(odra_error)
} else {
Ok(())
}
}
pub fn get_account_value<T: CLTyped + FromBytes + ToBytes>(
&self,
hash: AccountHash,
name: &str
) -> Result<T, String> {
let result: Result<StoredValue, String> =
self.context
.query(None, Key::Account(hash), &[name.to_string()]);
result.map(|value| value.as_cl_value().unwrap().clone().into_t().unwrap())
}
pub fn last_call_contract_gas_cost(&self) -> U512 {
self.context.last_exec_gas_consumed().value()
}
pub fn last_call_contract_gas_used(&self) -> U512 {
*DEFAULT_PAYMENT
}
pub fn total_gas_used(&self, address: Address) -> U512 {
match &address {
Address::Account(address) => self.gas_used.get(address).cloned().unwrap_or_default(),
Address::Contract(address) => panic!("Contract {} can't burn gas.", address)
}
}
pub fn gas_report(&self) -> &GasReport {
&self.gas_report
}
pub fn public_key(&self, address: &Address) -> PublicKey {
let (_, public_key) = self.key_pairs.get(address).unwrap();
public_key.clone()
}
pub fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes {
let (secret_key, public_key) = self.key_pairs.get(address).unwrap();
let signature = odra_core::casper_types::crypto::sign(message, secret_key, public_key)
.to_bytes()
.unwrap();
Bytes::from(signature)
}
pub fn last_call_gas_cost(&self) -> u64 {
self.last_call_contract_gas_cost().as_u64()
}
pub fn error(&self) -> Option<OdraError> {
self.error.clone()
}
fn get_active_account_result(&self) -> Bytes {
let active_account = self.active_account_hash();
let bytes: Bytes = self
.get_account_value(active_account, RESULT_KEY)
.unwrap_or_default();
bytes
}
fn collect_gas(&mut self) {
*self
.gas_used
.entry(*self.active_account.as_account_hash().unwrap())
.or_insert_with(U512::zero) += *DEFAULT_PAYMENT;
}
fn next_hash(&mut self) -> [u8; 32] {
let seed = self.calls_counter;
self.calls_counter += 1;
let mut hash = [0u8; 32];
hash[0] = seed as u8;
hash[1] = (seed >> 8) as u8;
hash
}
fn get_account_cspr_balance(&self, account_hash: &AccountHash) -> U512 {
let account: AddressableEntity = self
.context
.get_entity_by_account_hash(*account_hash)
.unwrap();
let purse = account.main_purse();
self.context.get_purse_balance(purse)
}
fn get_contract_cspr_balance(&self, package_hash: &PackageHash) -> U512 {
let purse_uref = self.get_contract_main_purse(*package_hash);
match purse_uref {
None => U512::zero(),
Some(uref) => self.context.get_purse_balance(uref)
}
}
fn get_contract_main_purse(&self, package_hash: PackageHash) -> Option<URef> {
let purse_key = self.package_named_key(package_hash, CONTRACT_MAIN_PURSE);
match purse_key {
None => None,
Some(purse_key) => {
let purse_uref = purse_key.as_uref().unwrap_or_else(|| {
panic!(
"Contract doesn't have main purse uref under {} named key",
CONTRACT_MAIN_PURSE
)
});
Some(*purse_uref)
}
}
}
fn genesis_accounts(
key_pairs: &BTreeMap<Address, (SecretKey, PublicKey)>
) -> (Vec<GenesisAccount>, Vec<GenesisAccount>) {
let mut accounts = Vec::new();
let mut validators = Vec::new();
let total_accounts = key_pairs.len();
let validators_count = 5; let regular_accounts_count = total_accounts - validators_count;
let iter = key_pairs.iter();
for (_, (_, public_key)) in iter.take(regular_accounts_count) {
accounts.push(GenesisAccount::account(
public_key.clone(),
Motes::new(DEFAULT_BALANCE),
None
));
}
for (_, (_, public_key)) in key_pairs.iter().skip(regular_accounts_count) {
let validator_account = GenesisAccount::account(
public_key.clone(),
Motes::new(DEFAULT_BALANCE),
Some(GenesisValidator::new(Motes::new(DEFAULT_BID_AMOUNT), 0))
);
accounts.push(validator_account.clone());
validators.push(validator_account);
}
(accounts, validators)
}
fn genesis_config(genesis_accounts: Vec<GenesisAccount>) -> GenesisConfig {
GenesisConfigBuilder::default()
.with_accounts(genesis_accounts)
.with_wasm_config(*DEFAULT_WASM_CONFIG)
.with_system_config(*DEFAULT_SYSTEM_CONFIG)
.with_validator_slots(DEFAULT_VALIDATOR_SLOTS)
.with_auction_delay(DEFAULT_AUCTION_DELAY)
.with_locked_funds_period_millis(DEFAULT_LOCKED_FUNDS_PERIOD_MILLIS)
.with_round_seigniorage_rate(DEFAULT_ROUND_SEIGNIORAGE_RATE)
.with_unbonding_delay(DEFAULT_UNBONDING_DELAY)
.with_genesis_timestamp_millis(DEFAULT_GENESIS_TIMESTAMP_MILLIS)
.build()
}
fn new_instance() -> Self {
let key_pairs = generate_key_pairs(ACCOUNTS_NUMBER);
let (genesis_accounts, validators) = Self::genesis_accounts(&key_pairs);
let accounts: Vec<Address> = key_pairs.keys().copied().collect();
let mut builder = LmdbWasmTestBuilder::default();
let chainspec = ChainspecConfig::create_genesis_request_from_local_chainspec(
genesis_accounts.clone(),
ProtocolVersion::V2_0_0
);
builder.run_genesis(chainspec.unwrap()).commit();
let unbonding_delay = builder.get_unbonding_delay();
let auction_delay = builder.get_auction_delay();
builder.advance_eras_by(unbonding_delay + auction_delay);
for account in validators.iter() {
let bid_request = ExecuteRequestBuilder::contract_call_by_hash(
account.account_hash(),
builder.get_auction_contract_hash(),
METHOD_ADD_BID,
runtime_args! {
ARG_PUBLIC_KEY => account.public_key(),
ARG_AMOUNT => U512::from(DEFAULT_BID_AMOUNT),
ARG_DELEGATION_RATE=> 0u8,
ARG_MINIMUM_DELEGATION_AMOUNT => DEFAULT_MINIMUM_DELEGATION_AMOUNT,
}
)
.build();
builder.exec(bid_request).commit().expect_success();
}
Self {
active_account: accounts[0],
context: builder,
accounts,
block_time: 0u64,
calls_counter: 0,
error: None,
attached_value: U512::zero(),
gas_used: BTreeMap::new(),
gas_report: GasReport::default(),
key_pairs,
messages: Default::default(),
validators,
removed_validators: Default::default()
}
}
fn deploy_wasm(&mut self, wasm_path: &str, args: &RuntimeArgs) -> Option<engine_state::Error> {
self.error = None;
let session_code = PathBuf::from(wasm_path);
let deploy_item = DeployItemBuilder::new()
.with_standard_payment(runtime_args! {ARG_AMOUNT => *DEFAULT_PAYMENT})
.with_authorization_keys(&[self.active_account_hash()])
.with_address(self.active_account_hash())
.with_session_code(session_code, args.clone())
.with_deploy_hash(self.next_hash())
.build();
let execute_request = ExecuteRequestBuilder::from_deploy_item(&deploy_item)
.with_block_time(self.block_time)
.build();
let result = self.context.exec(execute_request).commit();
self.collect_gas();
self.gas_report.push(DeployReport::WasmDeploy {
gas: self.last_call_contract_gas_cost(),
file_name: wasm_path.to_string()
});
self.context.get_error()
}
}
impl CasperVm {
fn get_package(&self, package_hash: PackageHash) -> Package {
self.context.get_package(package_hash).unwrap()
}
fn package_named_keys(&self, package_hash: PackageHash) -> NamedKeys {
let a = self
.context
.get_package(package_hash)
.unwrap()
.current_entity_hash()
.unwrap();
let addressable_entity_hash = AddressableEntityHash::new(a.value());
let named_keys = self
.context
.get_entity_with_named_keys_by_entity_hash(addressable_entity_hash)
.unwrap();
let keys = named_keys.named_keys();
keys.clone()
}
fn package_named_key(&self, package_hash: PackageHash, name: &str) -> Option<Key> {
self.package_named_keys(package_hash).get(name).cloned()
}
fn events_length(&self, package_hash: PackageHash) -> u32 {
let key = self.package_named_key(package_hash, EVENTS_LENGTH);
match key {
None => 0,
Some(key) => self.get_value(key)
}
}
fn get_value<T: CLTyped + FromBytes>(&self, key: Key) -> T {
let value = self.context.query(None, key, &[]);
value
.unwrap()
.as_cl_value()
.unwrap()
.clone()
.into_t::<T>()
.unwrap()
}
fn get_dict_value<T: CLTyped + FromBytes>(&self, uref: URef, name: &str) -> T {
let value = self.context.query_dictionary_item(None, uref, name);
value
.unwrap()
.as_cl_value()
.unwrap()
.clone()
.into_t::<T>()
.unwrap()
}
fn panic_with_error(
&self,
error: OdraError,
entrypoint: &str,
package_hash: &ContractPackageHash
) -> ! {
panic!("Revert: {:?} - {:?}::{}", error, package_hash, entrypoint)
}
}
fn parse_error(err: engine_state::Error) -> OdraError {
if let engine_state::Error::Exec(exec_err) = err {
match exec_err {
execution::ExecError::Revert(ApiError::MissingArgument) => {
OdraError::ExecutionError(ExecutionError::MissingArg)
}
execution::ExecError::Revert(ApiError::Mint(0)) => {
OdraError::VmError(VmError::BalanceExceeded)
}
execution::ExecError::Revert(ApiError::User(code)) => match code {
x if x == ExecutionError::UnwrapError.code() => {
OdraError::ExecutionError(ExecutionError::UnwrapError)
}
x if x == ExecutionError::AdditionOverflow.code() => {
OdraError::ExecutionError(ExecutionError::AdditionOverflow)
}
x if x == ExecutionError::SubtractionOverflow.code() => {
OdraError::ExecutionError(ExecutionError::SubtractionOverflow)
}
x if x == ExecutionError::NonPayable.code() => {
OdraError::ExecutionError(ExecutionError::NonPayable)
}
x if x == ExecutionError::TransferToContract.code() => {
OdraError::ExecutionError(ExecutionError::TransferToContract)
}
x if x == ExecutionError::ReentrantCall.code() => {
OdraError::ExecutionError(ExecutionError::ReentrantCall)
}
x if x == ExecutionError::CannotOverrideKeys.code() => {
OdraError::ExecutionError(ExecutionError::CannotOverrideKeys)
}
x if x == ExecutionError::UnknownConstructor.code() => {
OdraError::ExecutionError(ExecutionError::UnknownConstructor)
}
x if x == ExecutionError::NativeTransferError.code() => {
OdraError::ExecutionError(ExecutionError::NativeTransferError)
}
x if x == ExecutionError::IndexOutOfBounds.code() => {
OdraError::ExecutionError(ExecutionError::IndexOutOfBounds)
}
x if x == ExecutionError::ZeroAddress.code() => {
OdraError::ExecutionError(ExecutionError::ZeroAddress)
}
x if x == ExecutionError::AddressCreationFailed.code() => {
OdraError::ExecutionError(ExecutionError::AddressCreationFailed)
}
x if x == ExecutionError::EarlyEndOfStream.code() => {
OdraError::ExecutionError(ExecutionError::EarlyEndOfStream)
}
x if x == ExecutionError::Formatting.code() => {
OdraError::ExecutionError(ExecutionError::Formatting)
}
x if x == ExecutionError::LeftOverBytes.code() => {
OdraError::ExecutionError(ExecutionError::LeftOverBytes)
}
x if x == ExecutionError::OutOfMemory.code() => {
OdraError::ExecutionError(ExecutionError::OutOfMemory)
}
x if x == ExecutionError::NotRepresentable.code() => {
OdraError::ExecutionError(ExecutionError::NotRepresentable)
}
x if x == ExecutionError::ExceededRecursionDepth.code() => {
OdraError::ExecutionError(ExecutionError::ExceededRecursionDepth)
}
x if x == ExecutionError::KeyNotFound.code() => {
OdraError::ExecutionError(ExecutionError::KeyNotFound)
}
x if x == ExecutionError::CouldNotDeserializeSignature.code() => {
OdraError::ExecutionError(ExecutionError::CouldNotDeserializeSignature)
}
x if x == ExecutionError::TypeMismatch.code() => {
OdraError::ExecutionError(ExecutionError::TypeMismatch)
}
x if x == ExecutionError::CouldNotSignMessage.code() => {
OdraError::ExecutionError(ExecutionError::CouldNotSignMessage)
}
x if x == ExecutionError::EmptyDictionaryName.code() => {
OdraError::ExecutionError(ExecutionError::EmptyDictionaryName)
}
x if x == ExecutionError::MissingArg.code() => {
OdraError::ExecutionError(ExecutionError::MissingArg)
}
_ => OdraError::user(code, CASPER_ERROR_GENERIC_NAME)
},
execution::ExecError::InvalidContext => OdraError::VmError(VmError::InvalidContext),
execution::ExecError::NoSuchMethod(name) => {
OdraError::VmError(VmError::NoSuchMethod(name))
}
execution::ExecError::MissingArgument { name } => {
OdraError::ExecutionError(ExecutionError::MissingArg)
}
_ => OdraError::VmError(VmError::Other(format!("Casper ExecError: {exec_err}")))
}
} else {
OdraError::VmError(VmError::Other(format!("Casper EngineStateError: {}", err)))
}
}