From d7ba416e2ba2d2275423d84cc012b1c9c9953930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Mon, 3 Oct 2022 15:05:25 +0200 Subject: [PATCH] Test(engine): Increase unit test coverage --- Cargo.lock | 3 + engine-precompiles/Cargo.toml | 1 + engine-precompiles/src/lib.rs | 124 +++- engine-standalone-storage/src/sync/mod.rs | 1 - engine-test-doubles/src/io.rs | 6 +- engine-test-doubles/src/promise.rs | 1 + engine/Cargo.toml | 2 + engine/src/engine.rs | 682 ++++++++++++++++++++-- engine/src/json.rs | 246 ++++---- engine/src/lib.rs | 2 - engine/src/pausables.rs | 91 +-- 11 files changed, 928 insertions(+), 231 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e318df50..b217a5e1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,7 @@ dependencies = [ "bitflags", "borsh", "byte-slice-cast", + "digest 0.10.3", "ethabi", "evm", "hex", @@ -119,6 +120,7 @@ dependencies = [ "rlp", "serde", "serde_json", + "sha3 0.10.2", "test-case", "wee_alloc", ] @@ -128,6 +130,7 @@ name = "aurora-engine-precompiles" version = "1.0.0" dependencies = [ "aurora-engine-sdk", + "aurora-engine-test-doubles", "aurora-engine-types", "borsh", "ethabi", diff --git a/engine-precompiles/Cargo.toml b/engine-precompiles/Cargo.toml index ccdc9232c..8fc4067fb 100644 --- a/engine-precompiles/Cargo.toml +++ b/engine-precompiles/Cargo.toml @@ -27,6 +27,7 @@ ethabi = { version = "17.1", default-features = false } hex = { version = "0.4", default-features = false, features = ["alloc"] } [dev-dependencies] +aurora-engine-test-doubles = { path = "../engine-test-doubles" } serde = { version = "1", features = ["derive"] } serde_json = "1" rand = "0.7.3" diff --git a/engine-precompiles/src/lib.rs b/engine-precompiles/src/lib.rs index a6571f74c..2b3844195 100644 --- a/engine-precompiles/src/lib.rs +++ b/engine-precompiles/src/lib.rs @@ -455,10 +455,101 @@ const fn make_h256(x: u128, y: u128) -> prelude::H256 { #[cfg(test)] mod tests { use crate::prelude::H160; - use crate::{prelude, Byzantium, Istanbul}; + use crate::{ + prelude, AllPrecompiles, Byzantium, Context, EvmPrecompileResult, ExitError, Istanbul, + Precompile, PrecompileOutput, Precompiles, + }; + use aurora_engine_sdk::env::Fixed; + use aurora_engine_sdk::promise::Noop; + use aurora_engine_test_doubles::io::StoragePointer; + use aurora_engine_types::types::EthGas; + use evm::executor::stack::{PrecompileFailure, PrecompileHandle, PrecompileSet}; + use evm::{ExitFatal, ExitReason, Transfer}; use prelude::types::Address; use rand::Rng; + struct MockPrecompile; + + impl Precompile for MockPrecompile { + fn required_gas(input: &[u8]) -> Result + where + Self: Sized, + { + Ok(EthGas::new(0)) + } + + fn run( + &self, + input: &[u8], + target_gas: Option, + context: &Context, + is_static: bool, + ) -> EvmPrecompileResult { + Ok(PrecompileOutput::default()) + } + } + + struct MockPrecompileHandle { + code_address: H160, + } + + impl MockPrecompileHandle { + pub fn new(code_address: H160) -> Self { + Self { code_address } + } + } + + impl PrecompileHandle for MockPrecompileHandle { + fn call( + &mut self, + to: H160, + transfer: Option, + input: Vec, + gas_limit: Option, + is_static: bool, + context: &Context, + ) -> (ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + unimplemented!() + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn log( + &mut self, + address: H160, + topics: Vec, + data: Vec, + ) -> Result<(), ExitError> { + unimplemented!() + } + + fn code_address(&self) -> H160 { + self.code_address + } + + fn input(&self) -> &[u8] { + unimplemented!() + } + + fn context(&self) -> &Context { + unimplemented!() + } + + fn is_static(&self) -> bool { + unimplemented!() + } + + fn gas_limit(&self) -> Option { + unimplemented!() + } + } + #[test] fn test_precompile_addresses() { assert_eq!(super::secp256k1::ECRecover::ADDRESS, u8_to_address(1)); @@ -486,6 +577,37 @@ mod tests { } } + #[test] + fn test_paused_precompiles_throws_error() { + let precompile_address = Address::default(); + let precompile: AllPrecompiles = + AllPrecompiles::Generic(Box::new(MockPrecompile)); + + let precompiles: Precompiles = Precompiles { + all_precompiles: { + let mut map = prelude::BTreeMap::new(); + map.insert(precompile_address, precompile); + map + }, + paused_precompiles: { + let mut set = prelude::BTreeSet::new(); + set.insert(precompile_address); + set + }, + }; + let mut precompile_handle = MockPrecompileHandle::new(precompile_address.raw()); + + let result = precompiles + .execute(&mut precompile_handle) + .expect("result must contain error but is empty"); + let actual_failure = result.expect_err("result must contain failure but is successful"); + let expected_failure = PrecompileFailure::Fatal { + exit_status: ExitFatal::Other(prelude::Cow::Borrowed("ERR_PAUSED")), + }; + + assert_eq!(expected_failure, actual_failure); + } + fn u8_to_address(x: u8) -> Address { let mut bytes = [0u8; 20]; bytes[19] = x; diff --git a/engine-standalone-storage/src/sync/mod.rs b/engine-standalone-storage/src/sync/mod.rs index 0b349944e..9142e6b86 100644 --- a/engine-standalone-storage/src/sync/mod.rs +++ b/engine-standalone-storage/src/sync/mod.rs @@ -225,7 +225,6 @@ fn non_submit_execute<'db>( } else { engine.receive_erc20_tokens( &env.predecessor_account_id, - &env.signer_account_id, args, &env.current_account_id, &mut handler, diff --git a/engine-test-doubles/src/io.rs b/engine-test-doubles/src/io.rs index 93f930173..adb066c41 100644 --- a/engine-test-doubles/src/io.rs +++ b/engine-test-doubles/src/io.rs @@ -20,9 +20,9 @@ impl StorageIntermediate for Value { #[derive(Debug, Default)] pub struct Storage { - input: Vec, - output: Vec, - kv_store: HashMap, Vec>, + pub input: Vec, + pub output: Vec, + pub kv_store: HashMap, Vec>, } /// In-memory implementation of [IO]. diff --git a/engine-test-doubles/src/promise.rs b/engine-test-doubles/src/promise.rs index e07e5a2be..14db2fd66 100644 --- a/engine-test-doubles/src/promise.rs +++ b/engine-test-doubles/src/promise.rs @@ -4,6 +4,7 @@ use aurora_engine_types::parameters::{PromiseBatchAction, PromiseCreateArgs}; use aurora_engine_types::types::PromiseResult; use std::collections::HashMap; +#[derive(Debug, PartialEq)] pub enum PromiseArgs { Create(PromiseCreateArgs), #[allow(dead_code)] diff --git a/engine/Cargo.toml b/engine/Cargo.toml index e50b40103..579f78504 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -39,6 +39,8 @@ aurora-engine-test-doubles = { path = "../engine-test-doubles" } serde_json = "1" rand = "0.7.3" test-case = "2.1" +sha3 = "0.10" +digest = "0.10" [features] default = ["std"] diff --git a/engine/src/engine.rs b/engine/src/engine.rs index d5e1d62cd..3677dda80 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -352,7 +352,19 @@ impl AsRef<[u8]> for EngineStateError { } } -struct StackExecutorParams<'a, I, E, H> { +// trait StackExecutorFactory<'env, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> { +// fn make_executor<'a>( +// &'a self, +// engine: &'a Engine<'env, I, E>, +// ) -> executor::stack::StackExecutor< +// 'static, +// 'a, +// executor::stack::MemoryStackState>, +// Precompiles<'env, I, E, H>, +// >; +// } + +pub struct StackExecutorParams<'a, I, E, H> { precompiles: Precompiles<'a, I, E, H>, gas_limit: u64, } @@ -380,7 +392,7 @@ impl<'env, I: IO + Copy, E: Env, H: ReadOnlyPromiseHandler> StackExecutorParams< } } -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct GasPaymentResult { pub prepaid_amount: Wei, pub effective_gas_price: U256, @@ -389,7 +401,7 @@ pub struct GasPaymentResult { /// Engine internal state, mostly configuration. /// Should not contain anything large or enumerable. -#[derive(BorshSerialize, BorshDeserialize, Default, Clone)] +#[derive(BorshSerialize, BorshDeserialize, Default, Clone, PartialEq, Debug)] pub struct EngineState { /// Chain id, according to the EIP-155 / ethereum-lists spec. pub chain_id: [u8; 32], @@ -636,7 +648,19 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { let origin = &args.sender; let contract = &args.address; let value = U256::from_big_endian(&args.amount); - self.view(origin, contract, Wei::new(value), args.input, u64::MAX) + // View calls cannot interact with promises + let mut handler = aurora_engine_sdk::promise::Noop; + let pause_flags = EnginePrecompilesPauser::from_io(self.io).paused(); + let precompiles = self.create_precompiles(pause_flags, &mut handler); + + let executor_params = StackExecutorParams::new(u64::MAX, precompiles); + self.view( + origin, + contract, + Wei::new(value), + args.input, + &executor_params, + ) } pub fn view( @@ -645,21 +669,15 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { contract: &Address, value: Wei, input: Vec, - gas_limit: u64, + executor_params: &StackExecutorParams, ) -> Result { - // View calls cannot interact with promises - let mut handler = aurora_engine_sdk::promise::Noop; - let pause_flags = EnginePrecompilesPauser::from_io(self.io).paused(); - let precompiles = self.create_precompiles(pause_flags, &mut handler); - - let executor_params = StackExecutorParams::new(gas_limit, precompiles); let mut executor = executor_params.make_executor(self); let (status, result) = executor.transact_call( origin.raw(), contract.raw(), value.raw(), input, - gas_limit, + executor_params.gas_limit, Vec::new(), ); status.into_result(result) @@ -736,7 +754,6 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { pub fn receive_erc20_tokens( &mut self, token: &AccountId, - relayer_account_id: &AccountId, args: &NEP141FtOnTransferArgs, current_account_id: &AccountId, handler: &mut P, @@ -744,23 +761,18 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { let str_amount = crate::prelude::format!("\"{}\"", args.amount); let output_on_fail = str_amount.as_bytes(); - // Parse message to determine recipient and fee - let (recipient, fee) = { + // Parse message to determine recipient + let recipient = { // Message format: // Recipient of the transaction - 40 characters (Address in hex) - // Fee to be paid in ETH (Optional) - 64 characters (Encoded in big endian / hex) let message = args.msg.as_bytes(); assert_or_finish!(message.len() >= 40, output_on_fail, self.io); - let recipient = Address::new(H160(unwrap_res_or_finish!( + Address::new(H160(unwrap_res_or_finish!( hex::decode(&message[..40]).unwrap().as_slice().try_into(), output_on_fail, self.io - ))); - - let fee = U256::zero(); - - (recipient, fee) + ))) }; let erc20_token = Address::from_array(unwrap_res_or_finish!( @@ -775,26 +787,6 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { self.io )); - if fee != U256::from(0) { - let relayer_address = unwrap_res_or_finish!( - self.get_relayer(relayer_account_id.as_bytes()).ok_or(()), - output_on_fail, - self.io - ); - - unwrap_res_or_finish!( - self.transfer( - recipient, - relayer_address, - Wei::new_u64(fee.as_u64()), - u64::MAX, - handler, - ), - output_on_fail, - self.io - ); - } - let erc20_admin_address = current_address(current_account_id); unwrap_res_or_finish!( self.call( @@ -1785,4 +1777,608 @@ impl<'env, J: IO + Copy, E: Env> ApplyBackend for Engine<'env, J, E> { } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + use crate::parameters::{FunctionCallArgsV1, FunctionCallArgsV2}; + use aurora_engine_precompiles::make_address; + use aurora_engine_sdk::env::Fixed; + use aurora_engine_sdk::promise::Noop; + use aurora_engine_test_doubles::io::{Storage, StoragePointer}; + use aurora_engine_test_doubles::promise::PromiseTracker; + use aurora_engine_types::types::RawU256; + use sha3::{Digest, Keccak256}; + use std::sync::RwLock; + + #[test] + fn test_view_call_to_nothing_successfully_returns_nothing() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let contract = make_address(1, 1); + let value = Wei::new_u64(1000); + let input = vec![]; + let args = ViewCallArgs { + sender: origin, + address: contract, + amount: RawU256::from(value.raw()), + input, + }; + let actual_status = engine.view_with_args(args).unwrap(); + let expected_status = TransactionStatus::Succeed(Vec::new()); + + assert_eq!(expected_status, actual_status); + } + + #[test] + fn test_deploying_empty_code_succeeds() { + fn create_legacy_address(address: H160, nonce: U256) -> H160 { + let mut stream = rlp::RlpStream::new_list(2); + stream.append(&address); + stream.append(&nonce); + H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() + } + + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let io = StoragePointer(&storage); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let input = vec![]; + let mut handler = Noop; + + let actual_result = engine.deploy_code_with_input(input, &mut handler).unwrap(); + + let nonce = U256::zero(); + let expected_address = create_legacy_address(origin.raw(), nonce).0.to_vec(); + let expected_status = TransactionStatus::Succeed(expected_address); + let expected_gas_used = 53000; + let expected_logs = Vec::new(); + let expected_result = SubmitResult::new(expected_status, expected_gas_used, expected_logs); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_call_v2_succeeds() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let input = Vec::::new(); + let mut handler = Noop; + let contract = make_address(1, 1); + let value = Wei::new_u64(1000); + let args = CallArgs::V2(FunctionCallArgsV2 { + contract, + value: RawU256::from(value.raw()), + input, + }); + let actual_result = engine.call_with_args(args, &mut handler).unwrap(); + + let expected_address = Vec::new(); + let expected_status = TransactionStatus::Succeed(expected_address); + let expected_gas_used = 21000; + let expected_logs = Vec::new(); + let expected_result = SubmitResult::new(expected_status, expected_gas_used, expected_logs); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_call_with_empty_balance_fails_with_out_of_funds_error() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let io = StoragePointer(&storage); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let input = Vec::::new(); + let mut handler = Noop; + let contract = make_address(1, 1); + let value = Wei::new_u64(1000); + let args = CallArgs::V2(FunctionCallArgsV2 { + contract, + value: RawU256::from(value.raw()), + input, + }); + let actual_result = engine.call_with_args(args, &mut handler).unwrap(); + + let expected_status = TransactionStatus::OutOfFund; + let expected_gas_used = 21000; + let expected_logs = Vec::new(); + let expected_result = SubmitResult::new(expected_status, expected_gas_used, expected_logs); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_transfer_moves_balance_from_sender_to_recipient() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let gas_limit = u64::MAX; + let mut handler = Noop; + let receiver = make_address(1, 1); + let value = Wei::new_u64(1000); + let actual_result = engine + .transfer(origin, receiver, value, gas_limit, &mut handler) + .unwrap(); + + let expected_address = Vec::new(); + let expected_status = TransactionStatus::Succeed(expected_address); + let expected_gas_used = 21000; + let expected_logs = Vec::new(); + let expected_result = SubmitResult::new(expected_status, expected_gas_used, expected_logs); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_call_v1_succeeds() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let input = Vec::::new(); + let mut handler = Noop; + let contract = make_address(1, 1); + let args = CallArgs::V1(FunctionCallArgsV1 { contract, input }); + let actual_result = engine.call_with_args(args, &mut handler).unwrap(); + + let expected_address = Vec::new(); + let expected_status = TransactionStatus::Succeed(expected_address); + let expected_gas_used = 21000; + let expected_logs = Vec::new(); + let expected_result = SubmitResult::new(expected_status, expected_gas_used, expected_logs); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_registering_relayer_succeeds() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id, + io, + &env, + ); + + let account_id = AccountId::new("relayer").unwrap(); + let expected_relayer_address = make_address(1, 1); + engine.register_relayer(account_id.as_bytes(), expected_relayer_address); + let actual_relayer_address = engine.get_relayer(account_id.as_bytes()).unwrap(); + + assert_eq!(expected_relayer_address, actual_relayer_address); + } + + #[test] + fn test_registering_token_succeeds() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + set_balance(&mut io, &origin, &Wei::new_u64(22000)); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id.clone(), + io, + &env, + ); + + let receiver = make_address(6, 6); + let erc20_token = make_address(4, 5); + let nep141_token = AccountId::new("testcoin").unwrap(); + let args = NEP141FtOnTransferArgs { + sender_id: Default::default(), + amount: Default::default(), + msg: receiver.encode(), + }; + let mut handler = Noop; + engine + .register_token(erc20_token, nep141_token.clone()) + .unwrap(); + engine.receive_erc20_tokens(&nep141_token, &args, ¤t_account_id, &mut handler); + + let storage_read = storage.read().unwrap(); + let actual_output = std::str::from_utf8(storage_read.output.as_slice()).unwrap(); + let expected_output = "\"0\""; + + assert_eq!(expected_output, actual_output); + } + + #[test] + fn test_deploying_token_succeeds() { + let origin = Address::zero(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + set_state(&mut io, EngineState::default()); + + let nep141_token = AccountId::new("testcoin").unwrap(); + let mut handler = Noop; + let args = DeployErc20TokenArgs { + nep141: nep141_token, + }; + let expected_address = Address::decode("4eb2abfb7cbcb81d7f1a91a5a9caf4a4ef129dbd").unwrap(); + let actual_address = deploy_erc20_token(args, io, &env, &mut handler).unwrap(); + + assert_eq!(expected_address, actual_address); + } + + #[test] + fn test_gas_charge_for_empty_transaction_is_zero() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let mut engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id.clone(), + io, + &env, + ); + + let transaction = NormalizedEthTransaction { + address: Default::default(), + chain_id: None, + nonce: Default::default(), + gas_limit: U256::MAX, + max_priority_fee_per_gas: Default::default(), + max_fee_per_gas: U256::MAX, + to: None, + value: Default::default(), + data: vec![], + access_list: vec![], + }; + let actual_result = engine.charge_gas(&origin, &transaction).unwrap(); + + let expected_result = GasPaymentResult { + prepaid_amount: Wei::zero(), + effective_gas_price: U256::zero(), + priority_fee_per_gas: U256::zero(), + }; + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_scheduling_promise_creates_it() { + use aurora_engine_test_doubles::promise::PromiseArgs; + use std::collections::HashMap; + + let mut promise_tracker = PromiseTracker::default(); + let args = PromiseCreateArgs { + target_account_id: Default::default(), + method: "".to_string(), + args: vec![], + attached_balance: Default::default(), + attached_gas: Default::default(), + }; + let actual_id = schedule_promise(&mut promise_tracker, &args); + let actual_scheduled_promises = promise_tracker.scheduled_promises; + let expected_scheduled_promises = { + let mut map = HashMap::new(); + map.insert(actual_id.raw(), PromiseArgs::Create(args)); + map + }; + + assert_eq!(expected_scheduled_promises, actual_scheduled_promises); + } + + #[test] + fn test_scheduling_promise_callback_adds_it() { + use aurora_engine_test_doubles::promise::PromiseArgs; + use std::collections::HashMap; + + let mut promise_tracker = PromiseTracker::default(); + let args = PromiseCreateArgs { + target_account_id: Default::default(), + method: "".to_string(), + args: vec![], + attached_balance: Default::default(), + attached_gas: Default::default(), + }; + let base_id = PromiseId::new(6); + let actual_id = schedule_promise_callback(&mut promise_tracker, base_id, &args); + let actual_scheduled_promises = promise_tracker.scheduled_promises; + let expected_scheduled_promises = { + let mut map = HashMap::new(); + map.insert( + actual_id.raw(), + PromiseArgs::Callback { + base: base_id, + callback: args, + }, + ); + map + }; + + assert_eq!(expected_scheduled_promises, actual_scheduled_promises); + } + + #[test] + fn test_loading_original_storage_loads_stored_value() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + let engine = Engine::new_with_state( + EngineState::default(), + origin.clone(), + current_account_id.clone(), + io, + &env, + ); + + let expected_value = H256::from_low_u64_le(64); + let index = H256::zero(); + let generation = get_generation(&mut io, &origin); + set_storage(&mut io, &origin, &index, &expected_value, generation); + let actual_value = engine.original_storage(origin.raw(), index).unwrap(); + + assert_eq!(expected_value, actual_value); + } + + #[test] + fn test_loading_engine_from_storage_loads_stored_state() { + let origin = Address::zero(); + let current_account_id = AccountId::default(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + let expected_state = EngineState::default(); + set_state(&mut io, expected_state.clone()); + let engine = Engine::new(origin.clone(), current_account_id.clone(), io, &env).unwrap(); + let actual_state = engine.state; + + assert_eq!(expected_state, actual_state); + } + + #[test] + fn test_refund_transfer_eth_back_from_precompile_address() { + let recipient_address = make_address(1, 1); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + let expected_state = EngineState::default(); + let refund_amount = Wei::new_u64(1000); + add_balance(&mut io, &exit_to_near::ADDRESS, refund_amount).unwrap(); + set_state(&mut io, expected_state.clone()); + let args = RefundCallArgs { + recipient_address, + erc20_address: None, + amount: RawU256::from(refund_amount.raw()), + }; + let mut handler = Noop; + let actual_result = refund_on_error(io, &env, expected_state, args, &mut handler).unwrap(); + let expected_result = + SubmitResult::new(TransactionStatus::Succeed(Vec::new()), 25800, Vec::new()); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_refund_remint_burned_erc20_tokens() { + let origin = Address::zero(); + let env = Fixed::default(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + let expected_state = EngineState::default(); + set_state(&mut io, expected_state.clone()); + let value = Wei::new_u64(1000); + let args = RefundCallArgs { + recipient_address: Default::default(), + erc20_address: Some(origin.clone()), + amount: RawU256::from(value.raw()), + }; + let mut handler = Noop; + let actual_result = refund_on_error(io, &env, expected_state, args, &mut handler).unwrap(); + let expected_result = + SubmitResult::new(TransactionStatus::Succeed(Vec::new()), 21344, Vec::new()); + + assert_eq!(expected_result, actual_result); + } + + #[test] + fn test_refund_free_effective_gas_does_nothing() { + let origin = Address::zero(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + let expected_state = EngineState::default(); + set_state(&mut io, expected_state.clone()); + let relayer = make_address(1, 1); + let gas_result = GasPaymentResult { + prepaid_amount: Default::default(), + effective_gas_price: U256::zero(), + priority_fee_per_gas: U256::zero(), + }; + + refund_unused_gas(&mut io, &origin, 1000, gas_result, &relayer).unwrap(); + } + + #[test] + fn test_refund_gas_pays_expected_amount() { + let origin = Address::zero(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + let expected_state = EngineState::default(); + set_state(&mut io, expected_state.clone()); + let relayer = make_address(1, 1); + let gas_result = GasPaymentResult { + prepaid_amount: Wei::new_u64(8000), + effective_gas_price: Wei::new_u64(1).raw(), + priority_fee_per_gas: U256::zero(), + }; + let gas_used = 4000; + + refund_unused_gas(&mut io, &origin, gas_used, gas_result, &relayer).unwrap(); + + let actual_refund = get_balance(&io, &origin); + let expected_refund = Wei::new_u64(gas_used); + + assert_eq!(expected_refund, actual_refund); + } + + #[test] + fn test_check_nonce_with_increment_succeeds() { + let origin = Address::zero(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + + increment_nonce(&mut io, &origin); + check_nonce(&io, &origin, &U256::from(1u64)).unwrap(); + } + + #[test] + fn test_check_nonce_without_increment_fails() { + let origin = Address::zero(); + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + + increment_nonce(&mut io, &origin); + let actual_error_kind = check_nonce(&io, &origin, &U256::from(0u64)).unwrap_err(); + let actual_error_kind = std::str::from_utf8(actual_error_kind.as_bytes()).unwrap(); + let expected_error_kind = std::str::from_utf8(errors::ERR_INCORRECT_NONCE).unwrap(); + + assert_eq!(expected_error_kind, actual_error_kind); + } + + #[test] + fn test_missing_engine_state_is_not_found() { + let storage = Storage::default(); + let storage = RwLock::new(storage); + let io = StoragePointer(&storage); + + let actual_error = get_state(&io).unwrap_err(); + let actual_error = std::str::from_utf8(actual_error.as_ref()).unwrap(); + let expected_error = std::str::from_utf8(errors::ERR_STATE_NOT_FOUND).unwrap(); + + assert_eq!(expected_error, actual_error); + } + + #[test] + fn test_empty_engine_state_is_corrupted() { + let storage = Storage::default(); + let storage = RwLock::new(storage); + let mut io = StoragePointer(&storage); + + io.write_storage(&bytes_to_key(KeyPrefix::Config, STATE_KEY), &[]); + let actual_error = get_state(&io).unwrap_err(); + let actual_error = std::str::from_utf8(actual_error.as_ref()).unwrap(); + let expected_error = std::str::from_utf8(errors::ERR_STATE_CORRUPTED).unwrap(); + + assert_eq!(expected_error, actual_error); + } + + #[test] + fn test_filtering_promises_from_logs_with_none_keeps_all() { + let storage = Storage::default(); + let storage = RwLock::new(storage); + let io = StoragePointer(&storage); + let current_account_id = AccountId::default(); + let mut handler = Noop; + let logs = vec![Log { + address: Default::default(), + topics: vec![], + data: vec![], + }]; + + let actual_logs = filter_promises_from_logs(&io, &mut handler, logs, ¤t_account_id); + let expected_logs = vec![ResultLog { + address: Default::default(), + topics: vec![], + data: vec![], + }]; + + assert_eq!(expected_logs, actual_logs); + } +} diff --git a/engine/src/json.rs b/engine/src/json.rs index 2759a82b3..fec6b08bf 100644 --- a/engine/src/json.rs +++ b/engine/src/json.rs @@ -25,7 +25,6 @@ pub enum JsonError { InvalidU128, InvalidBool, InvalidString, - InvalidArray, ExpectedStringGotNumber, OutOfRange(JsonOutOfRangeError), } @@ -108,7 +107,6 @@ impl AsRef<[u8]> for JsonError { Self::InvalidU128 => errors::ERR_FAILED_PARSE_U128, Self::InvalidBool => errors::ERR_FAILED_PARSE_BOOL, Self::InvalidString => errors::ERR_FAILED_PARSE_STRING, - Self::InvalidArray => errors::ERR_FAILED_PARSE_ARRAY, Self::ExpectedStringGotNumber => errors::ERR_EXPECTED_STRING_GOT_NUMBER, Self::OutOfRange(err) => err.as_ref(), } @@ -124,23 +122,6 @@ impl AsRef<[u8]> for JsonOutOfRangeError { } } -#[cfg(test)] -impl std::fmt::Debug for JsonError { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.write_fmt(format_args!( - "{}", - std::str::from_utf8(self.as_ref()).unwrap() - )) - } -} - -#[cfg(test)] -impl std::fmt::Display for JsonError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_fmt(format_args!("{:?}", *self)) - } -} - impl Array for JsonArray { fn new() -> Self { JsonArray(Vec::new()) @@ -282,35 +263,65 @@ pub fn parse_json(data: &[u8]) -> Option { mod tests { use super::*; + #[test] + fn test_json_all_types_fail_to_parse_missing_key() { + let expected_err = std::str::from_utf8(errors::ERR_JSON_MISSING_VALUE).unwrap(); + let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap(); + + let actual_err = json.string("missing_key").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + + let actual_err = json.bool("missing_key").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + + let actual_err = json.u64("missing_key").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + + let actual_err = json.u128("missing_key").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + } + #[test] fn test_json_type_string() { let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap(); let string_data = json.string("foo").ok().unwrap(); assert_eq!(string_data, "abcd"); + let expected_err = std::str::from_utf8(errors::ERR_FAILED_PARSE_STRING).unwrap(); let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap(); - let err = json.string("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidString); + let actual_err = json.string("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap(); - let err = json.string("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidString); + let actual_err = json.string("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": ["abcd"]}"#.as_bytes()).unwrap(); - let err = json.string("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidString); + let actual_err = json.string("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap(); - let err = json.string("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidString); + let actual_err = json.string("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap(); - let err = json.string("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidString); + let actual_err = json.string("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_NOT_A_JSON_TYPE).unwrap(); let json = JsonValue::Null; - let err = json.string("foo").unwrap_err(); - assert_eq!(err, JsonError::NotJsonType); + let actual_err = json.string("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); } #[test] @@ -329,41 +340,52 @@ mod tests { let val = json.u64("foo").ok().unwrap(); assert_eq!(val, u64::MAX); + let expected_err = std::str::from_utf8(errors::ERR_FAILED_PARSE_U64).unwrap(); let json = parse_json(r#"{"foo": 12.99}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": -123}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": "123"}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": [123]}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap(); - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU64); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_NOT_A_JSON_TYPE).unwrap(); let json = JsonValue::Null; - let err = json.u64("foo").unwrap_err(); - assert_eq!(err, JsonError::NotJsonType); + let actual_err = json.u64("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); } #[test] @@ -372,48 +394,60 @@ mod tests { let val = json.u128("foo").ok().unwrap(); assert_eq!(val, 123); + let expected_err = + std::str::from_utf8(JsonOutOfRangeError::OutOfRangeU128.as_ref()).unwrap(); let json = parse_json(r#"{"foo": "-123"}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!( - err, - JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU128) - ); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_EXPECTED_STRING_GOT_NUMBER).unwrap(); let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::ExpectedStringGotNumber); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": 12.3}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::ExpectedStringGotNumber); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_FAILED_PARSE_U128).unwrap(); let json = parse_json(r#"{"foo": "12.3"}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU128); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU128); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": true}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU128); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": ["123"]}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU128); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU128); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap(); - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidU128); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_NOT_A_JSON_TYPE).unwrap(); let json = JsonValue::Null; - let err = json.u128("foo").unwrap_err(); - assert_eq!(err, JsonError::NotJsonType); + let actual_err = json.u128("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); } #[test] @@ -426,41 +460,52 @@ mod tests { let val = json.bool("foo").ok().unwrap(); assert_eq!(val, false); + let expected_err = std::str::from_utf8(errors::ERR_FAILED_PARSE_BOOL).unwrap(); let json = parse_json(r#"{"foo": "true"}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": "false"}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": [true]}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": 123}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": 12.3}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": "abcd"}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": {}}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); let json = parse_json(r#"{"foo": null}"#.as_bytes()).unwrap(); - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::InvalidBool); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_NOT_A_JSON_TYPE).unwrap(); let json = JsonValue::Null; - let err = json.bool("foo").unwrap_err(); - assert_eq!(err, JsonError::NotJsonType); + let actual_err = json.bool("foo").unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); } #[test] @@ -469,20 +514,23 @@ mod tests { let val = JsonValue::parse_u8(&json).ok().unwrap(); assert_eq!(val, 123); + let expected_err = std::str::from_utf8(errors::ERR_FAILED_PARSE_U8).unwrap(); let json = JsonValue::from(-1_i64); - let err = JsonValue::parse_u8(&json).unwrap_err(); - assert_eq!(err, JsonError::InvalidU8); + let actual_err = JsonValue::parse_u8(&json).unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(JsonOutOfRangeError::OutOfRangeU8.as_ref()).unwrap(); let json = JsonValue::from(256_u64); - let err = JsonValue::parse_u8(&json).unwrap_err(); - assert_eq!( - err, - JsonError::OutOfRange(JsonOutOfRangeError::OutOfRangeU8) - ); + let actual_err = JsonValue::parse_u8(&json).unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); + let expected_err = std::str::from_utf8(errors::ERR_FAILED_PARSE_U8).unwrap(); let json = JsonValue::from("abcd".to_string()); - let err = JsonValue::parse_u8(&json).unwrap_err(); - assert_eq!(err, JsonError::InvalidU8); + let actual_err = JsonValue::parse_u8(&json).unwrap_err(); + let actual_err = std::str::from_utf8(actual_err.as_ref()).unwrap(); + assert_eq!(actual_err, expected_err); } #[test] diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 5329882bf..f9048b397 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -395,10 +395,8 @@ mod contract { .ft_on_transfer(&engine, &args) .sdk_unwrap(); } else { - let signer_account_id = io.signer_account_id(); engine.receive_erc20_tokens( &predecessor_account_id, - &signer_account_id, &args, ¤t_account_id, &mut Runtime, diff --git a/engine/src/pausables.rs b/engine/src/pausables.rs index 86fc1ff2a..297c237c9 100644 --- a/engine/src/pausables.rs +++ b/engine/src/pausables.rs @@ -174,85 +174,11 @@ impl PausedPrecompilesManager for EnginePrecompilesPauser { #[cfg(test)] mod tests { use super::*; + use aurora_engine_test_doubles::io::{Storage, StoragePointer}; use std::iter::once; + use std::sync::RwLock; use test_case::test_case; - struct MockStorage { - key: Vec, - value: Option, - } - - impl MockStorage { - pub fn new(key: Vec) -> Self { - Self { key, value: None } - } - } - - impl IO for MockStorage { - type StorageValue = MockValue; - - fn read_input(&self) -> Self::StorageValue { - unimplemented!() - } - - fn return_output(&mut self, _value: &[u8]) { - unimplemented!() - } - - fn read_storage(&self, key: &[u8]) -> Option { - match self.key.as_slice() == key { - true => self.value.as_ref().map(Clone::clone), - false => None, - } - } - - fn storage_has_key(&self, _key: &[u8]) -> bool { - unimplemented!() - } - - fn write_storage(&mut self, key: &[u8], value: &[u8]) -> Option { - match self.key.as_slice() == key { - true => { - let old_value = self.value.as_ref().map(Clone::clone); - - self.value = Some(MockValue(value.to_vec())); - - old_value - } - false => None, - } - } - - fn write_storage_direct( - &mut self, - _key: &[u8], - _value: Self::StorageValue, - ) -> Option { - unimplemented!() - } - - fn remove_storage(&mut self, _key: &[u8]) -> Option { - unimplemented!() - } - } - - #[derive(Clone)] - pub struct MockValue(Vec); - - impl StorageIntermediate for MockValue { - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn copy_to_slice(&self, buffer: &mut [u8]) { - buffer.copy_from_slice(&self.0) - } - } - #[test_case(PrecompileFlags::EXIT_TO_ETHEREUM, exit_to_ethereum::ADDRESS)] #[test_case(PrecompileFlags::EXIT_TO_NEAR, exit_to_near::ADDRESS)] fn test_paused_flag_marks_precompiles_address_as_paused( @@ -272,8 +198,8 @@ mod tests { #[test] fn test_pausing_precompile_marks_it_as_paused() { - let key = EnginePrecompilesPauser::::storage_key(); - let io = MockStorage::new(key); + let storage = RwLock::new(Storage::default()); + let io = StoragePointer(&storage); let mut pauser = EnginePrecompilesPauser::from_io(io); let flags = PrecompileFlags::EXIT_TO_NEAR; @@ -284,8 +210,8 @@ mod tests { #[test] fn test_resuming_precompile_removes_its_mark_as_paused() { - let key = EnginePrecompilesPauser::::storage_key(); - let io = MockStorage::new(key); + let storage = RwLock::new(Storage::default()); + let io = StoragePointer(&storage); let mut pauser = EnginePrecompilesPauser::from_io(io); let flags = PrecompileFlags::EXIT_TO_NEAR; pauser.pause_precompiles(flags); @@ -313,8 +239,9 @@ mod tests { #[test] fn test_no_precompile_is_paused_if_storage_contains_too_few_bytes() { - let key = EnginePrecompilesPauser::::storage_key(); - let mut io = MockStorage::new(key.clone()); + let key = EnginePrecompilesPauser::::storage_key(); + let storage = RwLock::new(Storage::default()); + let mut io = StoragePointer(&storage); io.write_storage(key.as_slice(), &[7u8]); let pauser = EnginePrecompilesPauser::from_io(io);