From 59127a20fd479e14f7580180bb1ab31753007f96 Mon Sep 17 00:00:00 2001 From: Oleh Komendant Date: Thu, 28 May 2026 18:18:38 +0300 Subject: [PATCH] Remove owner signature from the IssuanceFactory covenant --- crates/contracts/simf/issuance_factory.simf | 153 +++++++++++++----- .../src/programs/issuance_factory/core.rs | 32 ++-- .../src/programs/issuance_factory/metadata.rs | 32 +--- .../src/programs/issuance_factory/params.rs | 4 +- .../src/programs/issuance_factory/witness.rs | 13 +- crates/contracts/src/programs/lending/core.rs | 2 +- .../creation_metadata_success_flow.rs | 19 +-- .../issue_assets_failure_flows.rs | 74 ++++++--- .../issue_assets_success_flows.rs | 131 +++++++++------ .../remove_factory_failure_flows.rs | 14 +- .../remove_factory_success_flows.rs | 11 +- .../contracts/tests/issuance_factory/setup.rs | 24 +-- .../lending/creation_metadata_success_flow.rs | 6 +- crates/contracts/tests/lending/setup.rs | 32 ++-- 14 files changed, 335 insertions(+), 212 deletions(-) diff --git a/crates/contracts/simf/issuance_factory.simf b/crates/contracts/simf/issuance_factory.simf index 7d9c09d..8a2fc92 100644 --- a/crates/contracts/simf/issuance_factory.simf +++ b/crates/contracts/simf/issuance_factory.simf @@ -9,7 +9,7 @@ fn get_script_hash(index: u32, is_input_index: bool) -> u256 { script_hash } -fn get_asset_and_amount(index: u32, is_input_index: bool) -> (u256, u64) { +fn get_explicit_asset_and_amount(index: u32, is_input_index: bool) -> (u256, u64) { let pair: (Asset1, Amount1) = match is_input_index { true => unwrap(jet::input_amount(index)), false => unwrap(jet::output_amount(index)), @@ -20,6 +20,20 @@ fn get_asset_and_amount(index: u32, is_input_index: bool) -> (u256, u64) { (asset_bits, amount) } +fn get_current_explicit_asset() -> u256 { + let asset: Asset1 = jet::current_asset(); + let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset); + + asset_bits +} + +fn is_op_return(output_index: u32) -> bool { + match jet::output_null_datum(output_index, 0) { + Some(entry: Option>>) => true, + None => false, + } +} + // Check helpers fn check_asset_amounts_eq(asset_amount_1: u64, asset_amount_2: u64) { @@ -38,37 +52,61 @@ fn check_flags_eq(flag_1: bool, flag_2: bool) { assert!(jet::eq_1(::into(flag_1), ::into(flag_2))); } +// Ensure functions + +fn ensure_output_is_op_return(index: u32) { + check_flags_eq(is_op_return(index), true); +} + +fn ensure_io_script_hashes_eq(input_index: u32, output_index: u32) { + let input_script_hash: u256 = get_script_hash(input_index, true); + let output_script_hash: u256 = get_script_hash(output_index, false); + + check_script_hashes_eq(input_script_hash, output_script_hash); +} + fn ensure_non_zero_amount(amount: u64) { assert!(jet::some_64(amount)); } -fn ensure_asset_with_amount(index: u32, is_input_index: bool, expected_asset_bits: u256, expected_amount: u64) { - let (asset_bits, amount): (u256, u64) = get_asset_and_amount(index, is_input_index); +fn ensure_output_asset_and_amount(output_index: u32, expected_asset_bits: u256, expected_amount: u64) { + let (asset_bits, amount): (u256, u64) = get_explicit_asset_and_amount(output_index, false); check_assets_eq(asset_bits, expected_asset_bits); check_asset_amounts_eq(amount, expected_amount); } -fn ensure_input_and_output_eq(input_index: u32, output_index: u32) { - let (input_asset_bits, input_amount): (u256, u64) = get_asset_and_amount(input_index, true); - let (output_asset_bits, output_amount): (u256, u64) = get_asset_and_amount(output_index, false); +fn ensure_io_asset_eq(input_index: u32, output_index: u32, expected_asset_bits: u256) -> (u64, u64) { + let (input_asset_bits, input_amount): (u256, u64) = get_explicit_asset_and_amount(input_index, true); + let (output_asset_bits, output_amount): (u256, u64) = get_explicit_asset_and_amount(output_index, false); - check_assets_eq(input_asset_bits, input_asset_bits); - check_asset_amounts_eq(input_amount, output_amount); + check_assets_eq(input_asset_bits, expected_asset_bits); + check_assets_eq(input_asset_bits, output_asset_bits); + + (input_amount, output_amount) } -fn ensure_input_and_output_script_hashes_eq(input_index: u32, output_index: u32) { - let input_script_hash: u256 = get_script_hash(input_index, true); - let output_script_hash: u256 = get_script_hash(output_index, false); +fn ensure_io_asset_and_amount_eq( + input_index: u32, + output_index: u32, + expected_asset_bits: u256, + expected_amount: u64 +) { + let (input_amount, output_amount): (u64, u64) = ensure_io_asset_eq(input_index, output_index, expected_asset_bits); - check_script_hashes_eq(input_script_hash, output_script_hash); + check_asset_amounts_eq(input_amount, expected_amount); + check_asset_amounts_eq(input_amount, output_amount); } -fn ensure_output_is_op_return(index: u32) { - match jet::output_null_datum(index, 0) { - Some(entry: Option>>) => (), - None => panic!(), - } +fn ensure_input_asset_and_amount_burn( + input_index: u32, + output_index: u32, + expected_asset_bits: u256, + expected_asset_amount: u64 +) { + ensure_io_asset_and_amount_eq(input_index, output_index, expected_asset_bits, expected_asset_amount); + + ensure_output_is_op_return(output_index); } fn ensure_zero_bit(bit: bool) { assert!(jet::eq_1(::into(bit), 0)); } @@ -78,10 +116,12 @@ fn ensure_zero_bit(bit: bool) { assert!(jet::eq_1(::into(bit), 0)); } fn safe_add_32(first: u32, second: u32) -> u32 { let (carry, result): (bool, u32) = jet::add_32(first, second); + check_flags_eq(carry, false); + result } -// Main paths logic +// Main paths helpers fn get_reissuance_flag(index: u8) -> bool { let shifted: u64 = jet::right_shift_64(index, param::REISSUANCE_FLAGS); @@ -95,7 +135,9 @@ fn verify_issuance(input_index: u32, output_index: u32, reissuance_flag: bool) { let outpoint: Outpoint = unwrap(jet::input_prev_outpoint(input_index)); let issuance_entropy: u256 = jet::calculate_issuance_entropy(outpoint, contract_hash); - let explicit_reissuance_tokens_amount: u64 = unwrap_right::<(u1, u256)>(unwrap(unwrap(jet::issuance_token_amount(input_index)))); + let explicit_reissuance_tokens_amount: u64 = unwrap_right::<(u1, u256)>(unwrap(unwrap( + jet::issuance_token_amount(input_index) + ))); match reissuance_flag { // TODO: Add confidential reissuance tokens verification @@ -112,7 +154,7 @@ fn verify_issuance(input_index: u32, output_index: u32, reissuance_flag: bool) { let issuance_amount: u64 = unwrap_right::<(u1, u256)>(issuance_amount); - ensure_asset_with_amount(output_index, false, issuance_asset, issuance_amount); + ensure_output_asset_and_amount(output_index, issuance_asset, issuance_amount); } fn verify_issuance_step(acc: (), start_output_index: u32, i: u8) -> Either<(), ()> { @@ -131,41 +173,70 @@ fn verify_issuance_step(acc: (), start_output_index: u32, i: u8) -> Either<(), ( } } -fn issue_new_assets(output_index: u32, owner_sig: Signature) { - let current_index: u32 = jet::current_index(); +// Assets issuance flow + +fn get_asset_issuance_issuance_factory_indexes(start_input_index: u32, start_output_index: u32) -> (u32, u32) { + (safe_add_32(start_input_index, 1), safe_add_32(start_output_index, 1)) +} + +fn get_asset_issuance_start_issued_output_index(start_output_index: u32) -> u32 { + safe_add_32(start_output_index, 2) +} - assert!(jet::eq_32(current_index, 0)); - jet::bip_0340_verify((param::FACTORY_OWNER_PUBKEY, jet::sig_all_hash()), owner_sig); +fn issue_new_assets(output_index: u32) { + let (auth_nft_input_index, auth_nft_output_index): (u32, u32) = (0, output_index); + let ( + issuance_factory_input_index, + issuance_factory_output_index + ): (u32, u32) = get_asset_issuance_issuance_factory_indexes(auth_nft_input_index, auth_nft_output_index); - ensure_input_and_output_script_hashes_eq(current_index, output_index); + assert!(jet::eq_32(issuance_factory_input_index, jet::current_index())); - let (carry, issued_outputs_start_index): (bool, u32) = jet::add_32(output_index, 1); - ensure_zero_bit(carry); + let current_asset_bits: u256 = get_current_explicit_asset(); + + ensure_io_asset_and_amount_eq(auth_nft_input_index, auth_nft_output_index, current_asset_bits, 1); + ensure_io_asset_and_amount_eq(issuance_factory_input_index, issuance_factory_output_index, current_asset_bits, 1); + + ensure_io_script_hashes_eq(issuance_factory_input_index, issuance_factory_output_index); + + let issued_outputs_start_index: u32 = get_asset_issuance_start_issued_output_index(auth_nft_output_index); unwrap_left::<()>(for_while::((), issued_outputs_start_index)); } -fn remove_factory(output_index: u32, owner_sig: Signature) { - let current_index: u32 = jet::current_index(); +// Factory removing flow - assert!(jet::eq_32(current_index, 0)); - jet::bip_0340_verify((param::FACTORY_OWNER_PUBKEY, jet::sig_all_hash()), owner_sig); +fn get_removing_auth_nft_indexes(start_input_index: u32, start_output_index: u32) -> (u32, u32) { + (safe_add_32(start_input_index, 1), safe_add_32(start_output_index, 1)) +} - ensure_output_is_op_return(output_index); - ensure_input_and_output_eq(current_index, output_index); +fn remove_factory(output_index: u32) { + let (issuance_factory_input_index, issuance_factory_output_index): (u32, u32) = (0, output_index); + let ( + auth_nft_input_index, + auth_nft_output_index + ): (u32, u32) = get_removing_auth_nft_indexes(issuance_factory_input_index, issuance_factory_output_index); + + assert!(jet::eq_32(issuance_factory_input_index, jet::current_index())); + + let current_asset_bits: u256 = get_current_explicit_asset(); + + ensure_input_asset_and_amount_burn( + issuance_factory_input_index, + issuance_factory_output_index, + current_asset_bits, + 1 + ); + ensure_input_asset_and_amount_burn(auth_nft_input_index, auth_nft_output_index, current_asset_bits, 1); } fn main() { match witness::PATH { - Left(params: (u32, Signature)) => { - let (output_index, owner_sig): (u32, Signature) = params; - - issue_new_assets(output_index, owner_sig); + Left(output_index: u32) => { + issue_new_assets(output_index); }, - Right(params: (u32, Signature)) => { - let (output_index, owner_sig): (u32, Signature) = params; - - remove_factory(output_index, owner_sig); + Right(output_index: u32) => { + remove_factory(output_index); } } } \ No newline at end of file diff --git a/crates/contracts/src/programs/issuance_factory/core.rs b/crates/contracts/src/programs/issuance_factory/core.rs index 81b6b03..5b193b1 100644 --- a/crates/contracts/src/programs/issuance_factory/core.rs +++ b/crates/contracts/src/programs/issuance_factory/core.rs @@ -13,7 +13,7 @@ use crate::programs::issuance_factory::{ use crate::programs::program::{MetadataProgram, SimplexProgram}; use crate::utils::op_return_payload; -const CREATION_METADATA_OUTPUT_INDEX: usize = 1; +const CREATION_METADATA_OUTPUT_INDEX: usize = 2; pub struct IssuanceFactory { program: IssuanceFactoryProgram, @@ -50,7 +50,6 @@ impl IssuanceFactory { let issuance_factory_parameters = IssuanceFactoryParameters { issuing_utxos_count: creation_metadata.issuing_utxos_count, reissuance_flags: creation_metadata.reissuance_flags, - owner_pubkey: creation_metadata.owner_pubkey, network: *provider.get_network(), }; @@ -74,19 +73,23 @@ impl IssuanceFactory { ft.add_output(PartialOutput::new_metadata(&op_return_data)); } - pub fn attach_assets_issuing( + pub fn attach_assets_issuance( &self, ft: &mut FinalTransaction, program_utxo: UTXO, program_issuance_input: IssuanceInput, ) -> IssuanceDetails { - let issuance_factory_amount = program_utxo.explicit_amount(); - let issuance_factory_asset = program_utxo.explicit_asset(); + assert!( + ft.n_inputs() > 0, + "Assets issuance cant't be first attachment in transaction" + ); - let issuance_factory_output_index = ft.n_outputs() as u32; + let auth_nft_output_index = ft.n_outputs() as u32 - 1; + + let issuance_factory_asset = program_utxo.explicit_asset(); let issuance_factory_witness_branch = IssuanceFactoryWitnessBranch::IssueAssets { - output_index: issuance_factory_output_index, + output_index: auth_nft_output_index, }; let issuance_details = self.add_program_issuance_input_with_signature( @@ -94,16 +97,15 @@ impl IssuanceFactory { program_utxo, program_issuance_input, issuance_factory_witness_branch.build_witness(), - RequiredSignature::witness_with_path("PATH", &["Left", "1"]), + RequiredSignature::None, ); - self.add_program_output(ft, issuance_factory_asset, issuance_factory_amount); + self.add_program_output(ft, issuance_factory_asset, 1); issuance_details } pub fn attach_factory_removing(&self, ft: &mut FinalTransaction, program_utxo: UTXO) { - let issuance_factory_amount = program_utxo.explicit_amount(); let issuance_factory_asset = program_utxo.explicit_asset(); let issuance_factory_output_index = ft.n_outputs() as u32; @@ -116,12 +118,18 @@ impl IssuanceFactory { ft, program_utxo, issuance_factory_witness_branch.build_witness(), - RequiredSignature::witness_with_path("PATH", &["Right", "1"]), + RequiredSignature::None, ); ft.add_output(PartialOutput::new( Script::new_op_return(b"burn"), - issuance_factory_amount, + 1, + issuance_factory_asset, + )); + + ft.add_output(PartialOutput::new( + Script::new_op_return(b"burn"), + 1, issuance_factory_asset, )); } diff --git a/crates/contracts/src/programs/issuance_factory/metadata.rs b/crates/contracts/src/programs/issuance_factory/metadata.rs index ff9211c..96c3ca5 100644 --- a/crates/contracts/src/programs/issuance_factory/metadata.rs +++ b/crates/contracts/src/programs/issuance_factory/metadata.rs @@ -1,36 +1,24 @@ -use simplex::simplicityhl::elements::{hex::ToHex, schnorr::XOnlyPublicKey}; - use crate::programs::issuance_factory::{IssuanceFactory, IssuanceFactoryError}; use crate::programs::program::{ CreationMetadata, MetadataProgram, PROGRAM_ID_LENGTH, ProgramId, SimplexProgram, }; -const OWNER_PUBKEY_LENGTH: usize = 32; -const CREATION_OP_RETURN_DATA_LENGTH: usize = PROGRAM_ID_LENGTH - + std::mem::size_of::() - + std::mem::size_of::() - + OWNER_PUBKEY_LENGTH; +const CREATION_OP_RETURN_DATA_LENGTH: usize = + PROGRAM_ID_LENGTH + std::mem::size_of::() + std::mem::size_of::(); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct IssuanceFactoryCreationMetadata { pub program_id: ProgramId, pub issuing_utxos_count: u8, pub reissuance_flags: u64, - pub owner_pubkey: XOnlyPublicKey, } impl IssuanceFactoryCreationMetadata { - pub fn new( - program_id: ProgramId, - issuing_utxos_count: u8, - reissuance_flags: u64, - owner_pubkey: XOnlyPublicKey, - ) -> Self { + pub fn new(program_id: ProgramId, issuing_utxos_count: u8, reissuance_flags: u64) -> Self { Self { program_id, issuing_utxos_count, reissuance_flags, - owner_pubkey, } } } @@ -58,17 +46,11 @@ impl CreationMetadata for IssuanceFactoryCreationMetadata { .try_into() .expect("reissuance flags length is fixed"), ); - cursor += std::mem::size_of::(); - - let owner_pubkey_bytes = &op_return_bytes[cursor..]; - let owner_pubkey = XOnlyPublicKey::from_slice(owner_pubkey_bytes) - .map_err(|_| IssuanceFactoryError::InvalidMetadataBytes(op_return_bytes.to_hex()))?; Ok(Self { program_id, issuing_utxos_count, reissuance_flags, - owner_pubkey, }) } @@ -77,7 +59,6 @@ impl CreationMetadata for IssuanceFactoryCreationMetadata { op_return_data.extend_from_slice(&self.program_id); op_return_data.push(self.issuing_utxos_count); op_return_data.extend_from_slice(&self.reissuance_flags.to_le_bytes()); - op_return_data.extend_from_slice(&self.owner_pubkey.serialize()); op_return_data } @@ -87,11 +68,12 @@ impl MetadataProgram for IssuanceFactory { type Metadata = IssuanceFactoryCreationMetadata; fn build_metadata(&self) -> Self::Metadata { + let parameters = self.get_parameters(); + IssuanceFactoryCreationMetadata::new( Self::get_program_id(), - self.get_parameters().issuing_utxos_count, - self.get_parameters().reissuance_flags, - self.get_parameters().owner_pubkey, + parameters.issuing_utxos_count, + parameters.reissuance_flags, ) } } diff --git a/crates/contracts/src/programs/issuance_factory/params.rs b/crates/contracts/src/programs/issuance_factory/params.rs index 9c3554e..c24033f 100644 --- a/crates/contracts/src/programs/issuance_factory/params.rs +++ b/crates/contracts/src/programs/issuance_factory/params.rs @@ -1,4 +1,4 @@ -use simplex::{provider::SimplicityNetwork, simplicityhl::elements::schnorr::XOnlyPublicKey}; +use simplex::provider::SimplicityNetwork; use crate::artifacts::issuance_factory::derived_issuance_factory::IssuanceFactoryArguments; @@ -6,7 +6,6 @@ use crate::artifacts::issuance_factory::derived_issuance_factory::IssuanceFactor pub struct IssuanceFactoryParameters { pub issuing_utxos_count: u8, pub reissuance_flags: u64, - pub owner_pubkey: XOnlyPublicKey, pub network: SimplicityNetwork, } @@ -15,7 +14,6 @@ impl IssuanceFactoryParameters { IssuanceFactoryArguments { issuing_utxos_count: self.issuing_utxos_count, reissuance_flags: self.reissuance_flags, - factory_owner_pubkey: self.owner_pubkey.serialize(), } } } diff --git a/crates/contracts/src/programs/issuance_factory/witness.rs b/crates/contracts/src/programs/issuance_factory/witness.rs index c9b9541..4a7b69e 100644 --- a/crates/contracts/src/programs/issuance_factory/witness.rs +++ b/crates/contracts/src/programs/issuance_factory/witness.rs @@ -1,7 +1,4 @@ -use simplex::{ - constants::DUMMY_SIGNATURE, - either::Either::{Left, Right}, -}; +use simplex::either::Either::{Left, Right}; use crate::artifacts::issuance_factory::derived_issuance_factory::IssuanceFactoryWitness; @@ -14,12 +11,8 @@ pub enum IssuanceFactoryWitnessBranch { impl IssuanceFactoryWitnessBranch { pub fn build_witness(&self) -> Box { let path = match self { - IssuanceFactoryWitnessBranch::IssueAssets { output_index } => { - Left((*output_index, DUMMY_SIGNATURE)) - } - IssuanceFactoryWitnessBranch::RemoveFactory { output_index } => { - Right((*output_index, DUMMY_SIGNATURE)) - } + IssuanceFactoryWitnessBranch::IssueAssets { output_index } => Left(*output_index), + IssuanceFactoryWitnessBranch::RemoveFactory { output_index } => Right(*output_index), }; Box::new(IssuanceFactoryWitness { path }) diff --git a/crates/contracts/src/programs/lending/core.rs b/crates/contracts/src/programs/lending/core.rs index d2bcfc0..71dd940 100644 --- a/crates/contracts/src/programs/lending/core.rs +++ b/crates/contracts/src/programs/lending/core.rs @@ -18,7 +18,7 @@ use crate::{ utils::op_return_payload, }; -const CREATION_METADATA_OUTPUT_INDEX: usize = 3; +const CREATION_METADATA_OUTPUT_INDEX: usize = 4; pub struct LendingOfferStorage { pub is_active: bool, diff --git a/crates/contracts/tests/issuance_factory/creation_metadata_success_flow.rs b/crates/contracts/tests/issuance_factory/creation_metadata_success_flow.rs index 24e99b2..a2372a8 100644 --- a/crates/contracts/tests/issuance_factory/creation_metadata_success_flow.rs +++ b/crates/contracts/tests/issuance_factory/creation_metadata_success_flow.rs @@ -6,7 +6,7 @@ use simplex::simplicityhl::elements::{Transaction, Txid}; use super::setup::setup_issuance_factory; fn op_return_payload(tx: &Transaction) -> Vec { - script_op_return_payload(&tx.output[1].script_pubkey) + script_op_return_payload(&tx.output[2].script_pubkey) .unwrap() .to_vec() } @@ -17,7 +17,7 @@ fn setup_default_issuance_factory( let provider = context.get_default_provider(); let issuing_utxos_count = 3; let reissuance_flags = 0x0102_0304_0506_0708; - let (issuance_factory, issuance_factory_parameters) = + let (_, issuance_factory, issuance_factory_parameters) = setup_issuance_factory(context, issuing_utxos_count, reissuance_flags)?; let issuance_factory_utxo = @@ -43,8 +43,8 @@ fn creates_issuance_factory_with_creation_metadata( let op_return_data = op_return_payload(&issuance_factory_creation_tx); let expected_reissuance_flags = issuance_factory_parameters.reissuance_flags.to_le_bytes(); - assert!(issuance_factory_creation_tx.output[1].is_null_data()); - assert_eq!(op_return_data.len(), 45); + assert!(issuance_factory_creation_tx.output[2].is_null_data()); + assert_eq!(op_return_data.len(), 13); assert_eq!( &op_return_data[0..4], IssuanceFactory::get_program_id().as_slice() @@ -54,13 +54,6 @@ fn creates_issuance_factory_with_creation_metadata( issuance_factory_parameters.issuing_utxos_count ); assert_eq!(&op_return_data[5..13], expected_reissuance_flags.as_slice()); - assert_eq!( - &op_return_data[13..45], - issuance_factory_parameters - .owner_pubkey - .serialize() - .as_slice() - ); Ok(()) } @@ -89,10 +82,6 @@ fn decodes_issuance_factory_creation_metadata(context: simplex::TestContext) -> decoded_op_return_data.reissuance_flags, issuance_factory_parameters.reissuance_flags ); - assert_eq!( - decoded_op_return_data.owner_pubkey, - issuance_factory_parameters.owner_pubkey - ); Ok(()) } diff --git a/crates/contracts/tests/issuance_factory/issue_assets_failure_flows.rs b/crates/contracts/tests/issuance_factory/issue_assets_failure_flows.rs index df53005..ae4ffb4 100644 --- a/crates/contracts/tests/issuance_factory/issue_assets_failure_flows.rs +++ b/crates/contracts/tests/issuance_factory/issue_assets_failure_flows.rs @@ -1,23 +1,55 @@ -use lending_contracts::{programs::program::SimplexProgram, utils::get_random_seed}; +use lending_contracts::{ + programs::{issuance_factory::IssuanceFactory, program::SimplexProgram}, + utils::get_random_seed, +}; use simplex::transaction::{ - FinalTransaction, PartialInput, PartialOutput, RequiredSignature, partial_input::IssuanceInput, + FinalTransaction, PartialInput, PartialOutput, RequiredSignature, UTXO, + partial_input::IssuanceInput, }; use super::setup::setup_issuance_factory; -#[simplex::test] -fn fails_to_issue_wrong_assets_number(context: simplex::TestContext) -> anyhow::Result<()> { +fn setup_default_assets_issuance( + context: &simplex::TestContext, + issuing_utxos_count: u8, + reissuance_flags: u64, +) -> anyhow::Result<(FinalTransaction, IssuanceFactory, UTXO)> { let provider = context.get_default_provider(); let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 3, 0)?; + let (factory_asset_id, issuance_factory, _) = + setup_issuance_factory(context, issuing_utxos_count, reissuance_flags)?; + + let issuance_factory_utxo = + provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + + let auth_nft_utxo = signer.get_utxos_asset(factory_asset_id)?[0].clone(); let mut ft = FinalTransaction::new(); + ft.add_input( + PartialInput::new(auth_nft_utxo), + RequiredSignature::NativeEcdsa, + ); + ft.add_output(PartialOutput::new( + signer.get_address().script_pubkey(), + 1, + factory_asset_id, + )); + + Ok((ft, issuance_factory, issuance_factory_utxo)) +} + +#[simplex::test] +fn fails_to_issue_wrong_assets_number(context: simplex::TestContext) -> anyhow::Result<()> { + let signer = context.get_default_signer(); + + let (mut ft, issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, 3, 0)?; + let policy_asset_id = context.get_network().policy_asset(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + let policy_utxo = signer.get_utxos_asset(policy_asset_id)?[0].clone(); let issuance_entropy = get_random_seed(); @@ -26,7 +58,7 @@ fn fails_to_issue_wrong_assets_number(context: simplex::TestContext) -> anyhow:: let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, 0, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, @@ -63,17 +95,12 @@ fn fails_to_issue_wrong_assets_number(context: simplex::TestContext) -> anyhow:: fn fails_to_issue_assets_with_reissuance_tokens( context: simplex::TestContext, ) -> anyhow::Result<()> { - let provider = context.get_default_provider(); let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 2, 0)?; + let (mut ft, issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, 2, 0)?; - let mut ft = FinalTransaction::new(); - - let policy_asset_id = context.get_network().policy_asset(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); - let policy_utxo = signer.get_utxos_asset(policy_asset_id)?[0].clone(); + let policy_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); let issuance_entropy = get_random_seed(); let first_asset_amount = 1000; @@ -82,7 +109,7 @@ fn fails_to_issue_assets_with_reissuance_tokens( let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, first_inflation_amount, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, @@ -127,17 +154,12 @@ fn fails_to_issue_assets_with_reissuance_tokens( fn fails_to_issue_assets_without_reissuance_tokens( context: simplex::TestContext, ) -> anyhow::Result<()> { - let provider = context.get_default_provider(); let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 2, 2)?; - - let mut ft = FinalTransaction::new(); + let (mut ft, issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, 2, 2)?; - let policy_asset_id = context.get_network().policy_asset(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); - let policy_utxo = signer.get_utxos_asset(policy_asset_id)?[0].clone(); + let policy_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); let issuance_entropy = get_random_seed(); let first_asset_amount = 1000; @@ -145,7 +167,7 @@ fn fails_to_issue_assets_without_reissuance_tokens( let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, 0, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, diff --git a/crates/contracts/tests/issuance_factory/issue_assets_success_flows.rs b/crates/contracts/tests/issuance_factory/issue_assets_success_flows.rs index 418b2cf..aced264 100644 --- a/crates/contracts/tests/issuance_factory/issue_assets_success_flows.rs +++ b/crates/contracts/tests/issuance_factory/issue_assets_success_flows.rs @@ -1,24 +1,55 @@ +use lending_contracts::programs::issuance_factory::IssuanceFactory; use lending_contracts::programs::program::SimplexProgram; use lending_contracts::utils::get_random_seed; use simplex::transaction::partial_input::IssuanceInput; -use simplex::transaction::{FinalTransaction, PartialInput, PartialOutput, RequiredSignature}; +use simplex::transaction::{ + FinalTransaction, PartialInput, PartialOutput, RequiredSignature, UTXO, +}; use super::setup::setup_issuance_factory; +fn setup_default_assets_issuance( + context: &simplex::TestContext, + ft: &mut FinalTransaction, + issuing_utxos_count: u8, + reissuance_flags: u64, +) -> anyhow::Result<(IssuanceFactory, UTXO)> { + let provider = context.get_default_provider(); + let signer = context.get_default_signer(); + + let (factory_asset_id, issuance_factory, _) = + setup_issuance_factory(context, issuing_utxos_count, reissuance_flags)?; + + let issuance_factory_utxo = + provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + + let auth_nft_utxo = signer.get_utxos_asset(factory_asset_id)?[0].clone(); + + ft.add_input( + PartialInput::new(auth_nft_utxo), + RequiredSignature::NativeEcdsa, + ); + ft.add_output(PartialOutput::new( + signer.get_address().script_pubkey(), + 1, + factory_asset_id, + )); + + Ok((issuance_factory, issuance_factory_utxo)) +} + #[simplex::test] fn issues_new_assets_without_reissuance_tokens_from_the_0_output( context: simplex::TestContext, ) -> anyhow::Result<()> { - let provider = context.get_default_provider(); let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 2, 0)?; - let mut ft = FinalTransaction::new(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + let (issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, &mut ft, 2, 0)?; + let policy_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); let issuance_entropy = get_random_seed(); @@ -27,7 +58,7 @@ fn issues_new_assets_without_reissuance_tokens_from_the_0_output( let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, 0, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, @@ -59,32 +90,18 @@ fn issues_new_assets_without_reissuance_tokens_from_the_0_output( fn issues_new_assets_without_reissuance_tokens_from_the_2_output( context: simplex::TestContext, ) -> anyhow::Result<()> { - let provider = context.get_default_provider(); let alice = context.get_default_signer(); let bob = context.random_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 3, 0)?; - let mut ft = FinalTransaction::new(); let policy_asset_id = context.get_network().policy_asset(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); - let policy_utxos = alice.get_utxos_asset(policy_asset_id)?; - let first_policy_utxo = policy_utxos[0].clone(); - let second_policy_utxo = policy_utxos[1].clone(); - - let issuance_entropy = get_random_seed(); - let first_asset_amount = 1000; - let second_asset_amount = 2000; - let third_asset_amount = 3000; - - let first_outputs_amount = first_policy_utxo.explicit_amount() / 4; + let policy_outputs_amount = 50; ft.add_output( PartialOutput::new( bob.get_confidential_address().script_pubkey(), - first_outputs_amount, + policy_outputs_amount, policy_asset_id, ) .with_blinding_key(bob.get_blinding_public_key()), @@ -92,15 +109,27 @@ fn issues_new_assets_without_reissuance_tokens_from_the_2_output( ft.add_output( PartialOutput::new( bob.get_confidential_address().script_pubkey(), - first_outputs_amount, + policy_outputs_amount, policy_asset_id, ) .with_blinding_key(bob.get_blinding_public_key()), ); + let (issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, &mut ft, 3, 0)?; + + let policy_utxos = alice.get_utxos_asset(policy_asset_id)?; + let first_policy_utxo = policy_utxos[0].clone(); + let second_policy_utxo = policy_utxos[1].clone(); + + let issuance_entropy = get_random_seed(); + let first_asset_amount = 1000; + let second_asset_amount = 2000; + let third_asset_amount = 3000; + let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, 0, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, @@ -133,7 +162,7 @@ fn issues_new_assets_without_reissuance_tokens_from_the_2_output( third_issuance_details.asset_id, )); - assert_eq!(ft.n_outputs(), 6); + assert_eq!(ft.n_outputs(), 7); alice.broadcast(&ft)?.wait()?; @@ -144,15 +173,13 @@ fn issues_new_assets_without_reissuance_tokens_from_the_2_output( fn issues_new_assets_with_reissuance_tokens_from_the_0_output( context: simplex::TestContext, ) -> anyhow::Result<()> { - let provider = context.get_default_provider(); let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 2, 1)?; - let mut ft = FinalTransaction::new(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + let (issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, &mut ft, 2, 1)?; + let policy_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); let issuance_entropy = get_random_seed(); @@ -162,7 +189,7 @@ fn issues_new_assets_with_reissuance_tokens_from_the_0_output( let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, first_inflation_amount, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, @@ -202,34 +229,18 @@ fn issues_new_assets_with_reissuance_tokens_from_the_0_output( fn issues_new_assets_with_reissuance_tokens_from_the_2_output( context: simplex::TestContext, ) -> anyhow::Result<()> { - let provider = context.get_default_provider(); let alice = context.get_default_signer(); let bob = context.random_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 3, 5)?; - let mut ft = FinalTransaction::new(); let policy_asset_id = context.get_network().policy_asset(); - let issuance_factory_utxo = - provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); - let policy_utxos = alice.get_utxos_asset(policy_asset_id)?; - let first_policy_utxo = policy_utxos[0].clone(); - let second_policy_utxo = policy_utxos[1].clone(); - - let issuance_entropy = get_random_seed(); - let first_asset_amount = 1000; - let first_inflation_amount = 5; - let second_asset_amount = 2000; - let third_asset_amount = 3000; - let third_inflation_amount = 15; - - let first_outputs_amount = first_policy_utxo.explicit_amount() / 4; + let policy_outputs_amount = 50; ft.add_output( PartialOutput::new( bob.get_confidential_address().script_pubkey(), - first_outputs_amount, + policy_outputs_amount, policy_asset_id, ) .with_blinding_key(bob.get_blinding_public_key()), @@ -237,15 +248,29 @@ fn issues_new_assets_with_reissuance_tokens_from_the_2_output( ft.add_output( PartialOutput::new( bob.get_confidential_address().script_pubkey(), - first_outputs_amount, + policy_outputs_amount, policy_asset_id, ) .with_blinding_key(bob.get_blinding_public_key()), ); + let (issuance_factory, issuance_factory_utxo) = + setup_default_assets_issuance(&context, &mut ft, 3, 5)?; + + let policy_utxos = alice.get_utxos_asset(policy_asset_id)?; + let first_policy_utxo = policy_utxos[0].clone(); + let second_policy_utxo = policy_utxos[1].clone(); + + let issuance_entropy = get_random_seed(); + let first_asset_amount = 1000; + let first_inflation_amount = 5; + let second_asset_amount = 2000; + let third_asset_amount = 3000; + let third_inflation_amount = 15; + let factory_issuance_input = IssuanceInput::new_issuance(first_asset_amount, first_inflation_amount, issuance_entropy); - let first_issuance_details = issuance_factory.attach_assets_issuing( + let first_issuance_details = issuance_factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, factory_issuance_input, @@ -294,7 +319,7 @@ fn issues_new_assets_with_reissuance_tokens_from_the_2_output( .with_blinding_key(alice.get_blinding_public_key()), ); - assert_eq!(ft.n_outputs(), 8); + assert_eq!(ft.n_outputs(), 9); alice.broadcast(&ft)?.wait()?; diff --git a/crates/contracts/tests/issuance_factory/remove_factory_failure_flows.rs b/crates/contracts/tests/issuance_factory/remove_factory_failure_flows.rs index d50f8e4..d3431c0 100644 --- a/crates/contracts/tests/issuance_factory/remove_factory_failure_flows.rs +++ b/crates/contracts/tests/issuance_factory/remove_factory_failure_flows.rs @@ -1,16 +1,17 @@ use lending_contracts::programs::program::SimplexProgram; -use simplex::transaction::FinalTransaction; +use simplex::transaction::{FinalTransaction, PartialInput, RequiredSignature}; use super::setup::setup_issuance_factory; #[simplex::test] -fn fails_to_remove_issuance_factory_with_invalid_signer( +fn fails_to_remove_issuance_factory_with_invalid_inputs_order( context: simplex::TestContext, ) -> anyhow::Result<()> { let provider = context.get_default_provider(); + let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 2, 0)?; + let (factory_asset_id, issuance_factory, _) = setup_issuance_factory(&context, 2, 0)?; let random_signer = context.random_signer(); @@ -19,6 +20,13 @@ fn fails_to_remove_issuance_factory_with_invalid_signer( let issuance_factory_utxo = provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + let auth_nft_utxo = signer.get_utxos_asset(factory_asset_id)?[0].clone(); + + ft.add_input( + PartialInput::new(auth_nft_utxo), + RequiredSignature::NativeEcdsa, + ); + issuance_factory.attach_factory_removing(&mut ft, issuance_factory_utxo); let result = random_signer.finalize(&ft); diff --git a/crates/contracts/tests/issuance_factory/remove_factory_success_flows.rs b/crates/contracts/tests/issuance_factory/remove_factory_success_flows.rs index 422fce6..e9acb08 100644 --- a/crates/contracts/tests/issuance_factory/remove_factory_success_flows.rs +++ b/crates/contracts/tests/issuance_factory/remove_factory_success_flows.rs @@ -1,6 +1,6 @@ use lending_contracts::programs::program::SimplexProgram; -use simplex::transaction::FinalTransaction; +use simplex::transaction::{FinalTransaction, PartialInput, RequiredSignature}; use super::setup::setup_issuance_factory; @@ -9,15 +9,22 @@ fn removes_issuance_factory_correctly(context: simplex::TestContext) -> anyhow:: let provider = context.get_default_provider(); let signer = context.get_default_signer(); - let (issuance_factory, _) = setup_issuance_factory(&context, 2, 0)?; + let (factory_asset_id, issuance_factory, _) = setup_issuance_factory(&context, 2, 0)?; let mut ft = FinalTransaction::new(); let issuance_factory_utxo = provider.fetch_scripthash_utxos(&issuance_factory.get_script_pubkey())?[0].clone(); + let auth_nft_utxo = signer.get_utxos_asset(factory_asset_id)?[0].clone(); + issuance_factory.attach_factory_removing(&mut ft, issuance_factory_utxo); + ft.add_input( + PartialInput::new(auth_nft_utxo), + RequiredSignature::NativeEcdsa, + ); + signer.broadcast(&ft)?.wait()?; Ok(()) diff --git a/crates/contracts/tests/issuance_factory/setup.rs b/crates/contracts/tests/issuance_factory/setup.rs index 280618c..f2fb151 100644 --- a/crates/contracts/tests/issuance_factory/setup.rs +++ b/crates/contracts/tests/issuance_factory/setup.rs @@ -1,8 +1,9 @@ use lending_contracts::programs::issuance_factory::{IssuanceFactory, IssuanceFactoryParameters}; use lending_contracts::utils::get_random_seed; +use simplex::simplicityhl::elements::AssetId; use simplex::transaction::partial_input::IssuanceInput; -use simplex::transaction::{FinalTransaction, PartialInput, RequiredSignature}; +use simplex::transaction::{FinalTransaction, PartialInput, PartialOutput, RequiredSignature}; use super::common::wallet::split_first_signer_utxo; @@ -10,7 +11,7 @@ pub(super) fn setup_issuance_factory( context: &simplex::TestContext, issuing_utxos_count: u8, reissuance_flags: u64, -) -> anyhow::Result<(IssuanceFactory, IssuanceFactoryParameters)> { +) -> anyhow::Result<(AssetId, IssuanceFactory, IssuanceFactoryParameters)> { let signer = context.get_default_signer(); split_first_signer_utxo(context, vec![1000, 5000, 10000]); @@ -18,13 +19,12 @@ pub(super) fn setup_issuance_factory( let issuance_factory_parameters = IssuanceFactoryParameters { issuing_utxos_count, reissuance_flags, - owner_pubkey: signer.get_schnorr_public_key(), network: *context.get_network(), }; let issuance_factory = IssuanceFactory::new(issuance_factory_parameters); let issuance_factory_entropy = get_random_seed(); - let issuance_factory_asset_amount = 1; + let issuance_factory_asset_amount = 2; let policy_utxo = signer.get_utxos_asset(context.get_network().policy_asset())?[0].clone(); @@ -36,13 +36,19 @@ pub(super) fn setup_issuance_factory( RequiredSignature::NativeEcdsa, ); - issuance_factory.attach_creation( - &mut ft, + ft.add_output(PartialOutput::new( + signer.get_address().script_pubkey(), + 1, issuance_details.asset_id, - issuance_factory_asset_amount, - ); + )); + + issuance_factory.attach_creation(&mut ft, issuance_details.asset_id, 1); signer.broadcast(&ft)?.wait()?; - Ok((issuance_factory, issuance_factory_parameters)) + Ok(( + issuance_details.asset_id, + issuance_factory, + issuance_factory_parameters, + )) } diff --git a/crates/contracts/tests/lending/creation_metadata_success_flow.rs b/crates/contracts/tests/lending/creation_metadata_success_flow.rs index afc3bc7..68c40f9 100644 --- a/crates/contracts/tests/lending/creation_metadata_success_flow.rs +++ b/crates/contracts/tests/lending/creation_metadata_success_flow.rs @@ -43,11 +43,11 @@ fn creates_pending_offer_with_creation_metadata( default_pending_offer_setup(&context)?; let lending_offer_creation_tx = provider.fetch_transaction(&lending_offer_creation_txid)?; - let op_return_data = op_return_payload(&lending_offer_creation_tx.output[3].script_pubkey) + let op_return_data = op_return_payload(&lending_offer_creation_tx.output[4].script_pubkey) .unwrap() .to_vec(); - assert!(lending_offer_creation_tx.output[3].is_null_data()); + assert!(lending_offer_creation_tx.output[4].is_null_data()); assert_eq!(op_return_data.len(), 50); assert_eq!( &op_return_data[0..4], @@ -97,7 +97,7 @@ fn decodes_pending_offer_creation_metadata(context: simplex::TestContext) -> any let lending_offer_creation_tx = provider.fetch_transaction(&lending_offer_creation_txid)?; - let op_return_data = op_return_payload(&lending_offer_creation_tx.output[3].script_pubkey) + let op_return_data = op_return_payload(&lending_offer_creation_tx.output[4].script_pubkey) .unwrap() .to_vec(); let decoded_metadata = LendingOffer::decode_metadata_op_return(op_return_data)?; diff --git a/crates/contracts/tests/lending/setup.rs b/crates/contracts/tests/lending/setup.rs index fee154e..2ccc2ca 100644 --- a/crates/contracts/tests/lending/setup.rs +++ b/crates/contracts/tests/lending/setup.rs @@ -23,7 +23,6 @@ pub(super) fn setup_issuance_factory( let issuance_factory_parameters = IssuanceFactoryParameters { issuing_utxos_count: 2, reissuance_flags: 0, - owner_pubkey: signer.get_schnorr_public_key(), network: *context.get_network(), }; let issuance_factory = IssuanceFactory::new(issuance_factory_parameters); @@ -31,7 +30,7 @@ pub(super) fn setup_issuance_factory( let mut ft = FinalTransaction::new(); let issuance_factory_entropy = get_random_seed(); - let issuance_factory_asset_amount = 1; + let issuance_factory_asset_amount = 2; let issuance_details = ft.add_issuance_input( PartialInput::new(signer_policy_utxo), @@ -39,11 +38,13 @@ pub(super) fn setup_issuance_factory( RequiredSignature::NativeEcdsa, ); - issuance_factory.attach_creation( - &mut ft, + ft.add_output(PartialOutput::new( + signer.get_address().script_pubkey(), + 1, issuance_details.asset_id, - issuance_factory_asset_amount, - ); + )); + + issuance_factory.attach_creation(&mut ft, issuance_details.asset_id, 1); signer.broadcast(&ft)?.wait()?; @@ -145,13 +146,26 @@ pub(super) fn setup_pending_offer( let issuance_factory_utxo = provider.fetch_scripthash_utxos(&factory.get_script_pubkey())?[0].clone(); + let issuance_factory_asset_id = issuance_factory_utxo.explicit_asset(); + + let factory_auth_nft_utxo = signer.get_utxos_asset(issuance_factory_asset_id)?[0].clone(); // TODO: Use hash from the offer_parameters as asset_entropy let nfts_entropy = get_random_seed(); let mut ft = FinalTransaction::new(); - let borrower_nft_issuance_details = factory.attach_assets_issuing( + ft.add_input( + PartialInput::new(factory_auth_nft_utxo), + RequiredSignature::NativeEcdsa, + ); + ft.add_output(PartialOutput::new( + signer.get_address().script_pubkey(), + 1, + issuance_factory_asset_id, + )); + + let borrower_nft_issuance_details = factory.attach_assets_issuance( &mut ft, issuance_factory_utxo, IssuanceInput::new_issuance(1, 0, nfts_entropy), @@ -327,8 +341,8 @@ pub(super) fn get_pending_offer_utxos( let pending_offer_creation_tx = provider.fetch_transaction(&pending_offer_creation_txid)?; let lender_nft_utxo = UTXO { - outpoint: OutPoint::new(pending_offer_creation_txid, 2), - txout: pending_offer_creation_tx.output[2].clone(), + outpoint: OutPoint::new(pending_offer_creation_txid, 3), + txout: pending_offer_creation_tx.output[3].clone(), secrets: None, };