From 7f1723972f6530d58a022c092c92f9c0f6c6fb20 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Wed, 20 Oct 2021 17:49:38 +0200 Subject: [PATCH] Refactor all storage access to be parametric in the new IO trait --- engine-precompiles/src/native.rs | 5 +- engine-sdk/src/error.rs | 18 ++ engine-sdk/src/io.rs | 28 ++- engine-sdk/src/lib.rs | 79 +------ engine-sdk/src/near_runtime.rs | 1 + engine-sdk/src/types.rs | 5 +- engine-tests/src/test_utils/mod.rs | 2 +- engine-tests/src/tests/sanity.rs | 3 +- engine/src/connector.rs | 150 ++++++------ engine/src/engine.rs | 351 +++++++++++++++------------- engine/src/fungible_token.rs | 47 ++-- engine/src/lib.rs | 307 ++++++++++++++---------- engine/src/map.rs | 60 +++-- etc/state-migration-test/src/lib.rs | 17 +- 14 files changed, 594 insertions(+), 479 deletions(-) diff --git a/engine-precompiles/src/native.rs b/engine-precompiles/src/native.rs index a2bc96a53..6df67d2e7 100644 --- a/engine-precompiles/src/native.rs +++ b/engine-precompiles/src/native.rs @@ -193,8 +193,11 @@ impl ExitToNear { #[cfg(feature = "contract")] fn get_nep141_from_erc20(erc20_token: &[u8]) -> AccountId { + use sdk::io::{StorageIntermediate, IO}; AccountId::from_utf8( - sdk::read_storage(bytes_to_key(KeyPrefix::Erc20Nep141Map, erc20_token).as_slice()) + sdk::near_runtime::Runtime + .read_storage(bytes_to_key(KeyPrefix::Erc20Nep141Map, erc20_token).as_slice()) + .map(|s| s.to_vec()) .expect(ERR_TARGET_TOKEN_NOT_FOUND), ) .unwrap() diff --git a/engine-sdk/src/error.rs b/engine-sdk/src/error.rs index c410d0ecb..afa10702c 100644 --- a/engine-sdk/src/error.rs +++ b/engine-sdk/src/error.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct BorshDeserializeError; impl AsRef<[u8]> for BorshDeserializeError { @@ -6,6 +7,7 @@ impl AsRef<[u8]> for BorshDeserializeError { } } +#[derive(Debug)] pub struct IncorrectInputLength; impl AsRef<[u8]> for IncorrectInputLength { @@ -14,6 +16,7 @@ impl AsRef<[u8]> for IncorrectInputLength { } } +#[derive(Debug)] pub enum ReadU64Error { InvalidU64, MissingValue, @@ -27,3 +30,18 @@ impl AsRef<[u8]> for ReadU64Error { } } } + +#[derive(Debug)] +pub enum ReadU256Error { + InvalidU256, + MissingValue, +} + +impl AsRef<[u8]> for ReadU256Error { + fn as_ref(&self) -> &[u8] { + match self { + Self::InvalidU256 => b"ERR_NOT_U256", + Self::MissingValue => b"ERR_U256_NOT_FOUND", + } + } +} diff --git a/engine-sdk/src/io.rs b/engine-sdk/src/io.rs index 92c2e031f..05642d45f 100644 --- a/engine-sdk/src/io.rs +++ b/engine-sdk/src/io.rs @@ -1,6 +1,7 @@ use crate::error; use crate::prelude::{vec, Vec}; -use borsh::BorshDeserialize; +use aurora_engine_types::U256; +use borsh::{BorshDeserialize, BorshSerialize}; /// The purpose of this trait is to represent a reference to a value that /// could be obtained by IO, but without eagerly loading it into memory. @@ -109,4 +110,29 @@ pub trait IO { value.copy_to_slice(&mut result); Ok(u64::from_le_bytes(result)) } + + /// Convenience function to read a 256-bit unsigned integer from storage + /// (assumes big-endian encoding). + fn read_u256(&self, key: &[u8]) -> Result { + let value = self + .read_storage(key) + .ok_or(error::ReadU256Error::MissingValue)?; + + if value.len() != 32 { + return Err(error::ReadU256Error::InvalidU256); + } + + let mut result = [0u8; 32]; + value.copy_to_slice(&mut result); + Ok(U256::from_big_endian(&result)) + } + + fn write_borsh( + &mut self, + key: &[u8], + value: &T, + ) -> Option { + let bytes = value.try_to_vec().ok()?; + self.write_storage(key, &bytes) + } } diff --git a/engine-sdk/src/lib.rs b/engine-sdk/src/lib.rs index bde966037..0d9d3acac 100644 --- a/engine-sdk/src/lib.rs +++ b/engine-sdk/src/lib.rs @@ -3,10 +3,7 @@ #![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] #![cfg_attr(feature = "log", feature(panic_info_message))] -use crate::prelude::{ - vec, Address, BorshDeserialize, BorshSerialize, PromiseResult, Vec, H256, - STORAGE_PRICE_PER_BYTE, -}; +use crate::prelude::{vec, Address, PromiseResult, Vec, H256, STORAGE_PRICE_PER_BYTE}; pub use types::keccak; pub mod error; @@ -15,7 +12,6 @@ pub mod near_runtime; mod prelude; pub mod types; -use io::{StorageIntermediate, IO}; use near_runtime::exports; const ECRECOVER_MESSAGE_SIZE: u64 = 32; @@ -24,70 +20,6 @@ const ECRECOVER_MALLEABILITY_FLAG: u64 = 1; const GAS_FOR_STATE_MIGRATION: u64 = 100_000_000_000_000; -pub fn read_input() -> Vec { - near_runtime::Runtime.read_input().to_vec() -} - -#[cfg_attr(not(feature = "contract"), allow(dead_code))] -pub fn read_input_borsh() -> Result { - near_runtime::Runtime.read_input_borsh() -} - -#[cfg_attr(not(feature = "contract"), allow(dead_code))] -pub fn read_input_arr20() -> Result<[u8; 20], error::IncorrectInputLength> { - near_runtime::Runtime.read_input_arr20() -} - -/// Reads current input and stores in the given key keeping data in the runtime. -pub fn read_input_and_store(key: &[u8]) { - near_runtime::Runtime.read_input_and_store(key); -} - -pub fn return_output(value: &[u8]) { - near_runtime::Runtime.return_output(value); -} - -#[allow(dead_code)] -pub fn read_storage(key: &[u8]) -> Option> { - near_runtime::Runtime.read_storage(key).map(|s| s.to_vec()) -} - -pub fn read_storage_len(key: &[u8]) -> Option { - near_runtime::Runtime.read_storage_len(key) -} - -/// Read u64 from storage at given key. -pub fn read_u64(key: &[u8]) -> Result { - near_runtime::Runtime.read_u64(key) -} - -pub fn write_storage(key: &[u8], value: &[u8]) { - near_runtime::Runtime.write_storage(key, value); -} - -pub fn remove_storage(key: &[u8]) { - near_runtime::Runtime.remove_storage(key); -} - -/// Returns the size of the blob stored in the given register. -/// * If register is used, then returns the size, which can potentially be zero; -/// * If register is not used, returns `u64::MAX` -pub fn register_len(register_id: u64) -> Option { - let len = unsafe { exports::register_len(register_id) }; - - if len == u64::MAX { - None - } else { - Some(len) - } -} - -pub fn remove_storage_with_result(key: &[u8]) -> Option> { - near_runtime::Runtime - .remove_storage(key) - .map(|s| s.to_vec()) -} - #[allow(dead_code)] pub fn block_timestamp() -> u64 { // NEAR timestamp is in nanoseconds @@ -228,10 +160,6 @@ pub fn self_deploy(code_key: &[u8]) { } } -pub fn save_contract(key: &[u8], data: &T) { - write_storage(key, &data.try_to_vec().unwrap()[..]); -} - #[allow(dead_code)] pub fn log(data: &str) { log_utf8(data.as_bytes()) @@ -373,11 +301,6 @@ pub fn promise_batch_action_function_call( } } -#[allow(dead_code)] -pub fn storage_has_key(key: &[u8]) -> bool { - near_runtime::Runtime.storage_has_key(key) -} - pub struct ECRecoverErr; impl ECRecoverErr { diff --git a/engine-sdk/src/near_runtime.rs b/engine-sdk/src/near_runtime.rs index 491a26e00..272c84c1f 100644 --- a/engine-sdk/src/near_runtime.rs +++ b/engine-sdk/src/near_runtime.rs @@ -3,6 +3,7 @@ pub struct RegisterIndex(u64); /// Singleton type used to implement the IO traits in the case of using NEAR's /// runtime (i.e. for wasm contracts). +#[derive(Copy, Clone, Default)] pub struct Runtime; impl Runtime { diff --git a/engine-sdk/src/types.rs b/engine-sdk/src/types.rs index a6292789f..90388db9c 100644 --- a/engine-sdk/src/types.rs +++ b/engine-sdk/src/types.rs @@ -1,5 +1,6 @@ +use crate::io::IO; +use crate::panic_utf8; use crate::prelude::{Address, H256}; -use crate::{panic_utf8, return_output}; #[cfg(not(feature = "contract"))] use sha3::{Digest, Keccak256}; @@ -99,7 +100,7 @@ pub trait SdkProcess { impl, E: AsRef<[u8]>> SdkProcess for Result { fn sdk_process(self) { match self { - Ok(r) => return_output(r.as_ref()), + Ok(r) => crate::near_runtime::Runtime.return_output(r.as_ref()), Err(e) => panic_utf8(e.as_ref()), } } diff --git a/engine-tests/src/test_utils/mod.rs b/engine-tests/src/test_utils/mod.rs index 2399b9ac5..03ee4e1c4 100644 --- a/engine-tests/src/test_utils/mod.rs +++ b/engine-tests/src/test_utils/mod.rs @@ -215,7 +215,7 @@ impl AuroraRunner { &[crate::prelude::storage::EthConnectorStorageId::FungibleToken as u8], ); let ft_value = { - let mut current_ft: FungibleToken = trie + let mut current_ft: FungibleToken = trie .get(&ft_key) .map(|bytes| FungibleToken::try_from_slice(&bytes).unwrap()) .unwrap_or_default(); diff --git a/engine-tests/src/tests/sanity.rs b/engine-tests/src/tests/sanity.rs index 0c5432bc0..11c9f2063 100644 --- a/engine-tests/src/tests/sanity.rs +++ b/engine-tests/src/tests/sanity.rs @@ -482,8 +482,7 @@ fn test_block_hash() { crate::prelude::u256_to_arr(&number) }; let account_id = runner.aurora_account_id; - let block_hash = - aurora_engine::engine::Engine::compute_block_hash(chain_id, 10, account_id.as_bytes()); + let block_hash = aurora_engine::engine::compute_block_hash(chain_id, 10, account_id.as_bytes()); assert_eq!( hex::encode(block_hash.0).as_str(), diff --git a/engine/src/connector.rs b/engine/src/connector.rs index 6deee8a06..aabf0f747 100644 --- a/engine/src/connector.rs +++ b/engine/src/connector.rs @@ -16,6 +16,7 @@ use crate::prelude::{ PromiseResult, String, ToString, Vec, WithdrawCallArgs, ERR_FAILED_PARSE, H160, U256, }; use crate::proof::Proof; +use aurora_engine_sdk::io::{StorageIntermediate, IO}; pub const ERR_NOT_ENOUGH_BALANCE_FOR_FEE: &str = "ERR_NOT_ENOUGH_BALANCE_FOR_FEE"; pub const NO_DEPOSIT: Balance = 0; @@ -28,10 +29,11 @@ pub const PAUSE_DEPOSIT: PausedMask = 1 << 0; pub const PAUSE_WITHDRAW: PausedMask = 1 << 1; #[derive(BorshSerialize, BorshDeserialize)] -pub struct EthConnectorContract { +pub struct EthConnectorContract { contract: EthConnector, - ft: FungibleToken, + ft: FungibleToken, paused_mask: PausedMask, + io: I, } /// eth-connector specific data @@ -56,12 +58,13 @@ pub struct OnTransferMessageData { pub fee: U256, } -impl EthConnectorContract { - pub fn get_instance() -> Self { +impl EthConnectorContract { + pub fn get_instance(io: I) -> Self { Self { - contract: Self::get_contract_data(&EthConnectorStorageId::Contract), - ft: Self::get_contract_data(&EthConnectorStorageId::FungibleToken), - paused_mask: Self::get_contract_data(&EthConnectorStorageId::PausedMask), + contract: Self::get_contract_data(&io, &EthConnectorStorageId::Contract), + ft: Self::get_contract_data(&io, &EthConnectorStorageId::FungibleToken), + paused_mask: Self::get_contract_data(&io, &EthConnectorStorageId::PausedMask), + io, } } @@ -69,25 +72,30 @@ impl EthConnectorContract { crate::prelude::bytes_to_key(KeyPrefix::EthConnector, &[*suffix as u8]) } - fn get_contract_data(suffix: &EthConnectorStorageId) -> T { - let data = sdk::read_storage(&Self::get_contract_key(suffix)).expect("Failed read storage"); - T::try_from_slice(&data[..]).unwrap() + fn get_contract_data(io: &I, suffix: &EthConnectorStorageId) -> T { + io.read_storage(&Self::get_contract_key(suffix)) + .expect("Failed read storage") + .to_value() + .unwrap() } /// Init eth-connector contract specific data - pub fn init_contract(args: InitCallArgs) { + pub fn init_contract(mut io: I, args: InitCallArgs) { // Check is it already initialized assert!( - !sdk::storage_has_key(&Self::get_contract_key(&EthConnectorStorageId::Contract)), + !io.storage_has_key(&Self::get_contract_key(&EthConnectorStorageId::Contract)), "ERR_CONTRACT_INITIALIZED" ); sdk::log!("[init contract]"); - let contract_data = Self::set_contract_data(SetContractDataCallArgs { - prover_account: args.prover_account, - eth_custodian_address: args.eth_custodian_address, - metadata: args.metadata, - }); + let contract_data = Self::set_contract_data( + &mut io, + SetContractDataCallArgs { + prover_account: args.prover_account, + eth_custodian_address: args.eth_custodian_address, + metadata: args.metadata, + }, + ); let current_account_id = sdk::current_account_id(); let owner_id = String::from_utf8(current_account_id).unwrap(); @@ -96,7 +104,7 @@ impl EthConnectorContract { ft.internal_register_account(&owner_id); let paused_mask = UNPAUSE_ALL; - sdk::save_contract( + io.write_borsh( &Self::get_contract_key(&EthConnectorStorageId::PausedMask), &paused_mask, ); @@ -105,24 +113,25 @@ impl EthConnectorContract { contract: contract_data, ft, paused_mask, + io, } .save_ft_contract(); } /// Sets the contract data and returns it back - pub fn set_contract_data(args: SetContractDataCallArgs) -> EthConnector { + pub fn set_contract_data(io: &mut I, args: SetContractDataCallArgs) -> EthConnector { // Get initial contract arguments let contract_data = EthConnector { prover_account: args.prover_account, eth_custodian_address: validate_eth_address(args.eth_custodian_address).sdk_unwrap(), }; // Save eth-connector specific data - sdk::save_contract( + io.write_borsh( &Self::get_contract_key(&EthConnectorStorageId::Contract), &contract_data, ); - sdk::save_contract( + io.write_borsh( &Self::get_contract_key(&EthConnectorStorageId::FungibleTokenMetadata), &args.metadata, ); @@ -186,13 +195,12 @@ impl EthConnectorContract { } /// Deposit all types of tokens - pub fn deposit(&self) { + pub fn deposit(&self, raw_proof: Vec) { self.assert_not_paused(PAUSE_DEPOSIT); sdk::log!("[Deposit tokens]"); // Get incoming deposit arguments - let raw_proof = sdk::read_input(); let proof: Proof = Proof::try_from_slice(&raw_proof).expect(ERR_FAILED_PARSE); // Fetch event data from Proof let event = DepositedEvent::from_log_entry_data(&proof.log_entry_data); @@ -299,10 +307,7 @@ impl EthConnectorContract { /// is that in this case we only calculate the amount to be credited but /// do not save it, however, if an error occurs during the calculation, /// this will happen before `record_proof`. After that contract will save. - pub fn finish_deposit(&mut self) { - sdk::assert_private_call(); - let data: FinishDepositCallArgs = - FinishDepositCallArgs::try_from_slice(&sdk::read_input()).unwrap(); + pub fn finish_deposit(&mut self, data: FinishDepositCallArgs) { sdk::log!(&format!("Finish deposit with the amount: {}", data.amount)); assert_eq!(sdk::promise_results_count(), 1); @@ -384,45 +389,44 @@ impl EthConnectorContract { /// Withdraw nETH from NEAR accounts /// NOTE: it should be without any log data - pub fn withdraw_eth_from_near(&mut self) { + pub fn withdraw_eth_from_near(&mut self, args: WithdrawCallArgs) -> WithdrawResult { self.assert_not_paused(PAUSE_WITHDRAW); sdk::assert_one_yocto(); - let args = WithdrawCallArgs::try_from_slice(&sdk::read_input()).expect(ERR_FAILED_PARSE); - let res = WithdrawResult { - recipient_id: args.recipient_address, - amount: args.amount, - eth_custodian_address: self.contract.eth_custodian_address, - } - .try_to_vec() - .unwrap(); // Burn tokens to recipient let predecessor_account_id = String::from_utf8(sdk::predecessor_account_id()).unwrap(); self.ft .internal_withdraw_eth_from_near(&predecessor_account_id, args.amount); // Save new contract data self.save_ft_contract(); - sdk::return_output(&res[..]); + + WithdrawResult { + recipient_id: args.recipient_address, + amount: args.amount, + eth_custodian_address: self.contract.eth_custodian_address, + } } /// Returns total ETH supply on NEAR (nETH as NEP-141 token) - pub fn ft_total_eth_supply_on_near(&self) { + pub fn ft_total_eth_supply_on_near(&mut self) { let total_supply = self.ft.ft_total_eth_supply_on_near(); sdk::log!(&format!("Total ETH supply on NEAR: {}", total_supply)); - sdk::return_output(format!("\"{}\"", total_supply.to_string()).as_bytes()); + self.io + .return_output(format!("\"{}\"", total_supply.to_string()).as_bytes()); } /// Returns total ETH supply on Aurora (ETH in Aurora EVM) - pub fn ft_total_eth_supply_on_aurora(&self) { + pub fn ft_total_eth_supply_on_aurora(&mut self) { let total_supply = self.ft.ft_total_eth_supply_on_aurora(); sdk::log!(&format!("Total ETH supply on Aurora: {}", total_supply)); - sdk::return_output(format!("\"{}\"", total_supply.to_string()).as_bytes()); + self.io + .return_output(format!("\"{}\"", total_supply.to_string()).as_bytes()); } /// Return balance of nETH (ETH on Near) - pub fn ft_balance_of(&self) { + pub fn ft_balance_of(&mut self) { let args = BalanceOfCallArgs::from( - parse_json(&sdk::read_input()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), + parse_json(&self.io.read_input().to_vec()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), ); let balance = self.ft.ft_balance_of(&args.account_id); @@ -431,13 +435,13 @@ impl EthConnectorContract { args.account_id, balance )); - sdk::return_output(format!("\"{}\"", balance.to_string()).as_bytes()); + self.io + .return_output(format!("\"{}\"", balance.to_string()).as_bytes()); } /// Return balance of ETH (ETH in Aurora EVM) - pub fn ft_balance_of_eth_on_aurora(&self) { - let args = - BalanceOfEthCallArgs::try_from_slice(&sdk::read_input()).expect(ERR_FAILED_PARSE); + pub fn ft_balance_of_eth_on_aurora(&mut self) { + let args: BalanceOfEthCallArgs = self.io.read_input().to_value().expect(ERR_FAILED_PARSE); let balance = self .ft .internal_unwrap_balance_of_eth_on_aurora(args.address); @@ -446,14 +450,15 @@ impl EthConnectorContract { hex::encode(args.address), balance )); - sdk::return_output(format!("\"{}\"", balance.to_string()).as_bytes()); + self.io + .return_output(format!("\"{}\"", balance.to_string()).as_bytes()); } /// Transfer between NEAR accounts pub fn ft_transfer(&mut self) { sdk::assert_one_yocto(); let args = TransferCallArgs::from( - parse_json(&sdk::read_input()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), + parse_json(&self.io.read_input().to_vec()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), ); self.ft .ft_transfer(&args.receiver_id, args.amount, &args.memo); @@ -470,7 +475,7 @@ impl EthConnectorContract { // Check if previous promise succeeded assert_eq!(sdk::promise_results_count(), 1); - let args = ResolveTransferCallArgs::try_from_slice(&sdk::read_input()).unwrap(); + let args: ResolveTransferCallArgs = self.io.read_input().to_value().unwrap(); let amount = self .ft .ft_resolve_transfer(&args.sender_id, &args.receiver_id, args.amount); @@ -480,7 +485,8 @@ impl EthConnectorContract { )); // `ft_resolve_transfer` can change `total_supply` so we should save the contract self.save_ft_contract(); - sdk::return_output(format!("\"{}\"", amount.to_string()).as_bytes()); + self.io + .return_output(format!("\"{}\"", amount.to_string()).as_bytes()); } /// FT transfer call from sender account (invoker account) to receiver @@ -520,37 +526,38 @@ impl EthConnectorContract { /// FT storage deposit logic pub fn storage_deposit(&mut self) { let args = StorageDepositCallArgs::from( - parse_json(&sdk::read_input()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), + parse_json(&self.io.read_input().to_vec()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), ); let res = self .ft .storage_deposit(args.account_id.as_ref(), args.registration_only); self.save_ft_contract(); - sdk::return_output(&res.to_json_bytes()); + self.io.return_output(&res.to_json_bytes()); } /// FT storage withdraw pub fn storage_withdraw(&mut self) { sdk::assert_one_yocto(); let args = StorageWithdrawCallArgs::from( - parse_json(&sdk::read_input()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), + parse_json(&self.io.read_input().to_vec()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), ); let res = self.ft.storage_withdraw(args.amount); self.save_ft_contract(); - sdk::return_output(&res.to_json_bytes()); + self.io.return_output(&res.to_json_bytes()); } /// Get balance of storage - pub fn storage_balance_of(&self) { + pub fn storage_balance_of(&mut self) { let args = StorageBalanceOfCallArgs::from( - parse_json(&sdk::read_input()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), + parse_json(&self.io.read_input().to_vec()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), ); - sdk::return_output(&self.ft.storage_balance_of(&args.account_id).to_json_bytes()); + self.io + .return_output(&self.ft.storage_balance_of(&args.account_id).to_json_bytes()); } /// ft_on_transfer callback function - pub fn ft_on_transfer(&mut self, engine: &Engine, args: &NEP141FtOnTransferArgs) { + pub fn ft_on_transfer(&mut self, engine: &Engine, args: &NEP141FtOnTransferArgs) { sdk::log!("Call ft_on_transfer"); // Parse message with specific rules let message_data = self.parse_on_transfer_message(&args.msg); @@ -567,18 +574,19 @@ impl EthConnectorContract { _ => self.mint_eth_on_aurora(message_data.recipient, args.amount), } self.save_ft_contract(); - sdk::return_output("\"0\"".as_bytes()); + self.io.return_output("\"0\"".as_bytes()); } /// Get accounts counter for statistics. /// It represents total unique accounts (all-time, including accounts which now have zero balance). - pub fn get_accounts_counter(&self) { - sdk::return_output(&self.ft.get_accounts_counter().to_le_bytes()); + pub fn get_accounts_counter(&mut self) { + self.io + .return_output(&self.ft.get_accounts_counter().to_le_bytes()); } /// Save eth-connector contract data fn save_ft_contract(&mut self) { - sdk::save_contract( + self.io.write_borsh( &Self::get_contract_key(&EthConnectorStorageId::FungibleToken), &self.ft, ); @@ -592,13 +600,13 @@ impl EthConnectorContract { } /// Save already used event proof as hash key - fn save_used_event(&self, key: &str) { - sdk::save_contract(&self.used_event_key(key), &0u8); + fn save_used_event(&mut self, key: &str) { + self.io.write_borsh(&self.used_event_key(key), &0u8); } /// Check is event of proof already used fn check_used_event(&self, key: &str) -> bool { - sdk::storage_has_key(&self.used_event_key(key)) + self.io.storage_has_key(&self.used_event_key(key)) } /// Checks whether the provided proof was already used @@ -617,22 +625,22 @@ impl EthConnectorContract { } /// Return metdata - pub fn get_metadata() -> Option { - sdk::read_storage(&Self::get_contract_key( + pub fn get_metadata(io: &I) -> Option { + io.read_storage(&Self::get_contract_key( &EthConnectorStorageId::FungibleTokenMetadata, )) - .and_then(|data| FungibleTokenMetadata::try_from_slice(&data).ok()) + .and_then(|data| data.to_value().ok()) } } -impl AdminControlled for EthConnectorContract { +impl AdminControlled for EthConnectorContract { fn get_paused(&self) -> PausedMask { self.paused_mask } fn set_paused(&mut self, paused_mask: PausedMask) { self.paused_mask = paused_mask; - sdk::save_contract( + self.io.write_borsh( &Self::get_contract_key(&EthConnectorStorageId::PausedMask), &self.paused_mask, ); diff --git a/engine/src/engine.rs b/engine/src/engine.rs index 0c2ea07ee..c9f8c11d7 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -15,6 +15,8 @@ use crate::prelude::{ Address, BorshDeserialize, BorshSerialize, KeyPrefix, KeyPrefixU8, PromiseCreateArgs, TryInto, Vec, Wei, ERC20_MINT_SELECTOR, H256, U256, }; +use aurora_engine_sdk::io::{StorageIntermediate, IO}; +use aurora_engine_sdk::near_runtime::Runtime; use crate::parameters::{NewCallArgs, TransactionStatus}; use crate::prelude::precompiles::native::{ExitToEthereum, ExitToNear}; @@ -33,13 +35,13 @@ pub fn current_address() -> Address { } macro_rules! unwrap_res_or_finish { - ($e:expr, $output:expr) => { + ($e:expr, $output:expr, $io:expr) => { match $e { Ok(v) => v, Err(_e) => { #[cfg(feature = "log")] sdk::log(crate::prelude::format!("{:?}", _e).as_str()); - sdk::return_output($output); + $io.return_output($output); return; } } @@ -47,9 +49,9 @@ macro_rules! unwrap_res_or_finish { } macro_rules! assert_or_finish { - ($e:expr, $output:expr) => { + ($e:expr, $output:expr, $io:expr) => { if !$e { - sdk::return_output($output); + $io.return_output($output); return; } }; @@ -270,10 +272,10 @@ impl StackExecutorParams { } } - fn make_executor<'a>( + fn make_executor<'a, I: IO + Default + Copy>( &'a self, - engine: &'a Engine, - ) -> executor::StackExecutor<'static, 'a, executor::MemoryStackState> { + engine: &'a Engine, + ) -> executor::StackExecutor<'static, 'a, executor::MemoryStackState>> { let metadata = executor::StackSubstateMetadata::new(self.gas_limit, CONFIG); let state = executor::MemoryStackState::new(metadata, engine); executor::StackExecutor::new_with_precompile(state, CONFIG, &self.precompiles.0) @@ -295,7 +297,8 @@ pub struct EngineState { /// How many blocks after staging upgrade can deploy it. pub upgrade_delay_blocks: u64, /// Mapping between relayer account id and relayer evm address - pub relayers_evm_addresses: LookupMap<{ KeyPrefix::RelayerEvmAddressMap as KeyPrefixU8 }>, + pub relayers_evm_addresses: + LookupMap, } impl From for EngineState { @@ -305,14 +308,15 @@ impl From for EngineState { owner_id: args.owner_id, bridge_prover_id: args.bridge_prover_id, upgrade_delay_blocks: args.upgrade_delay_blocks, - relayers_evm_addresses: LookupMap::new(), + relayers_evm_addresses: LookupMap::default(), } } } -pub struct Engine { +pub struct Engine { state: EngineState, origin: Address, + io: I, } // TODO: upgrade to Berlin HF @@ -321,59 +325,25 @@ pub(crate) const CONFIG: &Config = &Config::istanbul(); /// Key for storing the state of the engine. const STATE_KEY: &[u8; 5] = b"STATE"; -impl Engine { - pub fn new(origin: Address) -> Result { - Engine::get_state().map(|state| Self::new_with_state(state, origin)) +impl Engine { + pub fn new(origin: Address, io: I) -> Result { + Engine::get_state(&io).map(|state| Self::new_with_state(state, origin, io)) } - pub fn new_with_state(state: EngineState, origin: Address) -> Self { - Self { state, origin } + pub fn new_with_state(state: EngineState, origin: Address, io: I) -> Self { + Self { state, origin, io } } /// Saves state into the storage. - pub fn set_state(state: EngineState) { - sdk::write_storage( + pub fn set_state(io: &mut I, state: EngineState) { + io.write_storage( &bytes_to_key(KeyPrefix::Config, STATE_KEY), &state.try_to_vec().expect("ERR_SER"), ); } - /// There is one Aurora block per NEAR block height (note: when heights in NEAR are skipped - /// they are interpreted as empty blocks on Aurora). The blockhash is derived from the height - /// according to - /// ```text - /// block_hash = sha256(concat( - /// BLOCK_HASH_PREFIX, - /// block_height as u64, - /// chain_id, - /// engine_account_id, - /// )) - /// ``` - pub fn compute_block_hash(chain_id: [u8; 32], block_height: u64, account_id: &[u8]) -> H256 { - debug_assert_eq!(BLOCK_HASH_PREFIX_SIZE, mem::size_of_val(&BLOCK_HASH_PREFIX)); - debug_assert_eq!(BLOCK_HEIGHT_SIZE, mem::size_of_val(&block_height)); - debug_assert_eq!(CHAIN_ID_SIZE, mem::size_of_val(&chain_id)); - let mut data = Vec::with_capacity( - BLOCK_HASH_PREFIX_SIZE + BLOCK_HEIGHT_SIZE + CHAIN_ID_SIZE + account_id.len(), - ); - data.push(BLOCK_HASH_PREFIX); - data.extend_from_slice(&chain_id); - data.extend_from_slice(account_id); - data.extend_from_slice(&block_height.to_be_bytes()); - - #[cfg(not(feature = "contract"))] - { - use sha2::Digest; - - let output = sha2::Sha256::digest(&data); - H256(output.into()) - } - - #[cfg(feature = "contract")] - sdk::sha256(&data) - } - pub fn charge_gas_limit( + io: &mut I, sender: &Address, gas_limit: U256, gas_price: U256, @@ -388,17 +358,18 @@ impl Engine { .checked_mul(gas_price) .ok_or(GasPaymentError::EthAmountOverflow)?, ); - let account_balance = Self::get_balance(sender); + let account_balance = Self::get_balance(io, sender); let remaining_balance = account_balance .checked_sub(payment_for_gas) .ok_or(GasPaymentError::OutOfFund)?; - Self::set_balance(sender, &remaining_balance); + Self::set_balance(io, sender, &remaining_balance); Ok(payment_for_gas) } pub fn refund_unused_gas( + io: &mut I, sender: &Address, relayer: &Address, prepaid_amount: Wei, @@ -419,53 +390,60 @@ impl Engine { debug_assert!(used_amount <= prepaid_amount); let refund_amount = prepaid_amount - used_amount; - Self::add_balance(sender, refund_amount)?; - Self::add_balance(relayer, used_amount)?; + Self::add_balance(io, sender, refund_amount)?; + Self::add_balance(io, relayer, used_amount)?; Ok(()) } /// Fails if state is not found. - pub fn get_state() -> Result { - match sdk::read_storage(&bytes_to_key(KeyPrefix::Config, STATE_KEY)) { + pub fn get_state(io: &I) -> Result { + match io.read_storage(&bytes_to_key(KeyPrefix::Config, STATE_KEY)) { None => Err(EngineStateError::NotFound), - Some(bytes) => EngineState::try_from_slice(&bytes) + Some(bytes) => EngineState::try_from_slice(&bytes.to_vec()) .map_err(|_| EngineStateError::DeserializationFailed), } } - pub fn set_code(address: &Address, code: &[u8]) { - sdk::write_storage(&address_to_key(KeyPrefix::Code, address), code); + pub fn set_code(io: &mut I, address: &Address, code: &[u8]) { + io.write_storage(&address_to_key(KeyPrefix::Code, address), code); } - pub fn remove_code(address: &Address) { - sdk::remove_storage(&address_to_key(KeyPrefix::Code, address)) + pub fn remove_code(io: &mut I, address: &Address) { + io.remove_storage(&address_to_key(KeyPrefix::Code, address)); } - pub fn get_code(address: &Address) -> Vec { - sdk::read_storage(&address_to_key(KeyPrefix::Code, address)).unwrap_or_else(Vec::new) + pub fn get_code(io: &I, address: &Address) -> Vec { + io.read_storage(&address_to_key(KeyPrefix::Code, address)) + .map(|s| s.to_vec()) + .unwrap_or_else(Vec::new) } - pub fn get_code_size(address: &Address) -> usize { - sdk::read_storage_len(&address_to_key(KeyPrefix::Code, address)).unwrap_or(0) + pub fn get_code_size(io: &I, address: &Address) -> usize { + io.read_storage_len(&address_to_key(KeyPrefix::Code, address)) + .unwrap_or(0) } - pub fn set_nonce(address: &Address, nonce: &U256) { - sdk::write_storage( + pub fn set_nonce(io: &mut I, address: &Address, nonce: &U256) { + io.write_storage( &address_to_key(KeyPrefix::Nonce, address), &u256_to_arr(nonce), ); } - pub fn remove_nonce(address: &Address) { - sdk::remove_storage(&address_to_key(KeyPrefix::Nonce, address)) + 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(address: &Address, transaction_nonce: &U256) -> Result<(), EngineErrorKind> { - let account_nonce = Self::get_nonce(address); + pub fn check_nonce( + io: &I, + address: &Address, + transaction_nonce: &U256, + ) -> Result<(), EngineErrorKind> { + let account_nonce = Self::get_nonce(io, address); if transaction_nonce != &account_nonce { return Err(EngineErrorKind::IncorrectNonce); @@ -474,81 +452,88 @@ impl Engine { Ok(()) } - pub fn get_nonce(address: &Address) -> U256 { - sdk::read_storage(&address_to_key(KeyPrefix::Nonce, address)) - .map(|value| U256::from_big_endian(&value)) - .unwrap_or_else(U256::zero) + pub fn get_nonce(io: &I, address: &Address) -> U256 { + io.read_u256(&address_to_key(KeyPrefix::Nonce, address)) + .unwrap_or_else(|_| U256::zero()) } - pub fn add_balance(address: &Address, amount: Wei) -> Result<(), BalanceOverflow> { - let current_balance = Self::get_balance(address); + pub fn add_balance(io: &mut I, address: &Address, amount: Wei) -> Result<(), BalanceOverflow> { + let current_balance = Self::get_balance(io, address); let new_balance = current_balance.checked_add(amount).ok_or(BalanceOverflow)?; - Self::set_balance(address, &new_balance); + Self::set_balance(io, address, &new_balance); Ok(()) } - pub fn set_balance(address: &Address, balance: &Wei) { - sdk::write_storage( + pub fn set_balance(io: &mut I, address: &Address, balance: &Wei) { + io.write_storage( &address_to_key(KeyPrefix::Balance, address), &balance.to_bytes(), ); } - pub fn remove_balance(address: &Address) { - let balance = Self::get_balance(address); + pub fn remove_balance(io: &mut I, address: &Address) { + let balance = Self::get_balance(io, address); // Apply changes for eth-conenctor - EthConnectorContract::get_instance().internal_remove_eth(address, &balance.raw()); - sdk::remove_storage(&address_to_key(KeyPrefix::Balance, address)) + EthConnectorContract::get_instance(*io).internal_remove_eth(address, &balance.raw()); + io.remove_storage(&address_to_key(KeyPrefix::Balance, address)); } - pub fn get_balance(address: &Address) -> Wei { - let raw = sdk::read_storage(&address_to_key(KeyPrefix::Balance, address)) - .map(|value| U256::from_big_endian(&value)) - .unwrap_or_else(U256::zero); + pub fn get_balance(io: &I, address: &Address) -> Wei { + let raw = io + .read_u256(&address_to_key(KeyPrefix::Balance, address)) + .unwrap_or_else(|_| U256::zero()); Wei::new(raw) } - pub fn remove_storage(address: &Address, key: &H256, generation: u32) { - sdk::remove_storage(storage_to_key(address, key, generation).as_ref()); + pub fn remove_storage(io: &mut I, address: &Address, key: &H256, generation: u32) { + io.remove_storage(storage_to_key(address, key, generation).as_ref()); } - pub fn set_storage(address: &Address, key: &H256, value: &H256, generation: u32) { - sdk::write_storage(storage_to_key(address, key, generation).as_ref(), &value.0); + pub fn set_storage(io: &mut I, address: &Address, key: &H256, value: &H256, generation: u32) { + io.write_storage(storage_to_key(address, key, generation).as_ref(), &value.0); } - pub fn get_storage(address: &Address, key: &H256, generation: u32) -> H256 { - sdk::read_storage(storage_to_key(address, key, generation).as_ref()) - .map(|value| H256::from_slice(&value)) + pub fn get_storage(io: &I, address: &Address, key: &H256, generation: u32) -> H256 { + io.read_storage(storage_to_key(address, key, generation).as_ref()) + .and_then(|value| { + if value.len() == 32 { + let mut buf = [0u8; 32]; + value.copy_to_slice(&mut buf); + Some(H256(buf)) + } else { + None + } + }) .unwrap_or_else(H256::default) } - pub fn is_account_empty(address: &Address) -> bool { - let balance = Self::get_balance(address); - let nonce = Self::get_nonce(address); - let code_len = Self::get_code_size(address); + pub fn is_account_empty(io: &I, address: &Address) -> bool { + let balance = Self::get_balance(io, address); + let nonce = Self::get_nonce(io, address); + let code_len = Self::get_code_size(io, address); balance.is_zero() && nonce.is_zero() && code_len == 0 } /// Increments storage generation for a given address. - pub fn set_generation(address: &Address, generation: u32) { - sdk::write_storage( + pub fn set_generation(io: &mut I, address: &Address, generation: u32) { + io.write_storage( &address_to_key(KeyPrefix::Generation, address), &generation.to_be_bytes(), ); } - pub fn get_generation(address: &Address) -> u32 { - sdk::read_storage(&address_to_key(KeyPrefix::Generation, address)) + pub fn get_generation(io: &I, address: &Address) -> u32 { + io.read_storage(&address_to_key(KeyPrefix::Generation, address)) .map(|value| { let mut bytes = [0u8; 4]; - bytes[0..4].copy_from_slice(&value[0..4]); + value.copy_to_slice(&mut bytes); u32::from_be_bytes(bytes) }) .unwrap_or(0) } /// Removes all storage for the given address. - fn remove_all_storage(address: &Address, generation: u32) { + fn remove_all_storage(io: &mut I, address: &Address, generation: u32) { // FIXME: there is presently no way to prefix delete trie state. // NOTE: There is not going to be a method on runtime for this. // You may need to store all keys in a list if you want to do this in a contract. @@ -557,15 +542,15 @@ impl Engine { // Either way you may have to store the nonce per storage address root. When the account // has to be deleted the storage nonce needs to be increased, and the old nonce keys // can be deleted over time. That's how TurboGeth does storage. - Self::set_generation(address, generation + 1); + Self::set_generation(io, address, generation + 1); } /// Removes an account. - fn remove_account(address: &Address, generation: u32) { - Self::remove_nonce(address); - Self::remove_balance(address); - Self::remove_code(address); - Self::remove_all_storage(address, generation); + fn remove_account(io: &mut I, address: &Address, generation: u32) { + Self::remove_nonce(io, address); + Self::remove_balance(io, address); + Self::remove_code(io, address); + Self::remove_all_storage(io, address, generation); } pub fn deploy_code_with_input(&mut self, input: Vec) -> EngineResult { @@ -594,7 +579,7 @@ impl Engine { let status = match exit_reason.into_result(result.0.to_vec()) { Ok(status) => status, Err(e) => { - Engine::increment_nonce(&origin); + Engine::increment_nonce(&mut self.io, &origin); return Err(e.with_gas_used(used_gas)); } }; @@ -632,7 +617,7 @@ impl Engine { let status = match exit_reason.into_result(result) { Ok(status) => status, Err(e) => { - Engine::increment_nonce(&origin); + Engine::increment_nonce(&mut self.io, &origin); return Err(e.with_gas_used(used_gas)); } }; @@ -647,10 +632,10 @@ impl Engine { Ok(SubmitResult::new(status, used_gas, logs)) } - pub fn increment_nonce(address: &Address) { - let account_nonce = Self::get_nonce(address); + pub fn increment_nonce(io: &mut I, address: &Address) { + let account_nonce = Self::get_nonce(io, address); let new_nonce = account_nonce.saturating_add(U256::one()); - Self::set_nonce(address, &new_nonce); + Self::set_nonce(io, address, &new_nonce); } pub fn view_with_args(&self, args: ViewCallArgs) -> Result { @@ -694,7 +679,7 @@ impl Engine { erc20_token: &[u8], nep141_token: &[u8], ) -> Result<(), RegisterTokenError> { - match Self::get_erc20_from_nep141(nep141_token) { + match Self::get_erc20_from_nep141(self.io, nep141_token) { Err(GetErc20FromNep141Error::Nep141NotFound) => (), Err(GetErc20FromNep141Error::InvalidNep141AccountId) => { return Err(RegisterTokenError::InvalidNep141AccountId); @@ -702,18 +687,19 @@ impl Engine { Ok(_) => return Err(RegisterTokenError::TokenAlreadyRegistered), } - Self::nep141_erc20_map().insert(nep141_token, erc20_token); + Self::nep141_erc20_map(self.io).insert(nep141_token, erc20_token); Ok(()) } pub fn get_erc20_from_nep141( + io: I, nep141_account_id: &[u8], ) -> Result, GetErc20FromNep141Error> { if !is_valid_account_id(nep141_account_id) { return Err(GetErc20FromNep141Error::InvalidNep141AccountId); } - Self::nep141_erc20_map() + Self::nep141_erc20_map(io) .lookup_left(nep141_account_id) .ok_or(GetErc20FromNep141Error::Nep141NotFound) } @@ -753,20 +739,21 @@ impl Engine { // Recipient of the transaction - 40 characters (Address in hex) // Fee to be paid in ETH (Optional) - 64 characters (Encoded in big endian / hex) let mut message = args.msg.as_bytes(); - assert_or_finish!(message.len() >= 40, output_on_fail); + assert_or_finish!(message.len() >= 40, output_on_fail, self.io); let recipient = Address(unwrap_res_or_finish!( hex::decode(&message[..40]).unwrap().as_slice().try_into(), - output_on_fail + output_on_fail, + self.io )); message = &message[40..]; let fee = if message.is_empty() { U256::from(0) } else { - assert_or_finish!(message.len() == 64, output_on_fail); + assert_or_finish!(message.len() == 64, output_on_fail, self.io); U256::from_big_endian( - unwrap_res_or_finish!(hex::decode(message), output_on_fail).as_slice(), + unwrap_res_or_finish!(hex::decode(message), output_on_fail, self.io).as_slice(), ) }; @@ -775,17 +762,23 @@ impl Engine { let token = sdk::predecessor_account_id(); let erc20_token = Address(unwrap_res_or_finish!( - unwrap_res_or_finish!(Self::get_erc20_from_nep141(&token), output_on_fail) - .as_slice() - .try_into(), - output_on_fail + unwrap_res_or_finish!( + Self::get_erc20_from_nep141(self.io, &token), + output_on_fail, + self.io + ) + .as_slice() + .try_into(), + output_on_fail, + self.io )); if fee != U256::from(0) { let relayer_account_id = sdk::signer_account_id(); let relayer_address = unwrap_res_or_finish!( self.get_relayer(relayer_account_id.as_slice()).ok_or(()), - output_on_fail + output_on_fail, + self.io ); unwrap_res_or_finish!( @@ -795,7 +788,8 @@ impl Engine { Wei::new_u64(fee.as_u64()), u64::MAX, ), - output_on_fail + output_on_fail, + self.io ); } @@ -848,19 +842,23 @@ impl Engine { }), } }), - output_on_fail + output_on_fail, + self.io ); // TODO(marX) // Everything succeed so return "0" - sdk::return_output(b"\"0\""); + self.io.return_output(b"\"0\""); } - pub fn nep141_erc20_map() -> BijectionMap< + pub fn nep141_erc20_map( + io: I, + ) -> BijectionMap< + I, { KeyPrefix::Nep141Erc20Map as KeyPrefixU8 }, { KeyPrefix::Erc20Nep141Map as KeyPrefixU8 }, > { - Default::default() + BijectionMap { io } } fn filter_promises_from_logs>(logs: T) -> Vec { @@ -906,7 +904,42 @@ impl Engine { } } -impl evm::backend::Backend for Engine { +/// There is one Aurora block per NEAR block height (note: when heights in NEAR are skipped +/// they are interpreted as empty blocks on Aurora). The blockhash is derived from the height +/// according to +/// ```text +/// block_hash = sha256(concat( +/// BLOCK_HASH_PREFIX, +/// block_height as u64, +/// chain_id, +/// engine_account_id, +/// )) +/// ``` +pub fn compute_block_hash(chain_id: [u8; 32], block_height: u64, account_id: &[u8]) -> H256 { + debug_assert_eq!(BLOCK_HASH_PREFIX_SIZE, mem::size_of_val(&BLOCK_HASH_PREFIX)); + debug_assert_eq!(BLOCK_HEIGHT_SIZE, mem::size_of_val(&block_height)); + debug_assert_eq!(CHAIN_ID_SIZE, mem::size_of_val(&chain_id)); + let mut data = Vec::with_capacity( + BLOCK_HASH_PREFIX_SIZE + BLOCK_HEIGHT_SIZE + CHAIN_ID_SIZE + account_id.len(), + ); + data.push(BLOCK_HASH_PREFIX); + data.extend_from_slice(&chain_id); + data.extend_from_slice(account_id); + data.extend_from_slice(&block_height.to_be_bytes()); + + #[cfg(not(feature = "contract"))] + { + use sha2::Digest; + + let output = sha2::Sha256::digest(&data); + H256(output.into()) + } + + #[cfg(feature = "contract")] + sdk::sha256(&data) +} + +impl evm::backend::Backend for Engine { /// Returns the gas price. /// /// This is currently zero, but may be changed in the future. This is mainly @@ -944,11 +977,11 @@ impl evm::backend::Backend for Engine { #[cfg(feature = "contract")] { let account_id = sdk::current_account_id(); - Self::compute_block_hash(self.state.chain_id, number.low_u64(), &account_id) + compute_block_hash(self.state.chain_id, number.low_u64(), &account_id) } #[cfg(not(feature = "contract"))] - Self::compute_block_hash(self.state.chain_id, number.low_u64(), b"aurora") + compute_block_hash(self.state.chain_id, number.low_u64(), b"aurora") } else { H256::zero() } @@ -1000,26 +1033,26 @@ impl evm::backend::Backend for Engine { /// Checks if an address exists. fn exists(&self, address: Address) -> bool { - !Engine::is_account_empty(&address) + !Engine::is_account_empty(&self.io, &address) } /// Returns basic account information. fn basic(&self, address: Address) -> Basic { Basic { - nonce: Engine::get_nonce(&address), - balance: Engine::get_balance(&address).raw(), + nonce: Engine::get_nonce(&self.io, &address), + balance: Engine::get_balance(&self.io, &address).raw(), } } /// Returns the code of the contract from an address. fn code(&self, address: Address) -> Vec { - Engine::get_code(&address) + Engine::get_code(&self.io, &address) } /// Get storage value of address at index. fn storage(&self, address: Address, index: H256) -> H256 { - let generation = Self::get_generation(&address); - Engine::get_storage(&address, &index, generation) + let generation = Self::get_generation(&self.io, &address); + Engine::get_storage(&self.io, &address, &index, generation) } /// Get original storage value of address at index, if available. @@ -1030,7 +1063,7 @@ impl evm::backend::Backend for Engine { } } -impl ApplyBackend for Engine { +impl ApplyBackend for Engine { fn apply(&mut self, values: A, _logs: L, delete_empty: bool) where A: IntoIterator>, @@ -1046,16 +1079,16 @@ impl ApplyBackend for Engine { storage, reset_storage, } => { - let generation = Self::get_generation(&address); - Engine::set_nonce(&address, &basic.nonce); - Engine::set_balance(&address, &Wei::new(basic.balance)); + let generation = Self::get_generation(&self.io, &address); + Engine::set_nonce(&mut self.io, &address, &basic.nonce); + Engine::set_balance(&mut self.io, &address, &Wei::new(basic.balance)); if let Some(code) = code { - Engine::set_code(&address, &code) + Engine::set_code(&mut self.io, &address, &code) } let next_generation = if reset_storage { - Engine::remove_all_storage(&address, generation); + Engine::remove_all_storage(&mut self.io, &address, generation); generation + 1 } else { generation @@ -1063,9 +1096,15 @@ impl ApplyBackend for Engine { for (index, value) in storage { if value == H256::default() { - Engine::remove_storage(&address, &index, next_generation) + Engine::remove_storage(&mut self.io, &address, &index, next_generation) } else { - Engine::set_storage(&address, &index, &value, next_generation) + Engine::set_storage( + &mut self.io, + &address, + &index, + &value, + next_generation, + ) } } @@ -1075,15 +1114,15 @@ impl ApplyBackend for Engine { // 3. we didn't already clear out the storage (because if we did then there is // nothing to do) if delete_empty - && Engine::is_account_empty(&address) + && Engine::is_account_empty(&self.io, &address) && generation == next_generation { - Engine::remove_account(&address, generation); + Engine::remove_account(&mut self.io, &address, generation); } } Apply::Delete { address } => { - let generation = Self::get_generation(&address); - Engine::remove_account(&address, generation); + let generation = Self::get_generation(&self.io, &address); + Engine::remove_account(&mut self.io, &address, generation); } } } diff --git a/engine/src/fungible_token.rs b/engine/src/fungible_token.rs index a90c8cb3f..a200c7b9c 100644 --- a/engine/src/fungible_token.rs +++ b/engine/src/fungible_token.rs @@ -7,12 +7,13 @@ use crate::prelude::{ BorshSerialize, EthAddress, Gas, PromiseResult, StorageBalanceBounds, StorageUsage, String, ToString, TryInto, Vec, Wei, U256, }; +use aurora_engine_sdk::io::{StorageIntermediate, IO}; const GAS_FOR_RESOLVE_TRANSFER: Gas = 5_000_000_000_000; const GAS_FOR_FT_ON_TRANSFER: Gas = 10_000_000_000_000; #[derive(Debug, Default, BorshDeserialize, BorshSerialize)] -pub struct FungibleToken { +pub struct FungibleToken { /// Total ETH supply on Near (nETH as NEP-141 token) pub total_eth_supply_on_near: Balance, @@ -21,6 +22,9 @@ pub struct FungibleToken { /// The storage size in bytes for one account. pub account_storage_usage: StorageUsage, + + #[borsh_skip] + io: I, } #[derive(BorshDeserialize, BorshSerialize, Clone)] @@ -84,7 +88,7 @@ impl From for JsonValue { } } -impl FungibleToken { +impl FungibleToken { pub fn new() -> Self { Self::default() } @@ -99,7 +103,9 @@ impl FungibleToken { /// Balance of ETH (ETH on Aurora) pub fn internal_unwrap_balance_of_eth_on_aurora(&self, address: EthAddress) -> Balance { - Engine::get_balance(&Address(address)).raw().as_u128() + Engine::get_balance(&self.io, &Address(address)) + .raw() + .as_u128() } /// Internal ETH deposit to NEAR - nETH (NEP-141) @@ -120,7 +126,11 @@ impl FungibleToken { pub fn internal_deposit_eth_to_aurora(&mut self, address: EthAddress, amount: Balance) { let balance = self.internal_unwrap_balance_of_eth_on_aurora(address); if let Some(new_balance) = balance.checked_add(amount) { - Engine::set_balance(&Address(address), &Wei::new(U256::from(new_balance))); + Engine::set_balance( + &mut self.io, + &Address(address), + &Wei::new(U256::from(new_balance)), + ); self.total_eth_supply_on_aurora = self .total_eth_supply_on_aurora .checked_add(amount) @@ -148,7 +158,11 @@ impl FungibleToken { pub fn internal_withdraw_eth_from_aurora(&mut self, address: EthAddress, amount: Balance) { let balance = self.internal_unwrap_balance_of_eth_on_aurora(address); if let Some(new_balance) = balance.checked_sub(amount) { - Engine::set_balance(&Address(address), &Wei::new(U256::from(new_balance))); + Engine::set_balance( + &mut self.io, + &Address(address), + &Wei::new(U256::from(new_balance)), + ); self.total_eth_supply_on_aurora = self .total_eth_supply_on_aurora .checked_sub(amount) @@ -448,34 +462,39 @@ impl FungibleToken { /// Insert account. /// Calculate total unique accounts - pub fn accounts_insert(&self, account_id: &str, amount: Balance) { + pub fn accounts_insert(&mut self, account_id: &str, amount: Balance) { if !self.accounts_contains_key(account_id) { let key = Self::get_statistic_key(); - let accounts_counter = sdk::read_u64(&key) + let accounts_counter = self + .io + .read_u64(&key) .unwrap_or(0) .checked_add(1) .expect("ERR_ACCOUNTS_COUNTER_OVERFLOW"); - sdk::write_storage(&key, &accounts_counter.to_le_bytes()); + self.io.write_storage(&key, &accounts_counter.to_le_bytes()); } - sdk::save_contract(&Self::account_to_key(account_id), &amount); + self.io + .write_borsh(&Self::account_to_key(account_id), &amount); } /// Get accounts counter for statistics /// It represents total unique accounts. pub fn get_accounts_counter(&self) -> u64 { - sdk::read_u64(&Self::get_statistic_key()).unwrap_or(0) + self.io.read_u64(&Self::get_statistic_key()).unwrap_or(0) } fn accounts_contains_key(&self, account_id: &str) -> bool { - sdk::storage_has_key(&Self::account_to_key(account_id)) + self.io.storage_has_key(&Self::account_to_key(account_id)) } - fn accounts_remove(&self, account_id: &str) { - sdk::remove_storage(&Self::account_to_key(account_id)) + fn accounts_remove(&mut self, account_id: &str) { + self.io.remove_storage(&Self::account_to_key(account_id)); } pub fn accounts_get(&self, account_id: &str) -> Option> { - sdk::read_storage(&Self::account_to_key(account_id)) + self.io + .read_storage(&Self::account_to_key(account_id)) + .map(|s| s.to_vec()) } /// Fungible token key diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 3167e9eba..392c7e9c2 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -64,7 +64,7 @@ pub unsafe fn on_alloc_error(_: core::alloc::Layout) -> ! { #[cfg(feature = "contract")] mod contract { - use borsh::{BorshDeserialize, BorshSerialize}; + use borsh::BorshSerialize; use crate::connector::EthConnectorContract; use crate::engine::{Engine, EngineState, GasPaymentError}; @@ -77,6 +77,8 @@ mod contract { PauseEthConnectorCallArgs, SetContractDataCallArgs, SubmitResult, TransactionStatus, TransferCallCallArgs, ViewCallArgs, }; + use aurora_engine_sdk::io::{StorageIntermediate, IO}; + use aurora_engine_sdk::near_runtime::Runtime; use crate::json::parse_json; use crate::prelude::sdk::types::{ @@ -98,58 +100,65 @@ mod contract { /// Should be called on deployment. #[no_mangle] pub extern "C" fn new() { - if let Ok(state) = Engine::get_state() { + let mut io = Runtime; + if let Ok(state) = Engine::get_state(&io) { require_owner_only(&state); } - let args: NewCallArgs = sdk::read_input_borsh().sdk_unwrap(); - Engine::set_state(args.into()); + let args: NewCallArgs = io.read_input_borsh().sdk_unwrap(); + Engine::set_state(&mut io, args.into()); } /// Get version of the contract. #[no_mangle] pub extern "C" fn get_version() { + let mut io = Runtime; let version = match option_env!("NEAR_EVM_VERSION") { Some(v) => v.as_bytes(), None => include_bytes!("../../VERSION"), }; - sdk::return_output(version) + io.return_output(version) } /// Get owner account id for this contract. #[no_mangle] pub extern "C" fn get_owner() { - let state = Engine::get_state().sdk_unwrap(); - sdk::return_output(state.owner_id.as_bytes()); + let mut io = Runtime; + let state = Engine::get_state(&io).sdk_unwrap(); + io.return_output(state.owner_id.as_bytes()); } /// Get bridge prover id for this contract. #[no_mangle] pub extern "C" fn get_bridge_prover() { - let state = Engine::get_state().sdk_unwrap(); - sdk::return_output(state.bridge_prover_id.as_bytes()); + let mut io = Runtime; + let state = Engine::get_state(&io).sdk_unwrap(); + io.return_output(state.bridge_prover_id.as_bytes()); } /// Get chain id for this contract. #[no_mangle] pub extern "C" fn get_chain_id() { - sdk::return_output(&Engine::get_state().sdk_unwrap().chain_id) + let mut io = Runtime; + io.return_output(&Engine::get_state(&io).sdk_unwrap().chain_id) } #[no_mangle] pub extern "C" fn get_upgrade_index() { - let state = Engine::get_state().sdk_unwrap(); + let mut io = Runtime; + let state = Engine::get_state(&io).sdk_unwrap(); let index = internal_get_upgrade_index(); - sdk::return_output(&(index + state.upgrade_delay_blocks).to_le_bytes()) + io.return_output(&(index + state.upgrade_delay_blocks).to_le_bytes()) } /// Stage new code for deployment. #[no_mangle] pub extern "C" fn stage_upgrade() { - let state = Engine::get_state().sdk_unwrap(); + let mut io = Runtime; + let state = Engine::get_state(&io).sdk_unwrap(); require_owner_only(&state); - sdk::read_input_and_store(&bytes_to_key(KeyPrefix::Config, CODE_KEY)); - sdk::write_storage( + io.read_input_and_store(&bytes_to_key(KeyPrefix::Config, CODE_KEY)); + io.write_storage( &bytes_to_key(KeyPrefix::Config, CODE_STAGE_KEY), &sdk::block_index().to_le_bytes(), ); @@ -158,7 +167,8 @@ mod contract { /// Deploy staged upgrade. #[no_mangle] pub extern "C" fn deploy_upgrade() { - let state = Engine::get_state().sdk_unwrap(); + let io = Runtime; + let state = Engine::get_state(&io).sdk_unwrap(); let index = internal_get_upgrade_index(); if sdk::block_index() <= index + state.upgrade_delay_blocks { sdk::panic_utf8(b"ERR_NOT_ALLOWED:TOO_EARLY"); @@ -181,8 +191,9 @@ mod contract { /// Deploy code into the EVM. #[no_mangle] pub extern "C" fn deploy_code() { - let input = sdk::read_input(); - let mut engine = Engine::new(predecessor_address()).sdk_unwrap(); + let io = Runtime; + let input = io.read_input().to_vec(); + let mut engine = Engine::new(predecessor_address(), io).sdk_unwrap(); Engine::deploy_code_with_input(&mut engine, input) .map(|res| res.try_to_vec().sdk_expect("ERR_SERIALIZE")) .sdk_process(); @@ -192,8 +203,9 @@ mod contract { /// Call method on the EVM contract. #[no_mangle] pub extern "C" fn call() { - let args: FunctionCallArgs = sdk::read_input_borsh().sdk_unwrap(); - let mut engine = Engine::new(predecessor_address()).sdk_unwrap(); + let io = Runtime; + let args: FunctionCallArgs = io.read_input_borsh().sdk_unwrap(); + let mut engine = Engine::new(predecessor_address(), io).sdk_unwrap(); Engine::call_with_args(&mut engine, args) .map(|res| res.try_to_vec().sdk_expect("ERR_SERIALIZE")) .sdk_process(); @@ -206,11 +218,12 @@ mod contract { pub extern "C" fn submit() { use crate::transaction::EthTransaction; - let input = sdk::read_input(); + let mut io = Runtime; + let input = io.read_input().to_vec(); let signed_transaction = EthTransaction::try_from(input.as_slice()).sdk_unwrap(); - let state = Engine::get_state().sdk_unwrap(); + let state = Engine::get_state(&io).sdk_unwrap(); // Validate the chain ID, if provided inside the signature: if let Some(chain_id) = signed_transaction.chain_id() { @@ -224,7 +237,7 @@ mod contract { .sender() .sdk_expect("ERR_INVALID_ECDSA_SIGNATURE"); - Engine::check_nonce(&sender, signed_transaction.nonce()).sdk_unwrap(); + Engine::check_nonce(&io, &sender, signed_transaction.nonce()).sdk_unwrap(); // Check intrinsic gas is covered by transaction gas limit match signed_transaction.intrinsic_gas(crate::engine::CONFIG) { @@ -238,29 +251,30 @@ mod contract { // Pay for gas let gas_price = signed_transaction.gas_price(); - let prepaid_amount = - match Engine::charge_gas_limit(&sender, signed_transaction.gas_limit(), gas_price) { - Ok(amount) => amount, - // If the account does not have enough funds to cover the gas cost then we still - // must increment the nonce to prevent the transaction from being replayed in the - // future when the state may have changed such that it could pass. - Err(GasPaymentError::OutOfFund) => { - Engine::increment_nonce(&sender); - let result = SubmitResult::new( - TransactionStatus::OutOfFund, - 0, - crate::prelude::Vec::new(), - ); - sdk::return_output(&result.try_to_vec().unwrap()); - return; - } - // If an overflow happens then the transaction is statically invalid - // (i.e. validity does not depend on state), so we do not need to increment the nonce. - Err(err) => sdk::panic_utf8(err.as_ref()), - }; + let prepaid_amount = match Engine::charge_gas_limit( + &mut io, + &sender, + signed_transaction.gas_limit(), + gas_price, + ) { + Ok(amount) => amount, + // If the account does not have enough funds to cover the gas cost then we still + // must increment the nonce to prevent the transaction from being replayed in the + // future when the state may have changed such that it could pass. + Err(GasPaymentError::OutOfFund) => { + Engine::increment_nonce(&mut io, &sender); + let result = + SubmitResult::new(TransactionStatus::OutOfFund, 0, crate::prelude::Vec::new()); + io.return_output(&result.try_to_vec().unwrap()); + return; + } + // If an overflow happens then the transaction is statically invalid + // (i.e. validity does not depend on state), so we do not need to increment the nonce. + Err(err) => sdk::panic_utf8(err.as_ref()), + }; // Figure out what kind of a transaction this is, and execute it: - let mut engine = Engine::new_with_state(state, sender); + let mut engine = Engine::new_with_state(state, sender, io); let (value, gas_limit, data, maybe_receiver, access_list) = signed_transaction.destructure(); let gas_limit = gas_limit.sdk_expect(GAS_OVERFLOW); @@ -291,8 +305,15 @@ mod contract { Ok(submit_result) => submit_result.gas_used, Err(engine_err) => engine_err.gas_used, }; - Engine::refund_unused_gas(&sender, &relayer, prepaid_amount, gas_used, gas_price) - .sdk_unwrap(); + Engine::refund_unused_gas( + &mut io, + &sender, + &relayer, + prepaid_amount, + gas_used, + gas_price, + ) + .sdk_unwrap(); // return result to user result @@ -303,8 +324,9 @@ mod contract { #[cfg(feature = "meta-call")] #[no_mangle] pub extern "C" fn meta_call() { - let input = sdk::read_input(); - let state = Engine::get_state().sdk_unwrap(); + let io = Runtime; + let input = io.read_input().to_vec(); + let state = Engine::get_state(&io).sdk_unwrap(); let domain_separator = crate::meta_parsing::near_erc712_domain(U256::from(state.chain_id)); let meta_call_args = crate::meta_parsing::parse_meta_call( &domain_separator, @@ -313,9 +335,9 @@ mod contract { ) .sdk_expect("ERR_META_TX_PARSE"); - Engine::check_nonce(&meta_call_args.sender, &meta_call_args.nonce).sdk_unwrap(); + Engine::check_nonce(&io, &meta_call_args.sender, &meta_call_args.nonce).sdk_unwrap(); - let mut engine = Engine::new_with_state(state, meta_call_args.sender); + let mut engine = Engine::new_with_state(state, meta_call_args.sender, io); let result = engine.call( meta_call_args.sender, meta_call_args.contract_address, @@ -331,9 +353,10 @@ mod contract { #[no_mangle] pub extern "C" fn register_relayer() { - let relayer_address = sdk::read_input_arr20().sdk_unwrap(); + let io = Runtime; + let relayer_address = io.read_input_arr20().sdk_unwrap(); - let mut engine = Engine::new(predecessor_address()).sdk_unwrap(); + let mut engine = Engine::new(predecessor_address(), io).sdk_unwrap(); engine.register_relayer( sdk::predecessor_account_id().as_slice(), Address(relayer_address), @@ -347,16 +370,17 @@ mod contract { /// error, or no token is returned if tx was successful. #[no_mangle] pub extern "C" fn ft_on_transfer() { - let mut engine = Engine::new(predecessor_address()).sdk_unwrap(); + let io = Runtime; + let mut engine = Engine::new(predecessor_address(), io).sdk_unwrap(); - let args: NEP141FtOnTransferArgs = parse_json(sdk::read_input().as_slice()) + let args: NEP141FtOnTransferArgs = parse_json(io.read_input().to_vec().as_slice()) .sdk_unwrap() .try_into() .sdk_unwrap(); if sdk::predecessor_account_id() == sdk::current_account_id() { - let engine = Engine::new(predecessor_address()).sdk_unwrap(); - EthConnectorContract::get_instance().ft_on_transfer(&engine, &args); + let engine = Engine::new(predecessor_address(), io).sdk_unwrap(); + EthConnectorContract::get_instance(io).ft_on_transfer(&engine, &args); } else { engine.receive_erc20_tokens(&args); } @@ -365,11 +389,11 @@ mod contract { /// Deploy ERC20 token mapped to a NEP141 #[no_mangle] pub extern "C" fn deploy_erc20_token() { + let mut io = Runtime; // Id of the NEP141 token in Near - let args: DeployErc20TokenArgs = - DeployErc20TokenArgs::try_from_slice(&sdk::read_input()).sdk_expect("ERR_ARG_PARSE"); + let args: DeployErc20TokenArgs = io.read_input_borsh().sdk_unwrap(); - let mut engine = Engine::new(predecessor_address()).sdk_unwrap(); + let mut engine = Engine::new(predecessor_address(), io).sdk_unwrap(); let erc20_admin_address = current_address(); let erc20_contract = include_bytes!("../../etc/eth-contracts/res/EvmErc20.bin"); @@ -396,7 +420,7 @@ mod contract { engine .register_token(address.as_bytes(), args.nep141.as_bytes()) .sdk_unwrap(); - sdk::return_output(&address.as_bytes().try_to_vec().sdk_expect("ERR_SERIALIZE")); + io.return_output(&address.as_bytes().try_to_vec().sdk_expect("ERR_SERIALIZE")); // TODO: charge for storage } @@ -406,49 +430,57 @@ mod contract { /// #[no_mangle] pub extern "C" fn view() { - let args: ViewCallArgs = sdk::read_input_borsh().sdk_unwrap(); - let engine = Engine::new(Address::from_slice(&args.sender)).sdk_unwrap(); + let mut io = Runtime; + let args: ViewCallArgs = io.read_input_borsh().sdk_unwrap(); + let engine = Engine::new(Address::from_slice(&args.sender), io).sdk_unwrap(); let result = Engine::view_with_args(&engine, args).sdk_unwrap(); - sdk::return_output(&result.try_to_vec().sdk_expect("ERR_SERIALIZE")); + io.return_output(&result.try_to_vec().sdk_expect("ERR_SERIALIZE")); } #[no_mangle] pub extern "C" fn get_block_hash() { - let block_height = sdk::read_input_borsh().sdk_unwrap(); + let mut io = Runtime; + let block_height = io.read_input_borsh().sdk_unwrap(); let account_id = sdk::current_account_id(); - let chain_id = Engine::get_state().map(|state| state.chain_id).sdk_unwrap(); - let block_hash = Engine::compute_block_hash(chain_id, block_height, &account_id); - sdk::return_output(block_hash.as_bytes()) + let chain_id = Engine::get_state(&io) + .map(|state| state.chain_id) + .sdk_unwrap(); + let block_hash = crate::engine::compute_block_hash(chain_id, block_height, &account_id); + io.return_output(block_hash.as_bytes()) } #[no_mangle] pub extern "C" fn get_code() { - let address = sdk::read_input_arr20().sdk_unwrap(); - let code = Engine::get_code(&Address(address)); - sdk::return_output(&code) + let mut io = Runtime; + let address = io.read_input_arr20().sdk_unwrap(); + let code = Engine::get_code(&io, &Address(address)); + io.return_output(&code) } #[no_mangle] pub extern "C" fn get_balance() { - let address = sdk::read_input_arr20().sdk_unwrap(); - let balance = Engine::get_balance(&Address(address)); - sdk::return_output(&balance.to_bytes()) + let mut io = Runtime; + let address = io.read_input_arr20().sdk_unwrap(); + let balance = Engine::get_balance(&io, &Address(address)); + io.return_output(&balance.to_bytes()) } #[no_mangle] pub extern "C" fn get_nonce() { - let address = sdk::read_input_arr20().sdk_unwrap(); - let nonce = Engine::get_nonce(&Address(address)); - sdk::return_output(&u256_to_arr(&nonce)) + let mut io = Runtime; + let address = io.read_input_arr20().sdk_unwrap(); + let nonce = Engine::get_nonce(&io, &Address(address)); + io.return_output(&u256_to_arr(&nonce)) } #[no_mangle] pub extern "C" fn get_storage_at() { - let args: GetStorageAtArgs = sdk::read_input_borsh().sdk_unwrap(); + let mut io = Runtime; + let args: GetStorageAtArgs = io.read_input_borsh().sdk_unwrap(); let address = Address(args.address); - let generation = Engine::get_generation(&address); - let value = Engine::get_storage(&Address(args.address), &H256(args.key), generation); - sdk::return_output(&value.0) + let generation = Engine::get_generation(&io, &address); + let value = Engine::get_storage(&io, &Address(args.address), &H256(args.key), generation); + io.return_output(&value.0) } /// @@ -487,9 +519,10 @@ mod contract { // Only the owner can initialize the EthConnector sdk::assert_private_call(); - let args = InitCallArgs::try_from_slice(&sdk::read_input()).expect(ERR_FAILED_PARSE); + let io = Runtime; + let args: InitCallArgs = io.read_input_borsh().sdk_unwrap(); - EthConnectorContract::init_contract(args); + EthConnectorContract::init_contract(io, args); } #[no_mangle] @@ -497,69 +530,86 @@ mod contract { // Only the owner can set the EthConnector contract data sdk::assert_private_call(); - let args = - SetContractDataCallArgs::try_from_slice(&sdk::read_input()).expect(ERR_FAILED_PARSE); + let mut io = Runtime; + let args: SetContractDataCallArgs = io.read_input_borsh().sdk_unwrap(); - EthConnectorContract::set_contract_data(args); + EthConnectorContract::set_contract_data(&mut io, args); } #[no_mangle] pub extern "C" fn withdraw() { - EthConnectorContract::get_instance().withdraw_eth_from_near() + let mut io = Runtime; + let args = io.read_input_borsh().sdk_unwrap(); + let result = EthConnectorContract::get_instance(io).withdraw_eth_from_near(args); + let result_bytes = result.try_to_vec().sdk_expect("ERR_SERIALIZE"); + io.return_output(&result_bytes); } #[no_mangle] pub extern "C" fn deposit() { - EthConnectorContract::get_instance().deposit() + let io = Runtime; + let raw_proof = io.read_input().to_vec(); + EthConnectorContract::get_instance(io).deposit(raw_proof) } #[no_mangle] pub extern "C" fn finish_deposit() { - EthConnectorContract::get_instance().finish_deposit(); + sdk::assert_private_call(); + let io = Runtime; + let data = io.read_input_borsh().sdk_unwrap(); + EthConnectorContract::get_instance(io).finish_deposit(data); } #[no_mangle] pub extern "C" fn is_used_proof() { - let args = IsUsedProofCallArgs::try_from_slice(&sdk::read_input()).expect(ERR_FAILED_PARSE); + let mut io = Runtime; + let args: IsUsedProofCallArgs = io.read_input_borsh().sdk_unwrap(); - let is_used_proof = EthConnectorContract::get_instance().is_used_proof(args.proof); + let is_used_proof = EthConnectorContract::get_instance(io).is_used_proof(args.proof); let res = is_used_proof.try_to_vec().unwrap(); - sdk::return_output(&res[..]); + io.return_output(&res[..]); } #[no_mangle] pub extern "C" fn ft_total_supply() { - EthConnectorContract::get_instance().ft_total_eth_supply_on_near(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_total_eth_supply_on_near(); } #[no_mangle] pub extern "C" fn ft_total_eth_supply_on_near() { - EthConnectorContract::get_instance().ft_total_eth_supply_on_near(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_total_eth_supply_on_near(); } #[no_mangle] pub extern "C" fn ft_total_eth_supply_on_aurora() { - EthConnectorContract::get_instance().ft_total_eth_supply_on_aurora(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_total_eth_supply_on_aurora(); } #[no_mangle] pub extern "C" fn ft_balance_of() { - EthConnectorContract::get_instance().ft_balance_of(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_balance_of(); } #[no_mangle] pub extern "C" fn ft_balance_of_eth() { - EthConnectorContract::get_instance().ft_balance_of_eth_on_aurora(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_balance_of_eth_on_aurora(); } #[no_mangle] pub extern "C" fn ft_transfer() { - EthConnectorContract::get_instance().ft_transfer(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_transfer(); } #[no_mangle] pub extern "C" fn ft_resolve_transfer() { - EthConnectorContract::get_instance().ft_resolve_transfer(); + let io = Runtime; + EthConnectorContract::get_instance(io).ft_resolve_transfer(); } #[no_mangle] @@ -568,56 +618,62 @@ mod contract { // Check is payable sdk::assert_one_yocto(); + let io = Runtime; let args = TransferCallCallArgs::from( - parse_json(&sdk::read_input()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), + parse_json(&io.read_input().to_vec()).expect_utf8(ERR_FAILED_PARSE.as_bytes()), ); - EthConnectorContract::get_instance().ft_transfer_call(args); + EthConnectorContract::get_instance(io).ft_transfer_call(args); } #[no_mangle] pub extern "C" fn storage_deposit() { - EthConnectorContract::get_instance().storage_deposit() + let io = Runtime; + EthConnectorContract::get_instance(io).storage_deposit() } #[no_mangle] pub extern "C" fn storage_withdraw() { - EthConnectorContract::get_instance().storage_withdraw() + let io = Runtime; + EthConnectorContract::get_instance(io).storage_withdraw() } #[no_mangle] pub extern "C" fn storage_balance_of() { - EthConnectorContract::get_instance().storage_balance_of() + let io = Runtime; + EthConnectorContract::get_instance(io).storage_balance_of() } #[no_mangle] pub extern "C" fn get_paused_flags() { - let paused_flags = EthConnectorContract::get_instance().get_paused_flags(); + let mut io = Runtime; + let paused_flags = EthConnectorContract::get_instance(io).get_paused_flags(); let data = paused_flags.try_to_vec().expect(ERR_FAILED_PARSE); - sdk::return_output(&data[..]); + io.return_output(&data[..]); } #[no_mangle] pub extern "C" fn set_paused_flags() { sdk::assert_private_call(); - let args = - PauseEthConnectorCallArgs::try_from_slice(&sdk::read_input()).expect(ERR_FAILED_PARSE); - EthConnectorContract::get_instance().set_paused_flags(args); + let io = Runtime; + let args: PauseEthConnectorCallArgs = io.read_input_borsh().sdk_unwrap(); + + EthConnectorContract::get_instance(io).set_paused_flags(args); } #[no_mangle] pub extern "C" fn get_accounts_counter() { - EthConnectorContract::get_instance().get_accounts_counter() + let io = Runtime; + EthConnectorContract::get_instance(io).get_accounts_counter(); } #[no_mangle] pub extern "C" fn get_erc20_from_nep141() { - let args: GetErc20FromNep141CallArgs = - GetErc20FromNep141CallArgs::try_from_slice(&sdk::read_input()) - .sdk_expect("ERR_ARG_PARSE"); + let mut io = Runtime; + let args: GetErc20FromNep141CallArgs = io.read_input_borsh().sdk_unwrap(); - sdk::return_output( - Engine::get_erc20_from_nep141(args.nep141.as_bytes()) + io.return_output( + Engine::get_erc20_from_nep141(io, args.nep141.as_bytes()) .sdk_unwrap() .as_slice(), ); @@ -625,9 +681,10 @@ mod contract { #[no_mangle] pub extern "C" fn get_nep141_from_erc20() { - sdk::return_output( - Engine::nep141_erc20_map() - .lookup_right(sdk::read_input().as_slice()) + let mut io = Runtime; + io.return_output( + Engine::nep141_erc20_map(io) + .lookup_right(io.read_input().to_vec().as_slice()) .sdk_expect("ERC20_NOT_FOUND") .as_slice(), ); @@ -635,18 +692,20 @@ mod contract { #[no_mangle] pub extern "C" fn ft_metadata() { + let mut io = Runtime; let metadata: FungibleTokenMetadata = - EthConnectorContract::get_metadata().unwrap_or_default(); + EthConnectorContract::get_metadata(&io).unwrap_or_default(); let json_data = crate::json::JsonValue::from(metadata); - sdk::return_output(json_data.to_string().as_bytes()) + io.return_output(json_data.to_string().as_bytes()) } #[cfg(feature = "integration-test")] #[no_mangle] pub extern "C" fn verify_log_entry() { sdk::log!("Call from verify_log_entry"); + let mut io = Runtime; let data = true.try_to_vec().unwrap(); - sdk::return_output(&data[..]); + io.return_output(&data[..]); } /// Function used to create accounts for tests @@ -655,11 +714,12 @@ mod contract { pub extern "C" fn mint_account() { use evm::backend::ApplyBackend; - let args: ([u8; 20], u64, u64) = sdk::read_input_borsh().sdk_expect("ERR_ARGS"); + let io = Runtime; + let args: ([u8; 20], u64, u64) = io.read_input_borsh().sdk_expect("ERR_ARGS"); let address = Address(args.0); let nonce = U256::from(args.1); let balance = U256::from(args.2); - let mut engine = Engine::new(address).sdk_unwrap(); + let mut engine = Engine::new(address, io).sdk_unwrap(); let state_change = evm::backend::Apply::Modify { address, basic: evm::backend::Basic { balance, nonce }, @@ -675,7 +735,8 @@ mod contract { /// fn internal_get_upgrade_index() -> u64 { - match sdk::read_u64(&bytes_to_key(KeyPrefix::Config, CODE_STAGE_KEY)) { + let io = Runtime; + match io.read_u64(&bytes_to_key(KeyPrefix::Config, CODE_STAGE_KEY)) { Ok(index) => index, Err(sdk::error::ReadU64Error::InvalidU64) => sdk::panic_utf8(b"ERR_INVALID_UPGRADE"), Err(sdk::error::ReadU64Error::MissingValue) => sdk::panic_utf8(b"ERR_NO_UPGRADE"), diff --git a/engine/src/map.rs b/engine/src/map.rs index c08ce18be..28de64c14 100644 --- a/engine/src/map.rs +++ b/engine/src/map.rs @@ -1,14 +1,25 @@ pub use crate::prelude::{bytes_to_key, BorshDeserialize, BorshSerialize, KeyPrefixU8, Vec}; +use aurora_engine_sdk::io::{StorageIntermediate, IO}; +use aurora_engine_sdk::near_runtime::Runtime; /// An non-iterable implementation of a map that stores its content directly on the trie. /// Use `key_prefix` as a unique prefix for keys. -#[derive(BorshSerialize, BorshDeserialize, Default)] -pub struct LookupMap {} +#[derive(BorshSerialize, BorshDeserialize)] +pub struct LookupMap { + #[borsh_skip] + pub io: I, +} + +impl Default for LookupMap { + fn default() -> Self { + Self { io: Runtime } + } +} -impl LookupMap { +impl LookupMap { /// Create a new map. - pub fn new() -> Self { - Self {} + pub fn new(io: I) -> Self { + Self { io } } /// Build key for this map scope @@ -20,20 +31,20 @@ impl LookupMap { #[allow(dead_code)] pub fn contains_key_raw(&self, key_raw: &[u8]) -> bool { let storage_key = self.raw_key_to_storage_key(key_raw); - crate::prelude::sdk::storage_has_key(&storage_key) + self.io.storage_has_key(&storage_key) } /// Returns the serialized value corresponding to the serialized key. #[allow(dead_code)] pub fn get_raw(&self, key_raw: &[u8]) -> Option> { let storage_key = self.raw_key_to_storage_key(key_raw); - crate::prelude::sdk::read_storage(&storage_key) + self.io.read_storage(&storage_key).map(|s| s.to_vec()) } /// Inserts a serialized key-value pair into the map. pub fn insert_raw(&mut self, key_raw: &[u8], value_raw: &[u8]) { let storage_key = self.raw_key_to_storage_key(key_raw); - crate::prelude::sdk::write_storage(&storage_key, value_raw); + self.io.write_storage(&storage_key, value_raw); } /// Removes a serialized key from the map, returning the serialized value at the key if the key @@ -41,47 +52,50 @@ impl LookupMap { #[allow(dead_code)] pub fn remove_raw(&mut self, key_raw: &[u8]) -> Option> { let storage_key = self.raw_key_to_storage_key(key_raw); - crate::prelude::sdk::remove_storage_with_result(&storage_key) + self.io.remove_storage(&storage_key).map(|s| s.to_vec()) } } #[derive(BorshSerialize, BorshDeserialize, Default)] -pub struct BijectionMap {} +pub struct BijectionMap { + #[borsh_skip] + pub io: I, +} -impl BijectionMap { - fn left_to_right() -> LookupMap { - Default::default() +impl BijectionMap { + fn left_to_right(&self) -> LookupMap { + LookupMap { io: self.io } } - fn right_to_left() -> LookupMap { - Default::default() + fn right_to_left(&self) -> LookupMap { + LookupMap { io: self.io } } pub fn insert(&self, value_left: &[u8], value_right: &[u8]) { - Self::left_to_right().insert_raw(value_left, value_right); - Self::right_to_left().insert_raw(value_right, value_left); + self.left_to_right().insert_raw(value_left, value_right); + self.right_to_left().insert_raw(value_right, value_left); } pub fn lookup_left(&self, value_left: &[u8]) -> Option> { - Self::left_to_right().get_raw(value_left) + self.left_to_right().get_raw(value_left) } #[allow(dead_code)] pub fn lookup_right(&self, value_right: &[u8]) -> Option> { - Self::right_to_left().get_raw(value_right) + self.right_to_left().get_raw(value_right) } #[allow(dead_code)] pub fn remove_left(&self, value_left: &[u8]) { - if let Some(value_right) = Self::left_to_right().remove_raw(value_left) { - Self::right_to_left().remove_raw(value_right.as_slice()); + if let Some(value_right) = self.left_to_right().remove_raw(value_left) { + self.right_to_left().remove_raw(value_right.as_slice()); } } #[allow(dead_code)] pub fn remove_right(&self, value_right: &[u8]) { - if let Some(value_left) = Self::right_to_left().remove_raw(value_right) { - Self::left_to_right().remove_raw(value_left.as_slice()); + if let Some(value_left) = self.right_to_left().remove_raw(value_right) { + self.left_to_right().remove_raw(value_left.as_slice()); } } } diff --git a/etc/state-migration-test/src/lib.rs b/etc/state-migration-test/src/lib.rs index 8f949be4b..ef87ae348 100644 --- a/etc/state-migration-test/src/lib.rs +++ b/etc/state-migration-test/src/lib.rs @@ -4,7 +4,8 @@ extern crate alloc; use alloc::vec::Vec; use aurora_engine::engine::{Engine, EngineState}; -use aurora_engine_sdk as sdk; +use aurora_engine_sdk::near_runtime::Runtime; +use aurora_engine_sdk::io::{IO, StorageIntermediate}; use aurora_engine_types::storage; use borsh::{BorshDeserialize, BorshSerialize}; @@ -16,9 +17,10 @@ struct NewFancyState { #[no_mangle] pub extern "C" fn state_migration() { - let old_state = match Engine::get_state() { + let mut io = Runtime; + let old_state = match Engine::get_state(&io) { Ok(state) => state, - Err(e) => sdk::panic_utf8(e.as_ref()), + Err(e) => aurora_engine_sdk::panic_utf8(e.as_ref()), }; let new_state = NewFancyState { @@ -26,16 +28,17 @@ pub extern "C" fn state_migration() { some_other_numbers: [3, 1, 4, 1, 5, 9, 2], }; - sdk::write_storage(&state_key(), &new_state.try_to_vec().expect("ERR_SER")); + io.write_storage(&state_key(), &new_state.try_to_vec().expect("ERR_SER")); } #[no_mangle] pub extern "C" fn some_new_fancy_function() { - let state = sdk::read_storage(&state_key()) - .and_then(|bytes| NewFancyState::try_from_slice(&bytes).ok()) + let mut io = Runtime; + let state = io.read_storage(&state_key()) + .and_then(|bytes| NewFancyState::try_from_slice(&bytes.to_vec()).ok()) .unwrap(); - sdk::return_output(&state.some_other_numbers.try_to_vec().unwrap()); + io.return_output(&state.some_other_numbers.try_to_vec().unwrap()); } fn state_key() -> Vec {