use std::iter;
use ootle_byte_type::ToByteType;
use rand::random;
use tari_engine::transaction::TransactionErrorKind;
use tari_engine_types::{
commit_result::{RejectReason, TransactionResult},
hashing::hash_template_code,
limits,
published_template::{PublishedTemplateAddress, TemplateBlob},
substate::{SubstateId, SubstateValue},
};
use tari_ootle_transaction::Transaction;
use tari_template_test_tooling::{
TemplateTest,
compile::compile_template,
support::assert_error::assert_reject_reason,
};
const CRATE_PATH: &str = env!("CARGO_MANIFEST_DIR");
#[test]
fn publish_template_success() {
let mut test = TemplateTest::new(CRATE_PATH, &[] as &[&str]);
let (account_address, owner_proof, account_key, public_key) = test.create_funded_account_with_keypair();
let template = compile_template("tests/templates/hello_world", &[]).unwrap();
let expected_binary_hash = hash_template_code(template.code());
let expected_template_address =
PublishedTemplateAddress::from_author_and_binary_hash(&public_key.to_byte_type(), &expected_binary_hash);
let result = test.execute_expect_success(
Transaction::builder_localnet()
.pay_fee_from_component(account_address, 200_000u64)
.publish_template(template.into_code())
.build_and_seal(&account_key),
vec![owner_proof],
);
assert!(matches!(result.finalize.result, TransactionResult::Accept(_)));
let mut template_found = false;
let diff = result.expect_success();
diff.up_iter().for_each(|(substate_id, substate)| {
if let SubstateValue::Template(curr_template) = substate.substate_value() &&
curr_template.to_binary_hash() == expected_binary_hash
{
template_found = true;
assert!(matches!(substate_id, SubstateId::Template(_)));
if let SubstateId::Template(curr_template_addr) = substate_id {
assert_eq!(expected_template_address, *curr_template_addr);
}
}
});
assert!(template_found);
}
#[test]
fn publish_template_invalid_binary() {
let mut test = TemplateTest::new(CRATE_PATH, &[] as &[&str]);
let (account_address, owner_proof, account_key, _) = test.create_funded_account_with_keypair();
let result = test.execute_expect_failure(
Transaction::builder_localnet()
.pay_fee_from_component(account_address, 200_000u64)
.publish_template(vec![1u8, 2, 3])
.build_and_seal(&account_key),
vec![owner_proof],
);
assert!(matches!(result, RejectReason::ExecutionFailure(_)));
if let RejectReason::ExecutionFailure(error) = result {
assert!(error.starts_with("At instruction #1: Load template error:"));
}
}
#[test]
fn publish_template_too_big_binary() {
let mut test = TemplateTest::new(CRATE_PATH, &[] as &[&str]);
let (account_address, owner_proof, account_key, _) = test.create_funded_account_with_keypair();
let random_wasm_binary = generate_random_binary(limits::ENGINE_LIMITS.max_template_binary_size_bytes + 1);
let wasm_binary_size = random_wasm_binary.len();
let reason = test.execute_expect_failure(
Transaction::builder_localnet()
.pay_fee_from_component(account_address, 200_000u64)
.publish_template(unsafe { TemplateBlob::new_unchecked(random_wasm_binary) })
.build_and_seal(&account_key),
vec![owner_proof],
);
assert_reject_reason(reason, TransactionErrorKind::WasmBinaryTooBig {
size: wasm_binary_size,
max: limits::ENGINE_LIMITS.max_template_binary_size_bytes,
});
}
#[test]
fn rejects_more_than_one_publish_template() {
let mut test = TemplateTest::new(CRATE_PATH, &[] as &[&str]);
let (account_address, owner_proof, account_key, _) = test.create_funded_account_with_keypair();
let mut builder = Transaction::builder_localnet().pay_fee_from_component(account_address, 200_000u64);
for i in 0..=limits::MAX_PUBLISH_TEMPLATES_PER_TRANSACTION {
builder = builder.publish_template(vec![i as u8]);
}
let reason = test.execute_expect_failure(builder.build_and_seal(&account_key), vec![owner_proof]);
let RejectReason::ExecutionFailure(error) = reason else {
panic!("expected ExecutionFailure, got {reason:?}");
};
assert!(
error.contains("publish-template instructions") &&
error.contains(&format!(
"the maximum allowed is {}",
limits::MAX_PUBLISH_TEMPLATES_PER_TRANSACTION
)),
"unexpected reject reason: {error}"
);
}
fn generate_random_binary(size_in_bytes: usize) -> Vec<u8> {
iter::repeat_with(random).take(size_in_bytes).collect()
}