Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion etc/state-migration-test/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

116 changes: 95 additions & 21 deletions src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use near_vm_runner::{MockCompiledContractCache, VMError};
use primitive_types::U256;
use rlp::RlpStream;
use secp256k1::{self, Message, PublicKey, SecretKey};
use std::borrow::Cow;

use crate::fungible_token::{FungibleToken, FungibleTokenMetadata};
use crate::parameters::{InitCallArgs, NewCallArgs, SubmitResult, TransactionStatus};
use crate::parameters::{InitCallArgs, NewCallArgs, SubmitResult, TransactionStatus, ViewCallArgs};
use crate::prelude::Address;
use crate::storage;
use crate::test_utils::solidity::{ContractConstructor, DeployedContract};
Expand Down Expand Up @@ -74,7 +75,6 @@ pub(crate) struct AuroraRunner {
pub wasm_config: VMConfig,
pub fees_config: RuntimeFeesConfig,
pub current_protocol_version: u32,
pub profile: ProfileData,
pub previous_logs: Vec<String>,
}

Expand All @@ -89,10 +89,32 @@ pub(crate) struct OneShotAuroraRunner<'a> {

impl<'a> OneShotAuroraRunner<'a> {
pub fn call(
self,
method_name: &str,
caller_account_id: String,
input: Vec<u8>,
) -> (Option<VMOutcome>, Option<VMError>) {
self.call_with_optional_profile(method_name, caller_account_id, input, None)
}

pub fn profiled_call(
self,
method_name: &str,
caller_account_id: String,
input: Vec<u8>,
) -> (Option<VMOutcome>, Option<VMError>, ProfileData) {
let profile = Default::default();
let (outcome, error) =
self.call_with_optional_profile(method_name, caller_account_id, input, Some(&profile));
(outcome, error, profile)
}

fn call_with_optional_profile(
mut self,
method_name: &str,
caller_account_id: String,
input: Vec<u8>,
maybe_profile: Option<&ProfileData>,
) -> (Option<VMOutcome>, Option<VMError>) {
AuroraRunner::update_context(
&mut self.context,
Expand All @@ -101,6 +123,7 @@ impl<'a> OneShotAuroraRunner<'a> {
input,
);

let profile = maybe_profile.map(Cow::Borrowed).unwrap_or_default();
near_vm_runner::run(
&self.base.code,
method_name,
Expand All @@ -111,7 +134,7 @@ impl<'a> OneShotAuroraRunner<'a> {
&[],
self.base.current_protocol_version,
Some(&self.base.cache),
&self.base.profile,
profile.as_ref(),
)
}
}
Expand Down Expand Up @@ -149,15 +172,36 @@ impl AuroraRunner {
caller_account_id.clone(),
caller_account_id,
input,
None,
)
}

// Might be useful for optimizing performance in the future
#[allow(dead_code)]
pub fn profiled_call(
&mut self,
method_name: &str,
caller_account_id: String,
input: Vec<u8>,
) -> (Option<VMOutcome>, Option<VMError>, ProfileData) {
let profile = Default::default();
let (outcome, error) = self.call_with_signer(
method_name,
caller_account_id.clone(),
caller_account_id,
input,
Some(&profile),
);
(outcome, error, profile)
}

pub fn call_with_signer(
&mut self,
method_name: &str,
caller_account_id: String,
signer_account_id: String,
input: Vec<u8>,
maybe_profile: Option<&ProfileData>,
) -> (Option<VMOutcome>, Option<VMError>) {
Self::update_context(
&mut self.context,
Expand All @@ -166,6 +210,7 @@ impl AuroraRunner {
input,
);

let profile = maybe_profile.map(Cow::Borrowed).unwrap_or_default();
let (maybe_outcome, maybe_error) = near_vm_runner::run(
&self.code,
method_name,
Expand All @@ -176,7 +221,7 @@ impl AuroraRunner {
&[],
self.current_protocol_version,
Some(&self.cache),
&self.profile,
profile.as_ref(),
);
if let Some(outcome) = &maybe_outcome {
self.context.storage_usage = outcome.storage_usage;
Expand Down Expand Up @@ -265,6 +310,29 @@ impl AuroraRunner {
}
}

pub fn view_call(&self, args: ViewCallArgs) -> Result<TransactionStatus, VMError> {
let input = args.try_to_vec().unwrap();
let (outcome, maybe_error) = self.one_shot().call("view", "VIEWER".to_string(), input);
Ok(
TransactionStatus::try_from_slice(&Self::bytes_from_outcome(outcome, maybe_error)?)
.unwrap(),
)
}

pub fn profiled_view_call(
&self,
args: ViewCallArgs,
) -> (Result<TransactionStatus, VMError>, ProfileData) {
let input = args.try_to_vec().unwrap();
let (outcome, maybe_error, profile) =
self.one_shot()
.profiled_call("view", "VIEWER".to_string(), input);
let status = Self::bytes_from_outcome(outcome, maybe_error)
.map(|bytes| TransactionStatus::try_from_slice(&bytes).unwrap());

(status, profile)
}

pub fn get_balance(&self, address: Address) -> types::Wei {
types::Wei::new(self.getter_method_call("get_balance", address))
}
Expand All @@ -276,29 +344,27 @@ impl AuroraRunner {
// Used in `get_balance` and `get_nonce`. This function exists to avoid code duplication
// since the contract's `get_nonce` and `get_balance` have the same type signature.
fn getter_method_call(&self, method_name: &str, address: Address) -> U256 {
let mut context = self.context.clone();
Self::update_context(
&mut context,
"GETTER".to_string(),
let (outcome, maybe_error) = self.one_shot().call(
method_name,
"GETTER".to_string(),
address.as_bytes().to_vec(),
);
let (outcome, maybe_error) = near_vm_runner::run(
&self.code,
method_name,
&mut self.ext.clone(),
context,
&self.wasm_config,
&self.fees_config,
&[],
self.current_protocol_version,
Some(&self.cache),
&self.profile,
);
assert!(maybe_error.is_none());
let bytes = outcome.unwrap().return_data.as_value().unwrap();
U256::from_big_endian(&bytes)
}

fn bytes_from_outcome(
maybe_outcome: Option<VMOutcome>,
maybe_error: Option<VMError>,
) -> Result<Vec<u8>, VMError> {
if let Some(error) = maybe_error {
Err(error)
} else {
let bytes = maybe_outcome.unwrap().return_data.as_value().unwrap();
Ok(bytes)
}
}
}

impl Default for AuroraRunner {
Expand Down Expand Up @@ -339,7 +405,6 @@ impl Default for AuroraRunner {
wasm_config: Default::default(),
fees_config: Default::default(),
current_protocol_version: u32::MAX,
profile: Default::default(),
previous_logs: Default::default(),
}
}
Expand Down Expand Up @@ -408,6 +473,15 @@ pub(crate) fn create_eth_transaction(
sign_transaction(tx, chain_id, secret_key)
}

pub(crate) fn as_view_call(tx: LegacyEthTransaction, sender: Address) -> ViewCallArgs {
ViewCallArgs {
sender: sender.0,
address: tx.to.unwrap().0,
amount: tx.value.to_bytes(),
input: tx.data,
}
}

pub(crate) fn sign_transaction(
tx: LegacyEthTransaction,
chain_id: Option<u64>,
Expand Down
63 changes: 46 additions & 17 deletions src/tests/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn erc20_mint() {
// Validate pre-state
assert_eq!(
U256::zero(),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);

// Do mint transaction
Expand All @@ -33,7 +33,7 @@ fn erc20_mint() {
// Validate post-state
assert_eq!(
U256::from(mint_amount),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);
}

Expand All @@ -44,7 +44,7 @@ fn erc20_mint_out_of_gas() {
// Validate pre-state
assert_eq!(
U256::zero(),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);

// Try mint transaction
Expand All @@ -69,11 +69,12 @@ fn erc20_mint_out_of_gas() {
assert_eq!(error.status, TransactionStatus::OutOfGas);

// Validate post-state

test_utils::validate_address_balance_and_nonce(
&runner,
test_utils::address_from_secret_key(&source_account.secret_key),
Wei::new_u64(INITIAL_BALANCE - GAS_LIMIT * GAS_PRICE),
(INITIAL_NONCE + 3).into(),
(INITIAL_NONCE + 2).into(),
);
test_utils::validate_address_balance_and_nonce(
&runner,
Expand All @@ -83,6 +84,27 @@ fn erc20_mint_out_of_gas() {
);
}

#[test]
fn profile_erc20_get_balance() {
let (mut runner, mut source_account, _, contract) = initialize_erc20();
let source_address = test_utils::address_from_secret_key(&source_account.secret_key);

let outcome = runner.submit_with_signer(&mut source_account, |nonce| {
contract.mint(source_address, INITIAL_BALANCE.into(), nonce)
});
assert!(outcome.is_ok());

let balance_tx = contract.balance_of(source_address, U256::zero());
let (result, profile) =
runner.profiled_view_call(test_utils::as_view_call(balance_tx, source_address));
assert!(result.is_ok());

// call costs less than 6 Tgas
assert!(profile.all_gas() / 1_000_000_000_000 < 6);
// at least 70% of the cost is spent on wasm computation (as opposed to host functions)
assert!((100 * profile.wasm_gas()) / profile.all_gas() > 70);
}

#[test]
fn erc20_transfer_success() {
let (mut runner, mut source_account, dest_address, contract) = initialize_erc20();
Expand All @@ -96,11 +118,11 @@ fn erc20_transfer_success() {
// Validate pre-state
assert_eq!(
U256::from(INITIAL_BALANCE),
get_address_erc20_balance(&mut runner, &mut source_account, source_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, source_address, &contract)
);
assert_eq!(
U256::zero(),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);

// Do transfer
Expand All @@ -114,11 +136,11 @@ fn erc20_transfer_success() {
// Validate post-state
assert_eq!(
U256::from(INITIAL_BALANCE - TRANSFER_AMOUNT),
get_address_erc20_balance(&mut runner, &mut source_account, source_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, source_address, &contract)
);
assert_eq!(
U256::from(TRANSFER_AMOUNT),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);
}

Expand All @@ -135,11 +157,11 @@ fn erc20_transfer_insufficient_balance() {
// Validate pre-state
assert_eq!(
U256::from(INITIAL_BALANCE),
get_address_erc20_balance(&mut runner, &mut source_account, source_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, source_address, &contract)
);
assert_eq!(
U256::zero(),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);

// Do transfer
Expand All @@ -154,11 +176,11 @@ fn erc20_transfer_insufficient_balance() {
// Validate post-state
assert_eq!(
U256::from(INITIAL_BALANCE),
get_address_erc20_balance(&mut runner, &mut source_account, source_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, source_address, &contract)
);
assert_eq!(
U256::zero(),
get_address_erc20_balance(&mut runner, &mut source_account, dest_address, &contract)
get_address_erc20_balance(&mut runner, &source_account, dest_address, &contract)
);
}

Expand Down Expand Up @@ -205,15 +227,22 @@ fn deploy_erc_20_out_of_gas() {

fn get_address_erc20_balance(
runner: &mut test_utils::AuroraRunner,
signer: &mut Signer,
signer: &Signer,
address: Address,
contract: &ERC20,
) -> U256 {
let outcome = runner
.submit_with_signer(signer, |nonce| contract.balance_of(address, nonce))
let balance_tx = contract.balance_of(address, signer.nonce.into());
let result = runner
.view_call(test_utils::as_view_call(
balance_tx,
test_utils::address_from_secret_key(&signer.secret_key),
))
.unwrap();
let output = test_utils::unwrap_success(outcome);
U256::from_big_endian(&output)
let bytes = match result {
crate::parameters::TransactionStatus::Succeed(bytes) => bytes,
err => panic!("Unexpected view call status {:?}", err),
};
U256::from_big_endian(&bytes)
}

fn parse_erc20_error_message(result: &[u8]) -> String {
Expand Down
9 changes: 7 additions & 2 deletions src/tests/erc20_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ impl test_utils::AuroraRunner {
signer_account_id: String,
input: Vec<u8>,
) -> CallResult {
let (outcome, error) =
self.call_with_signer(method_name, caller_account_id, signer_account_id, input);
let (outcome, error) = self.call_with_signer(
method_name,
caller_account_id,
signer_account_id,
input,
None,
);
CallResult { outcome, error }
}

Expand Down