diff --git a/crates/engine/src/transaction/error.rs b/crates/engine/src/transaction/error.rs index e524dfcbd6..dfe017d97f 100644 --- a/crates/engine/src/transaction/error.rs +++ b/crates/engine/src/transaction/error.rs @@ -45,6 +45,8 @@ pub enum TransactionError { InvariantError { details: String }, #[error("Load template error: {0}")] LoadTemplate(#[from] TemplateLoaderError), + #[error("WASM binary too big! {size} bytes are greater than allowed maximum {max} bytes.")] + WasmBinaryTooBig { size: usize, max: usize }, #[error("Template provider error: {0}")] TemplateProvider(String), #[error("Converting to hash error: {0}")] diff --git a/crates/engine/src/transaction/processor.rs b/crates/engine/src/transaction/processor.rs index ea3e7667e6..f9c12dd656 100644 --- a/crates/engine/src/transaction/processor.rs +++ b/crates/engine/src/transaction/processor.rs @@ -30,6 +30,7 @@ use tari_engine_types::{ entity_id_provider::EntityIdProvider, indexed_value::{IndexedValue, IndexedWellKnownTypes}, instruction_result::InstructionResult, + limits, lock::LockFlag, virtual_substate::VirtualSubstates, }; @@ -449,6 +450,15 @@ impl + 'static> T /// Load, validate template binary and adds it to TemplateProvider. fn publish_template(runtime: &Runtime, binary: TemplateBlob) -> Result { + if binary.len() > limits::ENGINE_LIMITS.max_template_binary_size_bytes { + // Technically, not possible, but this check is kept in to make a test pass, and potentially for additional + // safety. + return Err(TransactionError::WasmBinaryTooBig { + size: binary.len(), + max: limits::ENGINE_LIMITS.max_template_binary_size_bytes, + }); + } + // validate binary WasmModule::load_template_from_code(&binary)?; // creating new substate diff --git a/crates/engine/tests/publish_template.rs b/crates/engine/tests/publish_template.rs index 9bf018f789..9c7e86c739 100644 --- a/crates/engine/tests/publish_template.rs +++ b/crates/engine/tests/publish_template.rs @@ -4,15 +4,16 @@ use std::iter; use rand::random; -use tari_engine::wasm::compile::compile_template; +use tari_engine::{transaction::TransactionError, wasm::compile::compile_template}; use tari_engine_types::{ commit_result::{RejectReason, TransactionResult}, hashing::{hash_template_code, hasher32, EngineHashDomainLabel}, + limits, published_template::PublishedTemplateAddress, substate::{SubstateId, SubstateValue}, }; -use tari_template_test_tooling::TemplateTest; -use tari_transaction::Transaction; +use tari_template_test_tooling::{support::assert_error::assert_reject_reason, TemplateTest}; +use tari_transaction::{TemplateBlob, Transaction}; #[test] fn publish_template_success() { @@ -78,27 +79,21 @@ fn publish_template_invalid_binary() { fn publish_template_too_big_binary() { let mut test = TemplateTest::new(Vec::::new()); let (account_address, owner_proof, account_key, _) = test.create_custom_funded_account(250_000); - let random_wasm_binary = generate_random_binary(6 * 1000 * 1000); // 6 MB + 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 result = test.execute_expect_failure( + let reason = test.execute_expect_failure( Transaction::builder() .fee_transaction_pay_from_component(account_address, 200_000) - .publish_template(random_wasm_binary.try_into().unwrap()) + // SAFETY: We are intentionally publishing an oversized binary to test size limits. + .publish_template(unsafe { TemplateBlob::new_unchecked(random_wasm_binary) }) .build_and_seal(&account_key), vec![owner_proof], ); - assert!(matches!(result, RejectReason::ExecutionFailure(_))); - - if let RejectReason::ExecutionFailure(error) = result { - assert_eq!( - error, - format!( - "WASM binary too big! {} bytes are greater than allowed maximum 5000000 bytes.", - wasm_binary_size - ) - ); - } + assert_reject_reason(reason, TransactionError::WasmBinaryTooBig { + size: wasm_binary_size, + max: limits::ENGINE_LIMITS.max_template_binary_size_bytes, + }); } fn generate_random_binary(size_in_bytes: usize) -> Vec { diff --git a/crates/template_lib_types/src/max_bytes.rs b/crates/template_lib_types/src/max_bytes.rs index 8e626c85a2..fdb180374b 100644 --- a/crates/template_lib_types/src/max_bytes.rs +++ b/crates/template_lib_types/src/max_bytes.rs @@ -32,6 +32,16 @@ impl MaxBytes { } } + /// Constructs a new `MaxBytes` without checking the length of the input. + /// This is the only way to break the invariant guarantees of `MaxBytes`. + /// NOTE: this exists for testing purposes and should not be used in general. + /// + /// # Safety + /// The caller must ensure that the length of `bytes` is less than or equal to `N`. + pub unsafe fn new_unchecked(bytes: impl Into>) -> Self { + Self { bytes: bytes.into() } + } + pub fn into_vec(self) -> Vec { self.bytes.into_vec() }