From f9780590a660ae9f51cab15d037ac9ceb39a0bd4 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Tue, 14 Jul 2026 14:26:15 +0300 Subject: [PATCH 1/3] added functions for op_return; added run_with_op_return to helpers --- simf/lib/op_return.simf | 10 +++ simf/op_return_test.simf | 22 +++++++ tests/common/core.rs | 75 ++++++++++++++++++---- tests/op_return_test.rs | 130 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 simf/lib/op_return.simf create mode 100644 simf/op_return_test.simf create mode 100644 tests/op_return_test.rs diff --git a/simf/lib/op_return.simf b/simf/lib/op_return.simf new file mode 100644 index 0000000..21fe7e3 --- /dev/null +++ b/simf/lib/op_return.simf @@ -0,0 +1,10 @@ +pub fn is_output_op_return(output_index: u32) -> bool { + match jet::output_null_datum(output_index, 0) { + Some(entry: Option>>) => true, + None => false, + } +} + +pub fn assert_output_is_op_return(index: u32) { + assert!(is_output_op_return(index)); +} diff --git a/simf/op_return_test.simf b/simf/op_return_test.simf new file mode 100644 index 0000000..eb23ef6 --- /dev/null +++ b/simf/op_return_test.simf @@ -0,0 +1,22 @@ +use crate::lib::op_return::{is_output_op_return, assert_output_is_op_return}; +use crate::helper::{if_test_this_function}; +use crate::lib::logical_operations::not; + +// to do: import from helper when pr #21 is merged +/// Asserts a result equals the expected bool value. +pub fn assert_bool(result: bool, expected_bool: bool) { + match expected_bool { + true => assert!(result), + false => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let expected: bool = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_output_op_return(index), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_output_is_op_return(index); }, false => (), }; +} diff --git a/tests/common/core.rs b/tests/common/core.rs index 9b4d8e2..ada5e42 100644 --- a/tests/common/core.rs +++ b/tests/common/core.rs @@ -4,7 +4,9 @@ use simplex::program::{Program, WitnessTrait}; use simplex::simplicityhl::elements::Script; -use simplex::transaction::{FinalTransaction, PartialInput, ProgramInput, RequiredSignature}; +use simplex::transaction::{ + FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, +}; #[derive(Clone, Copy)] pub enum Expect { @@ -41,13 +43,14 @@ pub fn fund( Ok(script) } -/// Spend the funded UTXO with `witness`. Returns the broadcast result. -pub fn spend( +/// Construct the funded UTXO with `witness`. +pub fn construct_final_tx( context: &simplex::TestContext, program: &impl AsRef, script: &Script, witness: W, -) -> anyhow::Result + data: Option<&[u8]>, +) -> anyhow::Result where W: WitnessTrait + 'static, { @@ -62,22 +65,34 @@ where RequiredSignature::None, ); - Ok(context.get_default_signer().broadcast(&ft)?.to_string()) + if let Some(data) = data { + ft.add_output(PartialOutput::new_metadata(data)) + }; + + Ok(ft) } -/// Fund + spend + assert the outcome. -pub fn run( +/// Spend the funded UTXO with `witness`. Return the broadcast result. +pub fn spend( context: &simplex::TestContext, - program: impl AsRef, + program: &impl AsRef, + script: &Script, witness: W, - expect: Expect, -) -> anyhow::Result<()> + data: Option<&[u8]>, +) -> anyhow::Result where W: WitnessTrait + 'static, { - let script = fund(context, &program)?; - let result = spend(context, &program, &script, witness); + let ft = construct_final_tx(context, program, script, witness, data)?; + + Ok(context.get_default_signer().broadcast(&ft)?.to_string()) +} +/// Assert that the test result is as expected. +pub fn assert_error_msg( + result: Result, + expect: Expect, +) -> anyhow::Result<()> { match expect.error_message() { None => { result?; @@ -88,7 +103,41 @@ where .to_string(); assert!(err.contains(expected)); } - } + }; Ok(()) } + +/// Fund + spend + assert the outcome. +pub fn run( + context: &simplex::TestContext, + program: impl AsRef, + witness: W, + expect: Expect, +) -> anyhow::Result<()> +where + W: WitnessTrait + 'static, +{ + let script = fund(context, &program)?; + let result = spend(context, &program, &script, witness, None); + + assert_error_msg(result, expect) +} + +/// Fund + spend + assert the outcome. +/// Tx has OP_RETURN data metadata output +pub fn run_with_op_return( + context: &simplex::TestContext, + program: impl AsRef, + witness: W, + expect: Expect, + data: &[u8], +) -> anyhow::Result<()> +where + W: WitnessTrait + 'static, +{ + let script = fund(context, &program)?; + let result = spend(context, &program, &script, witness, Some(data)); + + assert_error_msg(result, expect) +} diff --git a/tests/op_return_test.rs b/tests/op_return_test.rs new file mode 100644 index 0000000..917bd49 --- /dev/null +++ b/tests/op_return_test.rs @@ -0,0 +1,130 @@ +mod common; + +use rand::Rng; + +use crate::common::core::{run, run_with_op_return}; +use common::core::Expect; + +use simplicityhl_std::artifacts::op_return_test::OpReturnTestProgram; +use simplicityhl_std::artifacts::op_return_test::derived_op_return_test::{ + OpReturnTestArguments, OpReturnTestWitness, +}; + +enum FunctionToTest { + IsOpReturn, + AssertOutputIsOpReturn, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_BOOL: bool = false; +const DEFAULT_DATA: &[u8; 1] = &[1]; + +fn program() -> OpReturnTestProgram { + OpReturnTestProgram::new(OpReturnTestArguments {}) +} + +fn build_witness(function: u8, index: u32, expected: bool) -> OpReturnTestWitness { + OpReturnTestWitness { + function_index: function, + index, + expected, + } +} + +mod op_return_tests { + use super::*; + + #[simplex::test] + fn is_output_op_return_true(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + run_with_op_return( + &context, + program(), + build_witness(op(FunctionToTest::IsOpReturn), index, true), + Expect::Ok, + DEFAULT_DATA, + ) + } + + #[simplex::test] + fn is_output_op_return_empty_index_false(context: simplex::TestContext) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(1..=u32::MAX); + + run_with_op_return( + &context, + program(), + build_witness(op(FunctionToTest::IsOpReturn), index, false), + Expect::Ok, + DEFAULT_DATA, + ) + } + + #[simplex::test] + fn is_output_op_return_false(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::IsOpReturn), index, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_output_is_op_return_pass(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + run_with_op_return( + &context, + program(), + build_witness( + op(FunctionToTest::AssertOutputIsOpReturn), + index, + DEFAULT_BOOL, + ), + Expect::Ok, + DEFAULT_DATA, + ) + } + + #[simplex::test] + fn assert_output_is_op_return_empty_index_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(1..=u32::MAX); + + run_with_op_return( + &context, + program(), + build_witness( + op(FunctionToTest::AssertOutputIsOpReturn), + index, + DEFAULT_BOOL, + ), + Expect::AssertFailed, + DEFAULT_DATA, + ) + } + + #[simplex::test] + fn assert_output_is_op_return_fail(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::AssertOutputIsOpReturn), + index, + DEFAULT_BOOL, + ), + Expect::AssertFailed, + ) + } +} From e9cb99f0f60df604794157af149c95916cc5cc72 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Wed, 15 Jul 2026 12:26:05 +0300 Subject: [PATCH 2/3] fixed todo --- simf/op_return_test.simf | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/simf/op_return_test.simf b/simf/op_return_test.simf index eb23ef6..e80aaca 100644 --- a/simf/op_return_test.simf +++ b/simf/op_return_test.simf @@ -1,16 +1,7 @@ use crate::lib::op_return::{is_output_op_return, assert_output_is_op_return}; -use crate::helper::{if_test_this_function}; +use crate::helper::{if_test_this_function, assert_bool}; use crate::lib::logical_operations::not; -// to do: import from helper when pr #21 is merged -/// Asserts a result equals the expected bool value. -pub fn assert_bool(result: bool, expected_bool: bool) { - match expected_bool { - true => assert!(result), - false => assert!(not(result)), - } -} - fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; From 5f3ccdc753425b39a8a6ce5d188fb7a4dfbfcb97 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Wed, 15 Jul 2026 13:17:07 +0300 Subject: [PATCH 3/3] added documentation for op_return functions --- simf/lib/op_return.simf | 6 ++++-- simf/op_return_test.simf | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/simf/lib/op_return.simf b/simf/lib/op_return.simf index 21fe7e3..2f71087 100644 --- a/simf/lib/op_return.simf +++ b/simf/lib/op_return.simf @@ -1,3 +1,4 @@ +/// Returns true if the specified output is an OP_RETURN (null data) output pub fn is_output_op_return(output_index: u32) -> bool { match jet::output_null_datum(output_index, 0) { Some(entry: Option>>) => true, @@ -5,6 +6,7 @@ pub fn is_output_op_return(output_index: u32) -> bool { } } -pub fn assert_output_is_op_return(index: u32) { - assert!(is_output_op_return(index)); +/// Asserts that the specified output is an OP_RETURN (null data) output +pub fn assert_output_is_op_return(output_index: u32) { + assert!(is_output_op_return(output_index)); } diff --git a/simf/op_return_test.simf b/simf/op_return_test.simf index e80aaca..d847d8e 100644 --- a/simf/op_return_test.simf +++ b/simf/op_return_test.simf @@ -1,6 +1,5 @@ use crate::lib::op_return::{is_output_op_return, assert_output_is_op_return}; use crate::helper::{if_test_this_function, assert_bool}; -use crate::lib::logical_operations::not; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX;