use borsh::{BorshDeserialize, BorshSerialize};
use pchain_types::blockchain::{ExitStatus, Transaction};
use crate::common::{
compute_contract_address, ArgsBuilder, CallResult, SimulateWorldState, TestData,
};
mod common;
#[test]
fn test_ctoc_api() {
let (mut sws, contract_addr_1, contract_addr_2) =
deploy_two_contracts("all_features", true, "all_features", true);
let origin_address = [2u8; 32];
sws.set_balance(origin_address, 300_000_000);
let bd = TestData::block_params();
let mut base_tx = TestData::transaction();
base_tx.signer = origin_address;
base_tx.gas_limit = 200_000_000;
let result = pchain_runtime::Runtime::new().transition(
sws.world_state,
Transaction {
commands: vec![ArgsBuilder::new().add(12345_i32).make_call(
Some(0),
contract_addr_1,
"set_data_only",
)],
nonce: 0,
..base_tx
},
bd.clone(),
);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let function_args = Vec::<Vec<u8>>::new().try_to_vec().unwrap();
let result = pchain_runtime::Runtime::new().transition(
sws.world_state,
Transaction {
commands: vec![ArgsBuilder::new()
.add(contract_addr_1.clone()) .add("get_data_only".to_string()) .add(function_args)
.add(0u64) .add(1usize)
.make_call(Some(0), contract_addr_2, "call_other_contract")],
nonce: 1,
..base_tx
},
bd.clone(),
);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let return_bs: Vec<u8> =
CallResult::parse(receipt.last().unwrap().return_values.clone()).unwrap(); let get_data_value: i32 = BorshDeserialize::deserialize(&mut return_bs.as_slice()).unwrap(); assert_eq!(12345_i32, get_data_value);
let mut function_args: Vec<u8> = vec![];
let mut set_data_value_bs: Vec<u8> = vec![];
let set_data_value: i32 = 54321;
set_data_value.serialize(&mut set_data_value_bs).unwrap();
let function_args_bs: Vec<Vec<u8>> = vec![set_data_value_bs];
BorshSerialize::serialize(&function_args_bs, &mut function_args).unwrap();
let result = pchain_runtime::Runtime::new().transition(
sws.world_state,
Transaction {
commands: vec![ArgsBuilder::new()
.add(contract_addr_1.clone()) .add("set_data_only".to_string()) .add(function_args)
.add(0u64) .add(1usize) .make_call(Some(0), contract_addr_2, "call_other_contract")],
nonce: 2,
..base_tx
},
bd.clone(),
);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
assert_eq!(
sws.get_storage_data(contract_addr_1, vec![0u8]),
Some(54321_i32.to_le_bytes().to_vec())
);
}
#[test]
fn test_ctoc_use_contract() {
let (mut sws, _, contract_addr_2) =
deploy_two_contracts("basic_contract", false, "all_features", true);
let origin_address = [2u8; 32];
sws.set_balance(origin_address, 300_000_000);
let bd = TestData::block_params();
let mut base_tx = TestData::transaction();
base_tx.signer = origin_address;
base_tx.gas_limit = 200_000_000;
let result = pchain_runtime::Runtime::new().transition(
sws.world_state,
Transaction {
commands: vec![ArgsBuilder::new().add(0u64).make_call(
Some(0),
contract_addr_2,
"call_other_contract_using_macro",
)],
nonce: 0,
..base_tx
},
bd.clone(),
);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let return_bs: Vec<u8> =
CallResult::parse(receipt.last().unwrap().return_values.clone()).unwrap();
assert!(return_bs.is_empty());
assert!(receipt
.last()
.unwrap()
.logs
.iter()
.find(|e| {
e.topic == format!("topic: basic").as_bytes()
&& e.value == format!("Hello, Contract").as_bytes()
})
.is_some());
let result = pchain_runtime::Runtime::new().transition(
sws.world_state,
Transaction {
commands: vec![ArgsBuilder::new()
.add("testing name".to_string()) .add(0u64)
.make_call(
Some(0),
contract_addr_2,
"call_other_contract_using_macro_with_input",
)],
nonce: 1,
..base_tx
},
bd.clone(),
);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let _: SimulateWorldState = result.new_state.into();
let return_value: u32 =
CallResult::parse(receipt.last().unwrap().return_values.clone()).unwrap();
let check_value = "testing name".to_string().len() as u32;
assert_eq!(return_value, check_value);
assert!(receipt
.last()
.unwrap()
.logs
.iter()
.find(|e| {
e.topic == format!("topic: Hello From").as_bytes()
&& e.value == format!("Hello, Contract. From: testing name").as_bytes()
})
.is_some());
}
#[test]
fn test_ctoc_with_insufficient_gas_limit() {
let (mut sws, contract_addr_1, contract_addr_2) =
deploy_two_contracts("all_features", true, "all_features", true);
let origin_address = [2u8; 32];
sws.set_balance(origin_address, 300_000_000);
let bd = TestData::block_params();
let mut base_tx = TestData::transaction();
base_tx.signer = origin_address;
base_tx.gas_limit = 7_000_000;
let function_args = Vec::<Vec<u8>>::new().try_to_vec().unwrap();
let tx = Transaction {
commands: vec![ArgsBuilder::new()
.add(contract_addr_1.clone()) .add("get_data_only".to_string()) .add(function_args)
.add(0u64) .add(20usize)
.make_call(Some(0), contract_addr_2, "call_other_contract")],
nonce: 0,
..base_tx
};
let tx_base_cost = pchain_runtime::gas::tx_inclusion_cost(
pchain_types::serialization::Serializable::serialize(&tx).len(),
tx.commands.len(),
);
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(
receipt.last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(
receipt.last().unwrap().gas_used,
base_tx.gas_limit - tx_base_cost
); }
fn deploy_two_contracts(
contract_name_1: &str,
call_init_1: bool,
contract_name_2: &str,
call_init_2: bool,
) -> (SimulateWorldState, [u8; 32], [u8; 32]) {
let contract_code_1 = TestData::get_test_contract_code(contract_name_1);
let contract_code_2 = TestData::get_test_contract_code(contract_name_2);
let origin_address = TestData::get_origin_address();
let contract_address_1 = compute_contract_address(origin_address, 0)
.try_into()
.unwrap();
let contract_address_2 = compute_contract_address(origin_address, 1)
.try_into()
.unwrap();
let bd = TestData::block_params();
let mut sws = SimulateWorldState::default();
let init_from_balance = 500_000_000_000;
sws.set_balance(origin_address, init_from_balance);
let deploy_1 = if call_init_1 {
ArgsBuilder::new()
.empty_args()
.make_deploy(contract_code_1, 0)
} else {
ArgsBuilder::new().make_deploy(contract_code_1, 0)
};
let mut tx = TestData::transaction();
tx.signer = origin_address;
tx.commands = vec![deploy_1];
tx.gas_limit = 300_000_000;
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let deploy_2 = if call_init_2 {
ArgsBuilder::new()
.empty_args()
.make_deploy(contract_code_2, 0)
} else {
ArgsBuilder::new().make_deploy(contract_code_2, 0)
};
let mut tx = TestData::transaction();
tx.signer = origin_address;
tx.commands = vec![deploy_2];
tx.nonce = 1;
tx.gas_limit = 300_000_000;
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
(sws, contract_address_1, contract_address_2)
}