diff --git a/engine-precompiles/src/promise_result.rs b/engine-precompiles/src/promise_result.rs index 223536c65..908948426 100644 --- a/engine-precompiles/src/promise_result.rs +++ b/engine-precompiles/src/promise_result.rs @@ -16,7 +16,7 @@ pub mod costs { use crate::prelude::types::EthGas; /// This cost is always charged for calling this precompile. - pub const PROMISE_RESULT_BASE_COST: EthGas = EthGas::new(105); + pub const PROMISE_RESULT_BASE_COST: EthGas = EthGas::new(53); /// This is the cost per byte of promise result data. pub const PROMISE_RESULT_BYTE_COST: EthGas = EthGas::new(1); } diff --git a/engine-standalone-storage/src/sync/mod.rs b/engine-standalone-storage/src/sync/mod.rs index 006d1aaba..edc9a97a6 100644 --- a/engine-standalone-storage/src/sync/mod.rs +++ b/engine-standalone-storage/src/sync/mod.rs @@ -221,7 +221,7 @@ fn non_submit_execute<'db>( if env.predecessor_account_id == env.current_account_id { connector::EthConnectorContract::init_instance(io)? - .ft_on_transfer(&engine, args)?; + .ft_on_transfer(&mut engine, args)?; } else { engine.receive_erc20_tokens( &env.predecessor_account_id, diff --git a/engine-tests/src/test_utils/standalone/mocks/mod.rs b/engine-tests/src/test_utils/standalone/mocks/mod.rs index cfb5ec29d..df21c02bc 100644 --- a/engine-tests/src/test_utils/standalone/mocks/mod.rs +++ b/engine-tests/src/test_utils/standalone/mocks/mod.rs @@ -132,7 +132,9 @@ pub fn mint_evm_account( hex::encode(address.as_bytes()) ), }; - connector.ft_on_transfer(&engine, &transfer_args).unwrap(); + connector + .ft_on_transfer(&mut engine, &transfer_args) + .unwrap(); engine.apply(std::iter::once(state_change), std::iter::empty(), false); } diff --git a/engine-tests/src/tests/one_inch.rs b/engine-tests/src/tests/one_inch.rs index b1f4d388f..558c863d2 100644 --- a/engine-tests/src/tests/one_inch.rs +++ b/engine-tests/src/tests/one_inch.rs @@ -58,7 +58,7 @@ fn test_1inch_liquidity_protocol() { }, ); assert!(result.gas_used >= 302_000); // more than 302k EVM gas used - assert_gas_bound(profile.all_gas(), 23); + assert_gas_bound(profile.all_gas(), 22); // Same here helper.runner.context.block_timestamp += 10_000_000 * 1_000_000_000; diff --git a/engine-tests/src/tests/repro.rs b/engine-tests/src/tests/repro.rs index 5f8b88b56..3218dd559 100644 --- a/engine-tests/src/tests/repro.rs +++ b/engine-tests/src/tests/repro.rs @@ -72,7 +72,7 @@ fn repro_FRcorNv() { block_timestamp: 1650960438774745116, input_path: "src/tests/res/input_FRcorNv.hex", evm_gas_used: 1239721, - near_gas_used: 181, + near_gas_used: 180, }); } diff --git a/engine/src/connector.rs b/engine/src/connector.rs index 21b359643..d6ad28018 100644 --- a/engine/src/connector.rs +++ b/engine/src/connector.rs @@ -322,13 +322,15 @@ impl EthConnectorContract { } /// Mint ETH tokens - fn mint_eth_on_aurora( + fn mint_eth_on_aurora<'env, E: Env>( &mut self, + engine: &mut Engine<'env, I, E>, owner_id: Address, amount: Wei, ) -> Result<(), fungible_token::error::DepositError> { sdk::log!("Mint {} ETH tokens for: {}", amount, owner_id.encode()); - self.ft.internal_deposit_eth_to_aurora(owner_id, amount) + self.ft + .internal_deposit_eth_to_aurora(engine, owner_id, amount) } /// Burn ETH tokens @@ -575,7 +577,7 @@ impl EthConnectorContract { /// ft_on_transfer callback function pub fn ft_on_transfer<'env, E: Env>( &mut self, - engine: &Engine<'env, I, E>, + engine: &mut Engine<'env, I, E>, args: &NEP141FtOnTransferArgs, ) -> Result<(), error::FtTransferCallError> { sdk::log!("Call ft_on_transfer"); @@ -590,12 +592,14 @@ impl EthConnectorContract { match (wei_fee, relayer) { (fee, Some(evm_relayer_address)) if fee > ZERO_WEI => { self.mint_eth_on_aurora( + engine, message_data.recipient, Wei::new(U256::from(args.amount.as_u128())) - fee, )?; - self.mint_eth_on_aurora(evm_relayer_address, fee)?; + self.mint_eth_on_aurora(engine, evm_relayer_address, fee)?; } _ => self.mint_eth_on_aurora( + engine, message_data.recipient, Wei::new(U256::from(args.amount.as_u128())), )?, diff --git a/engine/src/engine.rs b/engine/src/engine.rs index b712f7696..37052838f 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -431,12 +431,14 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { .checked_mul(effective_gas_price) .map(Wei::new) .ok_or(GasPaymentError::EthAmountOverflow)?; - let balance = self.cached_balance(sender); + let balance = self.get_balance(sender); let new_balance = balance .checked_sub(prepaid_amount) .ok_or(GasPaymentError::OutOfFund)?; - self.update_cached_balance(sender, new_balance.raw()); + // At this point we don't need to store the balance into persistent storage. + // EVM will get the balance from the cache then. + self.set_cached_balance(sender, new_balance.raw()); self.gas_price = effective_gas_price; Ok(GasPaymentResult { @@ -816,14 +818,30 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { } } - /// Returns cached account's balance information. - fn cached_balance(&self, address: &Address) -> Wei { + /// Returns account's balance. + pub fn get_balance(&self, address: &Address) -> Wei { let basic = self.basic(address.raw()); Wei::new(basic.balance) } - /// Updates account's cached balance information. - pub fn update_cached_balance(&self, address: &Address, balance: U256) { + /// Sets account's balance. + pub fn set_balance(&mut self, address: &Address, balance: &Wei) { + self.set_cached_balance(address, balance.raw()); + set_balance(&mut self.io, address, balance); + } + + /// Increments account's balance. + pub fn add_balance(&mut self, address: &Address, amount: Wei) -> Result<(), BalanceOverflow> { + let current_balance = self.get_balance(address); + let new_balance = current_balance.checked_add(amount).ok_or(BalanceOverflow)?; + + self.set_balance(address, &new_balance); + + Ok(()) + } + + /// Updates account's cached balance. + pub fn set_cached_balance(&self, address: &Address, balance: U256) { let mut cache = self.account_info_cache.borrow_mut(); let mut basic = cache.get_or_insert_with(*address, || Basic { balance, @@ -833,8 +851,37 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { basic.balance = balance; } + /// Returns account's nonce. + pub fn get_nonce(&self, address: &Address) -> U256 { + let basic = self.basic(address.raw()); + basic.nonce + } + + /// Checks the nonce to ensure that the address matches the transaction + /// nonce. + #[inline] + pub fn check_nonce( + &self, + address: &Address, + transaction_nonce: &U256, + ) -> Result<(), EngineErrorKind> { + let account_nonce = self.get_nonce(address); + + if transaction_nonce != &account_nonce { + return Err(EngineErrorKind::IncorrectNonce); + } + + Ok(()) + } + + /// Sets user's nonce. + pub fn set_nonce(&mut self, address: &Address, nonce: &U256) { + self.set_cached_nonce(address, *nonce); + set_nonce(&mut self.io, address, nonce); + } + /// Updates account's cached nonce information. - fn update_cached_nonce(&self, address: &Address, nonce: U256) { + fn set_cached_nonce(&self, address: &Address, nonce: U256) { let mut cache = self.account_info_cache.borrow_mut(); let mut basic = cache.get_or_insert_with(*address, || Basic { balance: get_balance(&self.io, address).raw(), @@ -866,19 +913,13 @@ impl<'env, I: IO + Copy, E: Env> Engine<'env, I, E> { let spent_amount = gas_to_wei(gas_payment.effective_gas_price)?; let reward_amount = gas_to_wei(gas_payment.priority_fee_per_gas)?; - let balance = self.cached_balance(sender); - let refunded_balance = gas_payment + let refund = gas_payment .prepaid_amount .checked_sub(spent_amount) - .and_then(|refund| balance.checked_add(refund)) .ok_or(GasPaymentError::EthAmountOverflow)?; - set_balance(&mut self.io, sender, &refunded_balance); - self.update_cached_balance(sender, refunded_balance.raw()); - - if reward_amount > Wei::zero() { - add_balance(&mut self.io, relayer, reward_amount)?; - } + self.add_balance(sender, refund)?; + self.add_balance(relayer, reward_amount)?; Ok(()) } @@ -929,7 +970,9 @@ pub fn submit( sdk::log!("signer_address {:?}", sender); - check_nonce(&io, &sender, &transaction.nonce)?; + let mut engine = Engine::new_with_state(state, sender, current_account_id, io, env); + + engine.check_nonce(&sender, &transaction.nonce)?; // Check intrinsic gas is covered by transaction gas limit match transaction.intrinsic_gas(crate::engine::CONFIG) { @@ -947,7 +990,6 @@ pub fn submit( return Err(EngineErrorKind::MaxPriorityGasFeeTooLarge.into()); } - let mut engine = Engine::new_with_state(state, sender, current_account_id, io, env); let gas_payment = engine .charge_gas(&sender, &transaction) .map_err(EngineErrorKind::GasPayment)?; @@ -1193,35 +1235,11 @@ pub fn remove_nonce(io: &mut I, address: &Address) { io.remove_storage(&address_to_key(KeyPrefix::Nonce, address)); } -/// Checks the nonce to ensure that the address matches the transaction -/// nonce. -#[inline] -pub fn check_nonce( - io: &I, - address: &Address, - transaction_nonce: &U256, -) -> Result<(), EngineErrorKind> { - let account_nonce = get_nonce(io, address); - - if transaction_nonce != &account_nonce { - return Err(EngineErrorKind::IncorrectNonce); - } - - Ok(()) -} - pub fn get_nonce(io: &I, address: &Address) -> U256 { io.read_u256(&address_to_key(KeyPrefix::Nonce, address)) .unwrap_or_else(|_| U256::zero()) } -#[cfg(test)] -pub fn increment_nonce(io: &mut I, address: &Address) { - let account_nonce = get_nonce(io, address); - let new_nonce = account_nonce.saturating_add(U256::one()); - set_nonce(io, address, &new_nonce); -} - pub fn create_legacy_address(caller: &Address, nonce: &U256) -> Address { let mut stream = rlp::RlpStream::new_list(2); stream.append(&caller.raw()); @@ -1245,17 +1263,6 @@ pub fn get_erc20_from_nep141( .ok_or(GetErc20FromNep141Error::Nep141NotFound) } -pub fn add_balance( - io: &mut I, - address: &Address, - amount: Wei, -) -> Result<(), BalanceOverflow> { - let current_balance = get_balance(io, address); - let new_balance = current_balance.checked_add(amount).ok_or(BalanceOverflow)?; - set_balance(io, address, &new_balance); - Ok(()) -} - pub fn set_balance(io: &mut I, address: &Address, balance: &Wei) { io.write_storage( &address_to_key(KeyPrefix::Balance, address), @@ -1648,13 +1655,11 @@ impl<'env, J: IO + Copy, E: Env> ApplyBackend for Engine<'env, J, E> { let generation = get_generation(&self.io, &address); if current_basic.nonce != basic.nonce { - set_nonce(&mut self.io, &address, &basic.nonce); - self.update_cached_nonce(&address, basic.nonce); + self.set_nonce(&address, &basic.nonce); writes_counter += 1; } if current_basic.balance != basic.balance { - set_balance(&mut self.io, &address, &Wei::new(basic.balance)); - self.update_cached_balance(&address, basic.balance); + self.set_balance(&address, &Wei::new(basic.balance)); writes_counter += 1; } @@ -1755,7 +1760,6 @@ mod tests { use aurora_engine_test_doubles::io::{Storage, StoragePointer}; use aurora_engine_test_doubles::promise::PromiseTracker; use aurora_engine_types::types::RawU256; - use std::cell::RefCell; #[test] fn test_view_call_to_empty_contract_without_input_returns_empty_data() { @@ -1763,11 +1767,12 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); - let engine = + let io = StoragePointer(&storage); + let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + let contract = make_address(1, 1); let value = Wei::new_u64(1000); let input = vec![]; @@ -1814,11 +1819,12 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + let input = Vec::::new(); let mut handler = Noop; let contract = make_address(1, 1); @@ -1874,11 +1880,12 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + let gas_limit = u64::MAX; let mut handler = Noop; let receiver = make_address(1, 1); @@ -1902,11 +1909,12 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + let input = Vec::::new(); let mut handler = Noop; let contract = make_address(1, 1); @@ -1928,11 +1936,12 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + 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); @@ -1947,8 +1956,7 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - set_balance(&mut io, &origin, &Wei::new_u64(22000)); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state( EngineState::default(), origin, @@ -1957,6 +1965,8 @@ mod tests { &env, ); + engine.set_balance(&origin, &Wei::new_u64(22000)); + let receiver = make_address(6, 6); let erc20_token = make_address(4, 5); let nep141_token = AccountId::new("testcoin").unwrap(); @@ -1986,9 +1996,12 @@ mod tests { ); let storage = RefCell::new(Storage::default()); let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); state::set_state(&mut io, EngineState::default()).unwrap(); + let mut engine = Engine::new(origin, AccountId::default(), io, &env).unwrap(); + + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + let nep141_token = AccountId::new("testcoin").unwrap(); let mut handler = Noop; let args = DeployErc20TokenArgs { @@ -2007,11 +2020,12 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, Wei::new_u64(22000)).unwrap(); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); + engine.add_balance(&origin, Wei::new_u64(22000)).unwrap(); + let transaction = NormalizedEthTransaction { address: Default::default(), chain_id: None, @@ -2128,14 +2142,19 @@ mod tests { #[test] fn test_refund_transfer_eth_back_from_precompile_address() { + let origin = Address::zero(); let recipient_address = make_address(1, 1); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); 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(); state::set_state(&mut io, expected_state.clone()).unwrap(); + let mut engine = Engine::new(origin, AccountId::default(), io, &env).unwrap(); + + engine + .add_balance(&exit_to_near::ADDRESS, refund_amount) + .unwrap(); let args = RefundCallArgs { recipient_address, erc20_address: None, @@ -2217,7 +2236,7 @@ mod tests { let actual_balance = get_balance(&io, &origin); assert_eq!(expected_balance, actual_balance); - let cached_balance = engine.cached_balance(&origin); + let cached_balance = engine.get_balance(&origin); assert_eq!(expected_balance, cached_balance); } @@ -2226,9 +2245,14 @@ mod tests { let origin = Address::zero(); let storage = RefCell::new(Storage::default()); let mut io = StoragePointer(&storage); + let env = Fixed::default(); + let expected_state = EngineState::default(); + state::set_state(&mut io, expected_state).unwrap(); + let mut engine = Engine::new(origin, AccountId::default(), io, &env).unwrap(); + let nonce = U256::from(100u64); - increment_nonce(&mut io, &origin); - check_nonce(&io, &origin, &U256::from(1u64)).unwrap(); + engine.set_nonce(&origin, &nonce); + engine.check_nonce(&origin, &nonce).unwrap(); } #[test] @@ -2236,9 +2260,11 @@ mod tests { let origin = Address::zero(); let storage = RefCell::new(Storage::default()); let mut io = StoragePointer(&storage); - - increment_nonce(&mut io, &origin); - let actual_error_kind = check_nonce(&io, &origin, &U256::from(0u64)).unwrap_err(); + let env = Fixed::default(); + let expected_state = EngineState::default(); + state::set_state(&mut io, expected_state).unwrap(); + let engine = Engine::new(origin, AccountId::default(), io, &env).unwrap(); + let actual_error_kind = engine.check_nonce(&origin, &U256::from(100)).unwrap_err(); assert_eq!(actual_error_kind.as_bytes(), errors::ERR_INCORRECT_NONCE); } @@ -2285,12 +2311,13 @@ mod tests { let current_account_id = AccountId::default(); let env = Fixed::default(); let storage = RefCell::new(Storage::default()); - let mut io = StoragePointer(&storage); - add_balance(&mut io, &origin, init_balance).unwrap(); + let io = StoragePointer(&storage); let mut engine = Engine::new_with_state(EngineState::default(), origin, current_account_id, io, &env); - assert_eq!(engine.cached_balance(&origin), init_balance); + engine.add_balance(&origin, init_balance).unwrap(); + + assert_eq!(engine.get_balance(&origin), init_balance); let transaction = NormalizedEthTransaction { address: Address::default(), @@ -2313,14 +2340,14 @@ mod tests { }; assert_eq!(expected_result, actual_result); - assert_eq!(engine.cached_balance(&origin), Wei::new_u64(50_000)); + assert_eq!(engine.get_balance(&origin), Wei::new_u64(50_000)); engine .refund_unused_gas(&origin, 25_000, actual_result, &Address::zero()) .unwrap(); let expected_balance = Wei::new_u64(75_000); - assert_eq!(engine.cached_balance(&origin), expected_balance); + assert_eq!(engine.get_balance(&origin), expected_balance); assert_eq!(get_balance(&engine.io, &origin), expected_balance); } } diff --git a/engine/src/fungible_token.rs b/engine/src/fungible_token.rs index 24f98ecc0..6a0574e52 100644 --- a/engine/src/fungible_token.rs +++ b/engine/src/fungible_token.rs @@ -1,5 +1,5 @@ use crate::connector::ZERO_ATTACHED_BALANCE; -use crate::engine; +use crate::engine::{self, Engine}; use crate::parameters::{NEP141FtOnTransferArgs, ResolveTransferCallArgs, StorageBalance}; use crate::prelude::account_id::AccountId; use crate::prelude::Wei; @@ -8,6 +8,7 @@ use crate::prelude::{ PromiseBatchAction, PromiseCreateArgs, PromiseResult, PromiseWithCallbackArgs, StorageBalanceBounds, StorageUsage, String, ToString, Vec, }; +use aurora_engine_sdk::env::Env; use aurora_engine_sdk::io::{StorageIntermediate, IO}; use aurora_engine_types::types::{NEP141Wei, Yocto, ZERO_NEP141_WEI, ZERO_YOCTO}; use serde::{Deserialize, Serialize}; @@ -141,8 +142,9 @@ impl FungibleTokenOps { } /// Internal ETH deposit to Aurora - pub fn internal_deposit_eth_to_aurora( + pub fn internal_deposit_eth_to_aurora<'env, E: Env>( &mut self, + engine: &mut Engine<'env, I, E>, address: Address, amount: Wei, ) -> Result<(), error::DepositError> { @@ -150,7 +152,7 @@ impl FungibleTokenOps { let new_balance = balance .checked_add(amount) .ok_or(error::DepositError::BalanceOverflow)?; - engine::set_balance(&mut self.io, &address, &new_balance); + engine.set_balance(&address, &new_balance); self.total_eth_supply_on_aurora = self .total_eth_supply_on_aurora .checked_add(amount) diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 78ea86349..69c37e53a 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -395,7 +395,7 @@ mod contract { if predecessor_account_id == current_account_id { EthConnectorContract::init_instance(io) .sdk_unwrap() - .ft_on_transfer(&engine, &args) + .ft_on_transfer(&mut engine, &args) .sdk_unwrap(); } else { engine.receive_erc20_tokens(