use near_primitives::errors::{ActionError, ActionErrorKind};
use near_primitives::serialize::to_base64;
use near_primitives::views::FinalExecutionStatus;
use near_vm_errors::{FunctionCallError, HostError};
use std::mem::size_of;
use testlib::node::{Node, RuntimeNode};
#[cfg(test)]
use assert_matches::assert_matches;
pub const TESTING_INIT_BALANCE: u128 = 1_000_000_000 * NEAR_BASE;
pub const NEAR_BASE: u128 = 1_000_000_000_000_000_000_000_000;
const MAX_GAS: u64 = 300_000_000_000_000;
fn setup_test_contract(wasm_binary: &[u8]) -> RuntimeNode {
let node = RuntimeNode::new(&"alice.near".to_string());
let account_id = node.account_id().unwrap();
let node_user = node.user();
let transaction_result = node_user
.create_account(
account_id.clone(),
"test_contract".to_string(),
node.signer().public_key(),
TESTING_INIT_BALANCE / 2,
)
.unwrap();
assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(to_base64(&[])));
assert_eq!(transaction_result.receipts_outcome.len(), 2);
let transaction_result =
node_user.deploy_contract("test_contract".to_string(), wasm_binary.to_vec()).unwrap();
assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(to_base64(&[])));
assert_eq!(transaction_result.receipts_outcome.len(), 1);
node
}
#[test]
fn test_evil_deep_trie() {
let node =
setup_test_contract(include_bytes!("../../near-vm-runner/tests/res/test_contract_rs.wasm"));
(0..50).for_each(|i| {
println!("insertStrings #{}", i);
let from = i * 10 as u64;
let to = (i + 1) * 10 as u64;
let mut input_data = [0u8; 2 * size_of::<u64>()];
input_data[..size_of::<u64>()].copy_from_slice(&from.to_le_bytes());
input_data[size_of::<u64>()..].copy_from_slice(&to.to_le_bytes());
let res = node
.user()
.function_call(
"alice.near".to_string(),
"test_contract".to_string(),
"insert_strings",
input_data.to_vec(),
MAX_GAS,
0,
)
.unwrap();
println!("Gas burnt: {}", res.receipts_outcome[0].outcome.gas_burnt);
assert_eq!(res.status, FinalExecutionStatus::SuccessValue(to_base64(&[])), "{:?}", res);
});
(0..50).rev().for_each(|i| {
println!("deleteStrings #{}", i);
let from = i * 10 as u64;
let to = (i + 1) * 10 as u64;
let mut input_data = [0u8; 2 * size_of::<u64>()];
input_data[..size_of::<u64>()].copy_from_slice(&from.to_le_bytes());
input_data[size_of::<u64>()..].copy_from_slice(&to.to_le_bytes());
let res = node
.user()
.function_call(
"alice.near".to_string(),
"test_contract".to_string(),
"delete_strings",
input_data.to_vec(),
MAX_GAS,
0,
)
.unwrap();
println!("Gas burnt: {}", res.receipts_outcome[0].outcome.gas_burnt);
assert_eq!(res.status, FinalExecutionStatus::SuccessValue(to_base64(&[])), "{:?}", res);
});
}
#[test]
fn test_evil_deep_recursion() {
let node =
setup_test_contract(include_bytes!("../../near-vm-runner/tests/res/test_contract_rs.wasm"));
[100, 1000, 10000, 100000, 1000000].iter().for_each(|n| {
println!("{}", n);
let n = *n as u64;
let n_bytes = n.to_le_bytes().to_vec();
let res = node
.user()
.function_call(
"alice.near".to_string(),
"test_contract".to_string(),
"recurse",
n_bytes.clone(),
MAX_GAS,
0,
)
.unwrap();
if n <= 1000 {
assert_eq!(
res.status,
FinalExecutionStatus::SuccessValue(to_base64(&n_bytes)),
"{:?}",
res
);
} else {
assert_matches!(res.status, FinalExecutionStatus::Failure(_), "{:?}", res);
}
});
}
#[test]
fn test_evil_abort() {
let node =
setup_test_contract(include_bytes!("../../near-vm-runner/tests/res/test_contract_rs.wasm"));
let res = node
.user()
.function_call(
"alice.near".to_string(),
"test_contract".to_string(),
"abort_with_zero",
vec![],
MAX_GAS,
0,
)
.unwrap();
assert_eq!(
res.status,
FinalExecutionStatus::Failure(
ActionError {
index: Some(0),
kind: ActionErrorKind::FunctionCallError(FunctionCallError::HostError(
HostError::BadUTF16
))
}
.into()
),
"{:?}",
res
);
}