Skip to content
55 changes: 31 additions & 24 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::contract::current_address;
use crate::map::{BijectionMap, LookupMap};
use crate::parameters::{
FunctionCallArgs, NEP141FtOnTransferArgs, NewCallArgs, PromiseCreateArgs, SubmitResult,
ViewCallArgs,
TransactionStatus, ViewCallArgs,
};

use crate::precompiles::Precompiles;
Expand Down Expand Up @@ -147,14 +147,18 @@ pub type EngineResult<T> = Result<T, EngineError>;

trait ExitIntoResult {
/// Checks if the EVM exit is ok or an error.
fn into_result(self) -> Result<(), EngineErrorKind>;
fn into_result(self, data: Vec<u8>) -> Result<TransactionStatus, EngineErrorKind>;
}

impl ExitIntoResult for ExitReason {
fn into_result(self) -> Result<(), EngineErrorKind> {
fn into_result(self, data: Vec<u8>) -> Result<TransactionStatus, EngineErrorKind> {
use ExitReason::*;
match self {
Succeed(_) | Revert(_) => Ok(()),
Succeed(_) => Ok(TransactionStatus::Succeed(data)),
Revert(_) => Ok(TransactionStatus::Revert(data)),
Error(ExitError::OutOfOffset) => Ok(TransactionStatus::OutOfOffset),
Error(ExitError::OutOfFund) => Ok(TransactionStatus::OutOfFund),
Error(ExitError::OutOfGas) => Ok(TransactionStatus::OutOfGas),
Error(e) => Err(e.into()),
Fatal(e) => Err(e.into()),
}
Expand Down Expand Up @@ -555,24 +559,27 @@ impl Engine {
) -> EngineResult<SubmitResult> {
let mut executor = self.make_executor(gas_limit);
let address = executor.create_address(CreateScheme::Legacy { caller: origin });
let (status, result) = (
let (exit_reason, result) = (
executor.transact_create(origin, value.raw(), input, gas_limit, access_list),
address,
);
let is_succeed = status.is_succeed();

let used_gas = executor.used_gas();
if let Err(e) = status.into_result() {
Engine::increment_nonce(&origin);
return Err(e.with_gas_used(used_gas));
}
let status = match exit_reason.into_result(result.0.to_vec()) {
Ok(status) => status,
Err(e) => {
Engine::increment_nonce(&origin);
return Err(e.with_gas_used(used_gas));
}
};

let (values, logs, promises) = executor.into_state().deconstruct();
self.apply(values, Vec::<Log>::new(), true);
Self::schedule_promises(promises);

Ok(SubmitResult {
status: is_succeed,
status,
gas_used: used_gas,
result: result.0.to_vec(),
logs: logs.into_iter().map(Into::into).collect(),
})
}
Expand All @@ -594,15 +601,17 @@ impl Engine {
access_list: Vec<(Address, Vec<H256>)>, // See EIP-2930
) -> EngineResult<SubmitResult> {
let mut executor = self.make_executor(gas_limit);
let (status, result) =
let (exit_reason, result) =
executor.transact_call(origin, contract, value.raw(), input, gas_limit, access_list);

let is_succeed = status.is_succeed();
let used_gas = executor.used_gas();
if let Err(e) = status.into_result() {
Engine::increment_nonce(&origin);
return Err(e.with_gas_used(used_gas));
}
let status = match exit_reason.into_result(result) {
Ok(status) => status,
Err(e) => {
Engine::increment_nonce(&origin);
return Err(e.with_gas_used(used_gas));
}
};

let (values, logs, promises) = executor.into_state().deconstruct();

Expand All @@ -612,9 +621,8 @@ impl Engine {
Self::schedule_promises(promises);

Ok(SubmitResult {
status: is_succeed,
status,
gas_used: used_gas,
result,
logs: logs.into_iter().map(Into::into).collect(),
})
}
Expand All @@ -625,7 +633,7 @@ impl Engine {
Self::set_nonce(address, &new_nonce);
}

pub fn view_with_args(&self, args: ViewCallArgs) -> Result<Vec<u8>, EngineErrorKind> {
pub fn view_with_args(&self, args: ViewCallArgs) -> Result<TransactionStatus, EngineErrorKind> {
let origin = Address::from_slice(&args.sender);
let contract = Address::from_slice(&args.address);
let value = U256::from_big_endian(&args.amount);
Expand All @@ -639,12 +647,11 @@ impl Engine {
value: Wei,
input: Vec<u8>,
gas_limit: u64,
) -> Result<Vec<u8>, EngineErrorKind> {
) -> Result<TransactionStatus, EngineErrorKind> {
let mut executor = self.make_executor(gas_limit);
let (status, result) =
executor.transact_call(origin, contract, value.raw(), input, gas_limit, Vec::new());
status.into_result()?;
Ok(result)
status.into_result(result)
}

fn make_executor(&self, gas_limit: u64) -> StackExecutor<AuroraStackState, Precompiles> {
Expand Down
53 changes: 39 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ mod contract {
use crate::parameters::{
DeployErc20TokenArgs, ExpectUtf8, FunctionCallArgs, GetErc20FromNep141CallArgs,
GetStorageAtArgs, InitCallArgs, IsUsedProofCallArgs, NEP141FtOnTransferArgs, NewCallArgs,
PauseEthConnectorCallArgs, SetContractDataCallArgs, TransferCallCallArgs, ViewCallArgs,
PauseEthConnectorCallArgs, SetContractDataCallArgs, TransactionStatus,
TransferCallCallArgs, ViewCallArgs,
};

use crate::json::parse_json;
Expand Down Expand Up @@ -379,21 +380,24 @@ mod contract {
ethabi::Token::Address(current_address()),
]);

Engine::deploy_code_with_input(
let address = match Engine::deploy_code_with_input(
&mut engine,
(&[erc20_contract, deploy_args.as_slice()].concat()).to_vec(),
)
.map(|res| {
let address = H160(res.result.as_slice().try_into().unwrap());
crate::log!(
crate::prelude::format!("Deployed ERC-20 in Aurora at: {:#?}", address).as_str()
);
engine
.register_token(address.as_bytes(), &args.nep141.as_bytes())
.sdk_unwrap();
res.result.try_to_vec().sdk_expect("ERR_SERIALIZE")
})
.sdk_process();
) {
Ok(result) => match result.status {
TransactionStatus::Succeed(ret) => H160(ret.as_slice().try_into().unwrap()),
other => sdk::panic_utf8(other.as_ref()),
},
Err(e) => sdk::panic_utf8(e.as_ref()),
};

crate::log!(
crate::prelude::format!("Deployed ERC-20 in Aurora at: {:#?}", address).as_str()
);
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"));

// TODO: charge for storage
}
Expand Down Expand Up @@ -630,6 +634,27 @@ mod contract {
sdk::return_output(&data[..]);
}

/// Function used to create accounts for tests
#[cfg(feature = "integration-test")]
#[no_mangle]
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 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 state_change = evm::backend::Apply::Modify {
address,
basic: evm::backend::Basic { balance, nonce },
code: None,
storage: core::iter::empty(),
reset_storage: false,
};
engine.apply(core::iter::once(state_change), core::iter::empty(), false);
}

///
/// Utility methods.
///
Expand Down
44 changes: 42 additions & 2 deletions src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,53 @@ impl From<Log> for ResultLog {
}
}

/// The status of a transaction.
#[derive(Debug, BorshSerialize, BorshDeserialize, PartialEq, Eq)]
pub enum TransactionStatus {
Succeed(Vec<u8>),
Revert(Vec<u8>),
OutOfGas,
OutOfFund,
OutOfOffset,
CallTooDeep,
}

impl TransactionStatus {
pub fn is_ok(&self) -> bool {
matches!(*self, TransactionStatus::Succeed(_))
}

pub fn is_revert(&self) -> bool {
matches!(*self, TransactionStatus::Revert(_))
}

pub fn is_fail(&self) -> bool {
*self == TransactionStatus::OutOfGas
|| *self == TransactionStatus::OutOfFund
|| *self == TransactionStatus::OutOfOffset
|| *self == TransactionStatus::CallTooDeep
}
}

impl AsRef<[u8]> for TransactionStatus {
fn as_ref(&self) -> &[u8] {
match self {
Self::Succeed(_) => b"SUCCESS",
Self::Revert(_) => b"ERR_REVERT",
Self::OutOfFund => b"ERR_OUT_OF_FUNDS",
Self::OutOfGas => b"ERR_OUT_OF_GAS",
Self::OutOfOffset => b"ERR_OUT_OF_OFFSET",
Self::CallTooDeep => b"ERR_CALL_TOO_DEEP",
}
}
}

/// Borsh-encoded parameters for the `call`, `call_with_args`, `deploy_code`,
/// and `deploy_with_input` methods.
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct SubmitResult {
pub status: bool,
pub status: TransactionStatus,
pub gas_used: u64,
pub result: Vec<u8>,
pub logs: Vec<ResultLog>,
}

Expand Down
7 changes: 4 additions & 3 deletions src/test_utils/exit_precompile.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parameters::SubmitResult;
use crate::prelude::{Address, U256};
use crate::test_utils::{solidity, AuroraRunner, Signer};
use crate::test_utils::{self, solidity, AuroraRunner, Signer};
use crate::transaction::LegacyEthTransaction;

pub(crate) struct TesterConstructor(pub solidity::ContractConstructor);
Expand Down Expand Up @@ -78,8 +78,9 @@ impl Tester {

let result = runner.submit_transaction(&signer.secret_key, tx).unwrap();

if result.status {
Ok(ethabi::decode(output_type, result.result.as_slice()).unwrap())
if result.status.is_ok() {
let result = test_utils::unwrap_success(result);
Ok(ethabi::decode(output_type, result.as_slice()).unwrap())
} else {
Err(result)
}
Expand Down
26 changes: 24 additions & 2 deletions src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rlp::RlpStream;
use secp256k1::{self, Message, PublicKey, SecretKey};

use crate::fungible_token::FungibleToken;
use crate::parameters::{InitCallArgs, NewCallArgs, SubmitResult};
use crate::parameters::{InitCallArgs, NewCallArgs, SubmitResult, TransactionStatus};
use crate::prelude::Address;
use crate::storage;
use crate::test_utils::solidity::{ContractConstructor, DeployedContract};
Expand Down Expand Up @@ -281,7 +281,7 @@ impl AuroraRunner {
assert!(maybe_err.is_none());
let submit_result =
SubmitResult::try_from_slice(&output.unwrap().return_data.as_value().unwrap()).unwrap();
let address = Address::from_slice(&submit_result.result);
let address = Address::from_slice(&unwrap_success(submit_result));
let contract_constructor: ContractConstructor = contract_constructor.into();
DeployedContract {
abi: contract_constructor.abi,
Expand Down Expand Up @@ -512,3 +512,25 @@ pub(crate) fn address_from_hex(address: &str) -> Address {

Address::from_slice(&bytes)
}

pub fn unwrap_success(result: SubmitResult) -> Vec<u8> {
match result.status {
TransactionStatus::Succeed(ret) => ret,
other => panic!("Unexpected status: {:?}", other),
}
}

pub fn unwrap_revert(result: SubmitResult) -> Vec<u8> {
match result.status {
TransactionStatus::Revert(ret) => ret,
other => panic!("Unexpected status: {:?}", other),
}
}

pub fn panic_on_fail(status: TransactionStatus) {
match status {
TransactionStatus::Succeed(_) => (),
TransactionStatus::Revert(message) => panic!("{}", String::from_utf8_lossy(&message)),
other => panic!("{}", String::from_utf8_lossy(other.as_ref())),
}
}
12 changes: 6 additions & 6 deletions src/test_utils/self_destruct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parameters::FunctionCallArgs;
use crate::prelude::Address;
use crate::test_utils::{solidity, AuroraRunner, Signer};
use crate::test_utils::{self, solidity, AuroraRunner, Signer};
use crate::transaction::LegacyEthTransaction;
use borsh::BorshSerialize;
use primitive_types::U256;
Expand Down Expand Up @@ -73,8 +73,9 @@ impl SelfDestructFactory {
};

let result = runner.submit_transaction(&signer.secret_key, tx).unwrap();
let result = test_utils::unwrap_success(result);

Address::from_slice(&result.result[12..])
Address::from_slice(&result[12..])
}
}

Expand Down Expand Up @@ -111,11 +112,10 @@ impl SelfDestruct {
};

let result = runner.submit_transaction(&signer.secret_key, tx).unwrap();
let result = test_utils::unwrap_success(result);

if result.result.len() == 32 {
Some(u128::from_be_bytes(
result.result[16..32].try_into().unwrap(),
))
if result.len() == 32 {
Some(u128::from_be_bytes(result[16..32].try_into().unwrap()))
} else {
None
}
Expand Down
Loading