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; /// Initial balance used in tests. pub const TESTING_INIT_BALANCE: u128 = 1_000_000_000 * NEAR_BASE; /// One NEAR, divisible by 10^24. pub const NEAR_BASE: u128 = 1_000_000_000_000_000_000_000_000; /// Max prepaid amount of gas. 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::()]; input_data[..size_of::()].copy_from_slice(&from.to_le_bytes()); input_data[size_of::()..].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::()]; input_data[..size_of::()].copy_from_slice(&from.to_le_bytes()); input_data[size_of::()..].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 ); }