use pchain_runtime::{
formulas::{TOTAL_BASE_FEE, TREASURY_CUT_OF_BASE_FEE},
gas::tx_inclusion_cost,
TransitionError,
};
use pchain_types::{
blockchain::{Command, ExitStatus, Transaction},
cryptography::PublicAddress,
runtime::TransferInput,
serialization::Serializable,
};
use crate::common::{
compute_contract_address, ArgsBuilder, CallResult, SimulateWorldState, TestData,
EXPECTED_CBI_VERSION,
};
mod common;
#[test]
fn version() {
assert_eq!(pchain_runtime::cbi_version(), EXPECTED_CBI_VERSION);
}
#[test]
fn test_etoe() {
let transfer_value = 1u64;
let target = [2u8; 32];
let mut tx = TestData::transaction();
tx.commands = vec![Command::Transfer(TransferInput {
recipient: target,
amount: transfer_value,
})];
let priority_fee_per_gas = tx.priority_fee_per_gas;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let to_address = target;
let init_from_balance = 100_000_000;
sws.set_balance(from_address, init_from_balance);
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(from_address.clone());
assert_eq!(
new_from_balance,
init_from_balance
- transfer_value
- base_fee_per_gas * gas_used
- priority_fee_per_gas * gas_used
);
assert_eq!(sws.get_nonce(from_address), 1);
let new_to_balance = sws.get_balance(to_address.clone());
assert_eq!(new_to_balance, transfer_value);
assert_eq!(sws.get_nonce(to_address), 0);
}
#[test]
fn test_etoc() {
let wasm_bytes = TestData::get_test_contract_code("basic_contract");
let method_args = "arg".to_string();
let method_name = "emit_event_with_return";
let target = [2u8; 32];
let mut tx = TestData::transaction();
tx.gas_limit = 10_000_000;
tx.commands =
vec![ArgsBuilder::new()
.add(method_args.clone())
.make_call(Some(0), target, method_name)];
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let to_address = target;
let init_from_balance = 100_000_000;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(to_address, wasm_bytes, pchain_runtime::cbi_version());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used
);
assert_eq!(sws.get_nonce(from_address), 1);
let new_to_balance = sws.get_balance(to_address);
assert_eq!(new_to_balance, 0);
assert_eq!(sws.get_nonce(to_address), 0);
let result_value: u32 =
CallResult::parse(receipt.last().unwrap().return_values.clone()).unwrap();
assert_eq!(result_value as usize, method_args.len());
assert!(receipt
.last()
.unwrap()
.logs
.iter()
.find(|e| {
String::from_utf8(e.topic.clone()).unwrap() == "topic: Hello From".to_string()
&& String::from_utf8(e.value.clone()).unwrap()
== format!("Hello, Contract. From: {}", method_args).to_string()
})
.is_some());
}
#[test]
fn test_etoc_multiple() {
let wasm_bytes_1 = TestData::get_test_contract_code("all_features");
let contract_address_1 = [22u8; 32];
let method_args_1 = 123_i32;
let method_name_1 = "set_data_only";
let command_1 =
ArgsBuilder::new()
.add(method_args_1)
.make_call(Some(1), contract_address_1, method_name_1);
let wasm_bytes_2 = TestData::get_test_contract_code("basic_contract");
let contract_address_2 = [2u8; 32];
let method_args_2 = "arg".to_string();
let method_name_2 = "emit_event_with_return";
let command_2 = ArgsBuilder::new().add(method_args_2.clone()).make_call(
Some(2),
contract_address_2,
method_name_2,
);
let method_name_3 = "get_data_only";
let command_3 = ArgsBuilder::new().make_call(Some(3), contract_address_1, method_name_3);
let mut tx = TestData::transaction();
tx.gas_limit = 20_000_000;
let proposer_address = [4u8; 32];
let treasury_address = [100u8; 32];
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let init_from_balance = 500_000_000;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(
contract_address_1,
wasm_bytes_1,
pchain_runtime::cbi_version(),
);
sws.add_contract(
contract_address_2,
wasm_bytes_2,
pchain_runtime::cbi_version(),
);
tx.commands = vec![command_1.clone(), command_3.clone()];
let result =
pchain_runtime::Runtime::new().transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), tx.commands.len());
assert!(!receipt.iter().any(|r| r.exit_status != ExitStatus::Success));
let gas_consumed_1 = receipt[0].gas_used;
let gas_consumed_3 = receipt[1].gas_used;
tx.commands = vec![command_1.clone(), command_2.clone(), command_3.clone()];
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), tx.commands.len());
assert!(!receipt.iter().any(|r| r.exit_status != ExitStatus::Success));
let gas_consumed_2 = receipt[1].gas_used;
assert_eq!(gas_consumed_1, receipt[0].gas_used);
assert_ne!(gas_consumed_1, gas_consumed_2);
assert_eq!(gas_consumed_3, receipt[2].gas_used);
assert!(receipt[2].logs.is_empty());
let get_data_value: i32 = CallResult::parse(receipt[2].return_values.clone()).unwrap();
assert_eq!(method_args_1, get_data_value);
let sws: SimulateWorldState = result.new_state.into();
let data = sws.get_storage_data(contract_address_1, vec![0u8]);
assert_eq!(data, Some(123_i32.to_le_bytes().to_vec()));
let total_gas_used = gas_consumed_1 + gas_consumed_2 + gas_consumed_3 + tx_base_cost;
let new_from_balance = sws.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - 6 - base_fee_per_gas * total_gas_used
); assert_eq!(sws.get_nonce(from_address), 1);
assert_eq!(sws.get_balance(contract_address_1), 1 + 3);
assert_eq!(sws.get_balance(contract_address_2), 2);
assert_eq!(sws.get_nonce(contract_address_1), 0);
assert_eq!(sws.get_nonce(contract_address_2), 0);
assert_eq!(sws.get_balance(proposer_address), 0);
let treasury_balance = sws.get_balance(treasury_address);
let base_fee_to_treasury =
(total_gas_used * base_fee_per_gas * TREASURY_CUT_OF_BASE_FEE) / TOTAL_BASE_FEE;
assert_eq!(treasury_balance, base_fee_to_treasury);
}
#[test]
fn test_etoc_multiple_insufficient_gas() {
let wasm_bytes_1 = TestData::get_test_contract_code("all_features");
let contract_address_1 = [22u8; 32];
let command_1 =
ArgsBuilder::new()
.add(123_i32)
.make_call(Some(1), contract_address_1, "set_data_only");
let wasm_bytes_2 = TestData::get_test_contract_code("basic_contract");
let contract_address_2 = [2u8; 32];
let command_2 = ArgsBuilder::new().add("arg".to_string()).make_call(
Some(2),
contract_address_2,
"emit_event_with_return",
);
let command_3 = ArgsBuilder::new().make_call(Some(3), contract_address_1, "get_data_only");
let mut tx = TestData::transaction();
tx.gas_limit = 20_000_000;
let treasury_address = [100u8; 32];
let mut bd = TestData::block_params();
bd.proposer_address = [4u8; 32];
bd.treasury_address = treasury_address;
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let init_from_balance = 500_000_000;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(
contract_address_1,
wasm_bytes_1,
pchain_runtime::cbi_version(),
);
sws.add_contract(
contract_address_2,
wasm_bytes_2,
pchain_runtime::cbi_version(),
);
tx.commands = vec![command_1.clone(), command_2.clone(), command_3.clone()];
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let result =
pchain_runtime::Runtime::new().transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), tx.commands.len());
assert!(!receipt.iter().any(|r| r.exit_status != ExitStatus::Success));
let gas_consumed_1 = receipt[0].gas_used;
let gas_consumed_2 = receipt[1].gas_used;
let gas_consumed_3 = receipt[2].gas_used;
tx.gas_limit = tx_base_cost;
let result =
pchain_runtime::Runtime::new().transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), 1);
assert_eq!(
receipt.iter().last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(receipt.iter().last().unwrap().gas_used, 0);
let sws_1: SimulateWorldState = result.new_state.into();
let data = sws.get_storage_data(contract_address_1, vec![0u8]);
assert_eq!(data, None);
let total_gas_used = tx.gas_limit;
let new_from_balance = sws_1.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * total_gas_used
);
let treasury_balance = sws_1.get_balance(treasury_address);
let base_fee_to_treasury =
(total_gas_used * base_fee_per_gas * TREASURY_CUT_OF_BASE_FEE) / TOTAL_BASE_FEE;
assert_eq!(treasury_balance, base_fee_to_treasury);
tx.gas_limit = gas_consumed_1 + tx_base_cost;
let result =
pchain_runtime::Runtime::new().transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), 2);
assert_eq!(
receipt.iter().last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(receipt.iter().last().unwrap().gas_used, 0);
let sum_of_gas: u64 = receipt.iter().map(|r| r.gas_used).sum();
assert_eq!(sum_of_gas, gas_consumed_1);
let sws_2: SimulateWorldState = result.new_state.into();
let data = sws.get_storage_data(contract_address_1, vec![0u8]);
assert_eq!(data, None);
let total_gas_used = tx.gas_limit;
let new_from_balance = sws_2.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * total_gas_used
);
let treasury_balance = sws_2.get_balance(treasury_address);
let base_fee_to_treasury =
(total_gas_used * base_fee_per_gas * TREASURY_CUT_OF_BASE_FEE) / TOTAL_BASE_FEE;
assert_eq!(treasury_balance, base_fee_to_treasury);
tx.gas_limit = gas_consumed_1 + gas_consumed_2 + tx_base_cost;
let result =
pchain_runtime::Runtime::new().transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), 3);
assert_eq!(
receipt.iter().last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(receipt.iter().last().unwrap().gas_used, 0);
let sum_of_gas: u64 = receipt.iter().map(|r| r.gas_used).sum();
assert_eq!(sum_of_gas, gas_consumed_1 + gas_consumed_2);
let sws_3: SimulateWorldState = result.new_state.into();
let data = sws.get_storage_data(contract_address_1, vec![0u8]);
assert_eq!(data, None);
let total_gas_used = tx.gas_limit;
let new_from_balance = sws_3.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * total_gas_used
);
let treasury_balance = sws_3.get_balance(treasury_address);
let base_fee_to_treasury =
(total_gas_used * base_fee_per_gas * TREASURY_CUT_OF_BASE_FEE) / TOTAL_BASE_FEE;
assert_eq!(treasury_balance, base_fee_to_treasury);
tx.gas_limit = gas_consumed_1 + gas_consumed_2 + gas_consumed_3 + tx_base_cost - 1;
let result =
pchain_runtime::Runtime::new().transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.len(), 3);
assert_eq!(
receipt.iter().last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(receipt.iter().last().unwrap().gas_used, gas_consumed_3 - 1);
let sum_of_gas: u64 = receipt.iter().map(|r| r.gas_used).sum();
assert_eq!(
sum_of_gas,
gas_consumed_1 + gas_consumed_2 + gas_consumed_3 - 1
);
let sws_4: SimulateWorldState = result.new_state.into();
let data = sws.get_storage_data(contract_address_1, vec![0u8]);
assert_eq!(data, None);
let total_gas_used = tx.gas_limit;
let new_from_balance = sws_4.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * total_gas_used
);
let treasury_balance = sws_4.get_balance(treasury_address);
let base_fee_to_treasury =
(total_gas_used * base_fee_per_gas * TREASURY_CUT_OF_BASE_FEE) / TOTAL_BASE_FEE;
assert_eq!(treasury_balance, base_fee_to_treasury);
}
#[test]
fn test_etoc_panic() {
let wasm_bytes = TestData::get_test_contract_code("basic_contract");
let method_args = true; let method_name = "set_init_state_without_self";
let target = [2u8; 32];
let mut tx = TestData::transaction();
tx.commands =
vec![ArgsBuilder::new()
.add(method_args.clone())
.make_call(None, target, method_name)];
tx.gas_limit = 10_000_000;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let to_address = target;
let init_from_balance = 100_000_000;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(to_address, wasm_bytes, pchain_runtime::cbi_version());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
assert_eq!(result.error, Some(TransitionError::RuntimeError));
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Failed);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used
);
assert_eq!(sws.get_nonce(from_address), 1);
let new_to_balance = sws.get_balance(to_address);
assert_eq!(new_to_balance, 0);
assert_eq!(sws.get_nonce(to_address), 0);
}
#[test]
fn test_etoc_insufficient_gas() {
let wasm_bytes = TestData::get_test_contract_code("basic_contract");
let method_args = "arg".to_string();
let method_name = "emit_event_with_return";
let method_call_success_gas_consumption = 2_000_000;
let target = [2u8; 32];
let mut success_tx = TestData::transaction();
success_tx.commands =
vec![ArgsBuilder::new()
.add(method_args.clone())
.make_call(Some(1), target, method_name)];
success_tx.gas_limit = method_call_success_gas_consumption;
let tx_base_cost = tx_inclusion_cost(success_tx.serialize().len(), success_tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = success_tx.signer;
let to_address = target;
let init_from_balance = (method_call_success_gas_consumption + 1) * bd.this_base_fee;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(to_address, wasm_bytes, pchain_runtime::cbi_version());
let result =
pchain_runtime::Runtime::new().transition(sws.world_state, success_tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let mut sws: SimulateWorldState = result.new_state.into();
let method_call_theoretical_gas_consumption = receipt.last().unwrap().gas_used + tx_base_cost;
let init_from_balance = method_call_theoretical_gas_consumption * bd.this_base_fee;
sws.set_balance(from_address, init_from_balance);
sws.set_balance(to_address, 0);
let tx = Transaction {
gas_limit: method_call_theoretical_gas_consumption - 1,
nonce: success_tx.nonce + 1,
..success_tx
};
let tx_gas_limit = tx.gas_limit;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(
receipt.last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(
receipt.last().unwrap().gas_used,
tx_gas_limit - tx_base_cost
);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(from_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used
);
assert_eq!(sws.get_nonce(from_address), 2);
let new_to_balance = sws.get_balance(to_address);
assert_eq!(new_to_balance, 0);
assert_eq!(sws.get_nonce(to_address), 0);
assert!(!receipt.last().unwrap().logs.is_empty());
}
#[test]
fn test_etoc_host_functions() {
let wasm_bytes = TestData::get_test_contract_code("all_features");
let target = [2u8; 32];
let mut tx = TestData::transaction();
tx.gas_limit = 10_000_000;
tx.commands = vec![ArgsBuilder::new().make_call(None, target, "about")];
let bd = TestData::block_params();
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let to_address = target;
let init_from_balance = 100_000_000;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(to_address, wasm_bytes, pchain_runtime::cbi_version());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
}
#[test]
fn test_ctoc() {
let wasm_bytes_1 = TestData::get_test_contract_code("all_features");
let wasm_bytes_2 = TestData::get_test_contract_code("all_features");
let contract_address_1 = [49u8; 32];
let contract_address_2 = [50u8; 32];
let value_to_contract_2 = 10;
let value_to_contract_1 = 5;
let data_only = 1234_i32;
let function_args = borsh::BorshSerialize::try_to_vec(&Vec::<Vec<u8>>::new()).unwrap();
let mut tx = TestData::transaction();
tx.commands = vec![ArgsBuilder::new()
.add(contract_address_1)
.add("get_data_only".to_string()) .add(function_args)
.add(value_to_contract_1) .add(1usize)
.make_call(
Some(value_to_contract_2),
contract_address_2,
"call_other_contract",
)];
tx.gas_limit = 100_000_000;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let from_address = tx.signer;
let to_address = contract_address_2;
let init_from_balance = 1_000_000_000;
sws.set_balance(from_address, init_from_balance);
sws.add_contract(
contract_address_1,
wasm_bytes_1,
pchain_runtime::cbi_version(),
);
sws.add_contract(
contract_address_2,
wasm_bytes_2,
pchain_runtime::cbi_version(),
);
sws.set_storage_data(
contract_address_1,
vec![0u8],
data_only.to_le_bytes().to_vec(),
);
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(from_address.clone());
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used - value_to_contract_2
);
assert_eq!(sws.get_nonce(from_address), 1);
let new_to_balance = sws.get_balance(to_address.clone());
assert_eq!(new_to_balance, value_to_contract_2 - value_to_contract_1);
assert_eq!(sws.get_nonce(to_address), 0);
let result_value: Vec<u8> =
CallResult::parse(receipt.last().unwrap().return_values.clone()).unwrap();
assert_eq!(result_value, data_only.to_le_bytes().to_vec());
}
#[test]
fn test_ctoe() {
let wasm_bytes = TestData::get_test_contract_code("all_features");
let origin_address = [1u8; 32];
let init_from_balance = 100_000_000;
let contract_addr = [2u8; 32];
let value_to_contract = 100_000;
let receiver_address = [3u8; 32];
let value_to_receiver: u64 = 90_000;
let mut tx = TestData::transaction();
tx.commands = vec![ArgsBuilder::new()
.add(receiver_address)
.add(value_to_receiver)
.make_call(
Some(value_to_contract),
contract_addr,
"make_balance_transfer",
)];
tx.gas_limit = 10_000_000;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
sws.set_balance(origin_address, init_from_balance);
sws.add_contract(contract_addr, wasm_bytes, pchain_runtime::cbi_version());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(origin_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used - value_to_contract
);
assert_eq!(sws.get_nonce(origin_address), 1);
let new_to_balance = sws.get_balance(contract_addr);
assert_eq!(new_to_balance, value_to_contract - value_to_receiver);
assert_eq!(sws.get_nonce(contract_addr), 0);
let new_receiver_balance = sws.get_balance(receiver_address);
assert_eq!(new_receiver_balance, value_to_receiver);
assert_eq!(sws.get_nonce(receiver_address), 0);
let result_value: u64 =
CallResult::parse(receipt.last().unwrap().return_values.clone()).unwrap();
assert_eq!(result_value, value_to_contract - value_to_receiver);
}
#[test]
fn test_deploy() {
let wasm_bytes = TestData::get_test_contract_code("basic_contract");
let origin_address = [1u8; 32];
let contract_address: PublicAddress = compute_contract_address(origin_address, 0);
let mut tx = TestData::transaction();
tx.commands = vec![ArgsBuilder::new().make_deploy(wasm_bytes, 0)];
tx.gas_limit = 400_000_000;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let init_from_balance = 5_000_000_000;
sws.set_balance(origin_address, init_from_balance);
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(origin_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used
);
assert_eq!(sws.get_nonce(origin_address), 1);
let new_to_balance = sws.get_balance(contract_address);
assert_eq!(new_to_balance, 0);
assert_eq!(sws.get_nonce(contract_address), 0);
assert!(sws.get_contract_code(contract_address).is_some());
}
#[test]
fn test_deploy_invalid_entrypoint_contract() {
let wasm_bytes = TestData::get_test_contract_code("invalid_entrypoint_contract");
let origin_address = [1u8; 32];
let contract_address: PublicAddress = compute_contract_address(origin_address, 0);
let mut tx = TestData::transaction();
tx.commands = vec![ArgsBuilder::new().make_deploy(wasm_bytes, 0)];
tx.gas_limit = 20_500_000;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let init_from_balance = 500_000_000;
sws.set_balance(origin_address, init_from_balance);
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
assert_eq!(
result.error,
Some(TransitionError::NoExportedContractMethod)
);
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Failed);
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let sws: SimulateWorldState = result.new_state.into();
assert!(sws.get_contract_code(contract_address).is_none());
assert_eq!(
sws.get_balance(origin_address),
init_from_balance - base_fee_per_gas * gas_used
);
}
#[test]
fn test_deploy_contract_with_invalid_opcode() {
let wasm_bytes = TestData::get_test_contract_code("invalid_non_deterministic");
let origin_address = [1u8; 32];
let mut tx = TestData::transaction();
tx.signer = origin_address;
tx.commands = vec![ArgsBuilder::new().make_deploy(wasm_bytes, 0)];
tx.gas_limit = 20_000_000;
let bd = TestData::block_params();
let mut sws = SimulateWorldState::default();
let init_from_balance = 500_000_000;
sws.set_balance(origin_address, init_from_balance);
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
assert_eq!(result.error, Some(TransitionError::DisallowedOpcode));
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Failed);
}
#[test]
fn test_deploy_insufficient_gas() {
let wasm_bytes = TestData::get_test_contract_code("basic_contract");
let method_call_success_gas_consumption = 200_000_000;
let origin_address = [1u8; 32];
let contract_address: PublicAddress = compute_contract_address(origin_address, 0);
let mut success_tx = TestData::transaction();
success_tx.commands = vec![ArgsBuilder::new().make_deploy(wasm_bytes, 0)];
success_tx.gas_limit = method_call_success_gas_consumption;
let tx_base_cost = tx_inclusion_cost(success_tx.serialize().len(), success_tx.commands.len());
let bd = TestData::block_params();
let base_fee_per_gas = bd.this_base_fee;
let mut sws = SimulateWorldState::default();
let init_from_balance = (method_call_success_gas_consumption + 1) * bd.this_base_fee;
sws.set_balance(origin_address, init_from_balance);
let result =
pchain_runtime::Runtime::new().transition(sws.world_state, success_tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let mut sws: SimulateWorldState = result.new_state.into();
let method_call_theoretical_gas_consumption = receipt.last().unwrap().gas_used + tx_base_cost;
let init_from_balance = method_call_theoretical_gas_consumption * bd.this_base_fee;
sws.set_balance(origin_address, init_from_balance);
let tx = Transaction {
gas_limit: method_call_theoretical_gas_consumption - 1,
nonce: success_tx.nonce + 1,
..success_tx
};
let tx_gas_limit = tx.gas_limit;
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx, bd);
assert_eq!(
result.error,
Some(TransitionError::ExecutionProperGasExhausted)
);
let receipt = result.receipt.unwrap();
assert_eq!(
receipt.last().unwrap().exit_status,
ExitStatus::GasExhausted
);
assert_eq!(
receipt.last().unwrap().gas_used,
tx_gas_limit - tx_base_cost
);
let sws: SimulateWorldState = result.new_state.into();
let gas_used = receipt.last().unwrap().gas_used + tx_base_cost;
let new_from_balance = sws.get_balance(origin_address);
assert_eq!(
new_from_balance,
init_from_balance - base_fee_per_gas * gas_used
);
assert_eq!(sws.get_nonce(origin_address), 2);
let new_to_balance = sws.get_balance(contract_address);
assert_eq!(new_to_balance, 0);
assert_eq!(sws.get_nonce(contract_address), 0);
assert!(receipt.last().unwrap().logs.is_empty());
}
#[test]
fn test_memory_limited_contract_module() {
let wasm_bytes = TestData::get_test_contract_code("basic_contract");
let mut tx = TestData::transaction();
tx.commands = vec![ArgsBuilder::new().make_deploy(wasm_bytes, 0)];
tx.gas_limit = 400_000_000;
let bd = TestData::block_params();
let mut sws = SimulateWorldState::default();
sws.set_balance(tx.signer, 5_000_000_000);
let runtime = pchain_runtime::Runtime::new().set_smart_contract_memory_limit(100 * 1024 * 1024);
let result = runtime.transition(sws.world_state.clone(), tx.clone(), bd.clone());
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Success);
let runtime = pchain_runtime::Runtime::new().set_smart_contract_memory_limit(1024);
let result = runtime.transition(sws.world_state.clone(), tx, bd);
assert_eq!(result.error, Some(TransitionError::CannotCompile));
let receipt = result.receipt.unwrap();
assert_eq!(receipt.last().unwrap().exit_status, ExitStatus::Failed);
}
#[test]
fn test_fail_in_pre_charge() {
let tx = TestData::transaction();
let tx_base_cost = tx_inclusion_cost(tx.serialize().len(), tx.commands.len());
let bd = TestData::block_params();
let mut sws = SimulateWorldState::default();
let init_from_balance = 100_000_000;
sws.set_balance(tx.signer, init_from_balance);
let tx1 = Transaction {
gas_limit: tx_base_cost - 1,
..tx.clone()
};
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx1, bd.clone());
assert!(result.receipt.is_none());
assert_eq!(
result.error,
Some(TransitionError::PreExecutionGasExhausted)
);
let sws: SimulateWorldState = result.new_state.into();
let tx2 = Transaction {
nonce: 1,
..tx.clone()
};
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx2, bd.clone());
assert!(result.receipt.is_none());
assert_eq!(result.error, Some(TransitionError::WrongNonce));
let sws: SimulateWorldState = result.new_state.into();
let tx3 = Transaction {
priority_fee_per_gas: u64::MAX,
..tx.clone()
};
let result = pchain_runtime::Runtime::new().transition(sws.world_state, tx3, bd.clone());
assert!(result.receipt.is_none());
assert_eq!(
result.error,
Some(TransitionError::NotEnoughBalanceForGasLimit)
);
let sws: SimulateWorldState = result.new_state.into();
let new_from_balance = sws.get_balance(tx.signer);
assert_eq!(new_from_balance, init_from_balance);
assert_eq!(sws.get_nonce(tx.signer), 0);
}