From 8f7afbbd950d1614185139ecee856e0ea4101d70 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 26 May 2026 10:30:37 +0200 Subject: [PATCH 1/2] feat: implement Clone for TransactionContextBuilder --- .../miden-testing/src/tx_context/builder.rs | 1 + .../tests/auth/guarded_multisig.rs | 123 ++++----- .../tests/auth/hybrid_multisig.rs | 127 ++++----- crates/miden-testing/tests/auth/multisig.rs | 245 +++++++----------- .../tests/auth/multisig_smart.rs | 85 +++--- 5 files changed, 228 insertions(+), 353 deletions(-) diff --git a/crates/miden-testing/src/tx_context/builder.rs b/crates/miden-testing/src/tx_context/builder.rs index c4cbac72e3..bbf60a9426 100644 --- a/crates/miden-testing/src/tx_context/builder.rs +++ b/crates/miden-testing/src/tx_context/builder.rs @@ -67,6 +67,7 @@ use crate::MockChain; /// # Ok(()) /// # } /// ``` +#[derive(Clone)] pub struct TransactionContextBuilder { source_manager: Arc, account: Account, diff --git a/crates/miden-testing/tests/auth/guarded_multisig.rs b/crates/miden-testing/tests/auth/guarded_multisig.rs index 6c995b93d5..4676a29f77 100644 --- a/crates/miden-testing/tests/auth/guarded_multisig.rs +++ b/crates/miden-testing/tests/auth/guarded_multisig.rs @@ -199,13 +199,12 @@ async fn test_guarded_multisig_signature_required( let mut mock_chain = mock_chain_builder.build().unwrap(); let salt = Word::from([Felt::new_unchecked(777); 4]); - let tx_context_init = mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .auth_args(salt) - .build()?; + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -220,12 +219,10 @@ async fn test_guarded_multisig_signature_required( .await?; // Missing guardian signature must fail. - let without_guardian_result = mock_chain - .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) + let without_guardian_result = tx_context_builder + .clone() .add_signature(public_keys[0].to_commitment(), msg, sig_1.clone()) .add_signature(public_keys[1].to_commitment(), msg, sig_2.clone()) - .auth_args(salt) .build()? .execute() .await; @@ -239,13 +236,10 @@ async fn test_guarded_multisig_signature_required( .await?; // With guardian signature the transaction should succeed. - let tx_context_execute = mock_chain - .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + let tx_context_execute = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) .add_signature(guardian_public_key.to_commitment(), msg, guardian_signature) - .auth_args(salt) .build()? .execute() .await?; @@ -312,13 +306,12 @@ async fn test_guarded_multisig_update_guardian_public_key( ))?; let update_salt = Word::from([Felt::new_unchecked(991); 4]); - let tx_context_init = mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(update_guardian_script.clone()) - .auth_args(update_salt) - .build()?; + .tx_script(update_guardian_script) + .auth_args(update_salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -333,12 +326,9 @@ async fn test_guarded_multisig_update_guardian_public_key( .await?; // Guardian key rotation intentionally skips guardian signature for this update tx. - let update_guardian_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(update_guardian_script) + let update_guardian_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), update_msg, sig_1) .add_signature(public_keys[1].to_commitment(), update_msg, sig_2) - .auth_args(update_salt) .build()? .execute() .await?; @@ -364,15 +354,15 @@ async fn test_guarded_multisig_update_guardian_public_key( // Build one tx summary after key update. Old GUARDIAN must fail and new GUARDIAN must pass on // this same transaction. let next_salt = Word::from([Felt::new_unchecked(992); 4]); - let tx_context_init_next = mock_chain + let tx_context_builder_next = mock_chain .build_tx_context(updated_multisig_account.id(), &[], &[])? - .auth_args(next_salt) - .build()?; + .auth_args(next_salt); - let tx_summary_next = match tx_context_init_next.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary_next = + match tx_context_builder_next.clone().build()?.execute().await.unwrap_err() { + TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, + error => anyhow::bail!("expected abort with tx effects: {error}"), + }; let next_msg = tx_summary_next.as_ref().to_commitment(); let tx_summary_next_signing = SigningInputs::TransactionSummary(tx_summary_next); @@ -390,12 +380,11 @@ async fn test_guarded_multisig_update_guardian_public_key( .await?; // Old guardian signature must fail after key update. - let with_old_guardian_result = mock_chain - .build_tx_context(updated_multisig_account.id(), &[], &[])? + let with_old_guardian_result = tx_context_builder_next + .clone() .add_signature(public_keys[0].to_commitment(), next_msg, next_sig_1.clone()) .add_signature(public_keys[1].to_commitment(), next_msg, next_sig_2.clone()) .add_signature(old_guardian_public_key.to_commitment(), next_msg, old_guardian_sig_next) - .auth_args(next_salt) .build()? .execute() .await; @@ -405,12 +394,10 @@ async fn test_guarded_multisig_update_guardian_public_key( )); // New guardian signature must pass. - mock_chain - .build_tx_context(updated_multisig_account.id(), &[], &[])? + tx_context_builder_next .add_signature(public_keys[0].to_commitment(), next_msg, next_sig_1) .add_signature(public_keys[1].to_commitment(), next_msg, next_sig_2) .add_signature(new_guardian_public_key.to_commitment(), next_msg, new_guardian_sig_next) - .auth_args(next_salt) .build()? .execute() .await?; @@ -470,13 +457,12 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( let mock_chain = mock_chain_builder.build().unwrap(); let salt = Word::from([Felt::new_unchecked(993); 4]); - let tx_context_init = mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[receive_asset_note.id()], &[])? - .tx_script(update_guardian_script.clone()) - .auth_args(salt) - .build()?; + .tx_script(update_guardian_script) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -490,12 +476,10 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( .get_signature(public_keys[1].to_commitment(), &tx_summary_signing) .await?; - let without_guardian_result = mock_chain - .build_tx_context(multisig_account.id(), &[receive_asset_note.id()], &[])? - .tx_script(update_guardian_script.clone()) + let without_guardian_result = tx_context_builder + .clone() .add_signature(public_keys[0].to_commitment(), msg, sig_1.clone()) .add_signature(public_keys[1].to_commitment(), msg, sig_2.clone()) - .auth_args(salt) .build()? .execute() .await; @@ -508,13 +492,10 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( .get_signature(old_guardian_public_key.to_commitment(), &tx_summary_signing) .await?; - let with_guardian_result = mock_chain - .build_tx_context(multisig_account.id(), &[receive_asset_note.id()], &[])? - .tx_script(update_guardian_script) + let with_guardian_result = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) .add_signature(old_guardian_public_key.to_commitment(), msg, old_guardian_signature) - .auth_args(salt) .build()? .execute() .await; @@ -553,15 +534,14 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( .unwrap(); let salt = Word::from([Felt::new_unchecked(994); 4]); - let tx_context_init = mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(update_guardian_with_output_script.clone()) - .add_note_script(note_script.clone()) - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .auth_args(salt) - .build()?; + .tx_script(update_guardian_with_output_script) + .add_note_script(note_script) + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -575,14 +555,9 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( .get_signature(public_keys[1].to_commitment(), &tx_summary_signing) .await?; - let result = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(update_guardian_with_output_script) - .add_note_script(note_script) - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + let result = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) - .auth_args(salt) .build()? .execute() .await; @@ -690,14 +665,15 @@ async fn test_guarded_multisig_update_guardian_enforces_no_notes( let salt = Word::from([Felt::new_unchecked(995); 4]); // Dry-run to obtain the tx summary the signers must sign. - let mut init_ctx = mock_chain + let mut tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &input_ids, &[])? - .tx_script(update_guardian_script.clone()) + .tx_script(update_guardian_script) .auth_args(salt); - if let Some(ref out) = output_note { - init_ctx = init_ctx.extend_expected_output_notes(vec![RawOutputNote::Full(out.clone())]); + if let Some(out) = output_note { + tx_context_builder = + tx_context_builder.extend_expected_output_notes(vec![RawOutputNote::Full(out)]); } - let tx_summary = match init_ctx.build()?.execute().await.unwrap_err() { + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected dry-run abort with tx effects: {error}"), }; @@ -714,18 +690,13 @@ async fn test_guarded_multisig_update_guardian_enforces_no_notes( .get_signature(old_guardian_public_key.to_commitment(), &signing) .await?; - let mut signed_ctx = mock_chain - .build_tx_context(multisig_account.id(), &input_ids, &[])? - .tx_script(update_guardian_script) - .auth_args(salt) + let result = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) - .add_signature(old_guardian_public_key.to_commitment(), msg, guardian_sig); - if let Some(ref out) = output_note { - signed_ctx = - signed_ctx.extend_expected_output_notes(vec![RawOutputNote::Full(out.clone())]); - } - let result = signed_ctx.build()?.execute().await; + .add_signature(old_guardian_public_key.to_commitment(), msg, guardian_sig) + .build()? + .execute() + .await; // Input check fires first, output check fires only when no input notes are present. match (include_input_note, include_output_note) { diff --git a/crates/miden-testing/tests/auth/hybrid_multisig.rs b/crates/miden-testing/tests/auth/hybrid_multisig.rs index 222cf79dbb..2dc3612cc6 100644 --- a/crates/miden-testing/tests/auth/hybrid_multisig.rs +++ b/crates/miden-testing/tests/auth/hybrid_multisig.rs @@ -140,14 +140,14 @@ async fn test_multisig_2_of_2_with_note_creation() -> anyhow::Result<()> { let salt = Word::from([Felt::ONE; 4]); - // Execute transaction without signatures - should fail - let tx_context_init = mock_chain + // Build transaction context with all config + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .auth_args(salt) - .build()?; + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures - should fail + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -164,12 +164,9 @@ async fn test_multisig_2_of_2_with_note_creation() -> anyhow::Result<()> { .await?; // Execute transaction with signatures - should succeed - let tx_context_execute = mock_chain - .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + let tx_context_execute = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) - .auth_args(salt) .build()? .execute() .await?; @@ -228,13 +225,12 @@ async fn test_multisig_2_of_4_all_signer_combinations() -> anyhow::Result<()> { for (i, (signer1_idx, signer2_idx)) in signer_combinations.iter().enumerate() { let salt = Word::from([Felt::new_unchecked(10 + i as u64); 4]); - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .auth_args(salt) - .build()?; + // Build transaction context with all config + let tx_context_builder = + mock_chain.build_tx_context(multisig_account.id(), &[], &[])?.auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -251,9 +247,7 @@ async fn test_multisig_2_of_4_all_signer_combinations() -> anyhow::Result<()> { .await?; // Execute transaction with signatures - should succeed for any combination - let tx_context_execute = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .auth_args(salt) + let tx_context_execute = tx_context_builder .add_signature(public_keys[*signer1_idx].to_commitment(), msg, sig_1) .add_signature(public_keys[*signer2_idx].to_commitment(), msg, sig_2) .build()?; @@ -361,24 +355,21 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { .with_dynamically_linked_library(AuthMultisig::code())? .compile_tx_script(tx_script_code)?; - let advice_inputs = AdviceInputs { - map: advice_map.clone(), - ..Default::default() - }; + let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; // Pass the MULTISIG_CONFIG_HASH as the tx_script_args let tx_script_args: Word = multisig_config_hash; - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain + // Build transaction context with all config + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) + .tx_script(tx_script) .tx_script_args(tx_script_args) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()?; + .extend_advice_inputs(advice_inputs) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -395,14 +386,9 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { .await?; // Execute transaction with signatures - should succeed - let update_approvers_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script) - .tx_script_args(multisig_config_hash) + let update_approvers_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) - .auth_args(salt) - .extend_advice_inputs(advice_inputs) .build()? .execute() .await @@ -507,14 +493,20 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { new_mock_chain_builder.add_output_note(RawOutputNote::Full(input_note_new.clone())); let new_mock_chain = new_mock_chain_builder.build().unwrap(); - // Execute transaction without signatures first to get tx summary - let tx_context_init_new = new_mock_chain + // Build transaction context with base config (output notes differ between init and execute) + let tx_context_builder_new = new_mock_chain .build_tx_context(updated_multisig_account.id(), &[input_note_new.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .auth_args(salt_new) - .build()?; + .auth_args(salt_new); - let tx_summary_new = match tx_context_init_new.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary_new = match tx_context_builder_new + .clone() + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) + .build()? + .execute() + .await + .unwrap_err() + { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -537,13 +529,11 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { // ================================================================================ // Execute transaction with new signatures - should succeed - let tx_context_execute_new = new_mock_chain - .build_tx_context(updated_multisig_account.id(), &[input_note_new.id()], &[])? + let tx_context_execute_new = tx_context_builder_new .extend_expected_output_notes(vec![RawOutputNote::Full(output_note_new)]) .add_signature(new_public_keys[0].to_commitment(), msg_new, sig_1_new) .add_signature(new_public_keys[1].to_commitment(), msg_new, sig_2_new) .add_signature(new_public_keys[2].to_commitment(), msg_new, sig_3_new) - .auth_args(salt_new) .build()? .execute() .await?; @@ -625,16 +615,16 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { let salt = Word::from([Felt::new_unchecked(3); 4]); - // Execute without signatures to get tx summary - let tx_context_init = mock_chain + // Build transaction context with all config + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) + .tx_script(tx_script) .tx_script_args(multisig_config_hash) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()?; + .extend_advice_inputs(advice_inputs) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute without signatures to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -657,16 +647,11 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { .await?; // Execute with signatures - let update_approvers_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script) - .tx_script_args(multisig_config_hash) + let update_approvers_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) .add_signature(public_keys[2].to_commitment(), msg, sig_3) .add_signature(public_keys[3].to_commitment(), msg, sig_4) - .auth_args(salt) - .extend_advice_inputs(advice_inputs) .build()? .execute() .await @@ -846,24 +831,21 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu .with_dynamically_linked_library(AuthMultisig::code())? .compile_tx_script(tx_script_code)?; - let advice_inputs = AdviceInputs { - map: advice_map.clone(), - ..Default::default() - }; + let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; // Pass the MULTISIG_CONFIG_HASH as the tx_script_args let tx_script_args: Word = multisig_config_hash; - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain + // Build transaction context with all config + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) + .tx_script(tx_script) .tx_script_args(tx_script_args) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()?; + .extend_advice_inputs(advice_inputs) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -883,14 +865,9 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu .await?; // Try to execute transaction with NEW signatures - should FAIL - let tx_context_with_new_sigs = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) - .tx_script_args(multisig_config_hash) + let tx_context_with_new_sigs = tx_context_builder .add_signature(new_public_keys[0].to_commitment(), msg, new_sig_1) .add_signature(new_public_keys[1].to_commitment(), msg, new_sig_2) - .auth_args(salt) - .extend_advice_inputs(advice_inputs.clone()) .build()?; // SECTION 4: Verify that only the CURRENT approvers can sign the update transaction diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index 629bd032dc..e72a6f6d96 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -185,14 +185,14 @@ async fn test_multisig_2_of_2_with_note_creation( let salt = Word::from([Felt::ONE; 4]); - // Execute transaction without signatures - should fail - let tx_context_init = mock_chain + // Build base transaction context + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .auth_args(salt) - .build()?; + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures - should fail + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -209,12 +209,9 @@ async fn test_multisig_2_of_2_with_note_creation( .await?; // Execute transaction with signatures - should succeed - let tx_context_execute = mock_chain - .build_tx_context(multisig_account.id(), &[input_note.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + let tx_context_execute = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) - .auth_args(salt) .build()? .execute() .await?; @@ -284,13 +281,12 @@ async fn test_multisig_2_of_4_all_signer_combinations( for (i, (signer1_idx, signer2_idx)) in signer_combinations.iter().enumerate() { let salt = Word::from([Felt::new_unchecked(10 + i as u64); 4]); - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .auth_args(salt) - .build()?; + // Build base transaction context + let tx_context_builder = + mock_chain.build_tx_context(multisig_account.id(), &[], &[])?.auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => anyhow::bail!("expected abort with tx effects: {error}"), }; @@ -307,9 +303,7 @@ async fn test_multisig_2_of_4_all_signer_combinations( .await?; // Execute transaction with signatures - should succeed for any combination - let tx_context_execute = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .auth_args(salt) + let tx_context_execute = tx_context_builder .add_signature(public_keys[*signer1_idx].to_commitment(), msg, sig_1) .add_signature(public_keys[*signer2_idx].to_commitment(), msg, sig_2) .build()?; @@ -361,13 +355,12 @@ async fn test_multisig_replay_protection(#[case] auth_scheme: AuthScheme) -> any let salt = Word::from([Felt::new_unchecked(3); 4]); - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .auth_args(salt) - .build()?; + // Build base transaction context + let tx_context_builder = + mock_chain.build_tx_context(multisig_account.id(), &[], &[])?.auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -384,11 +377,9 @@ async fn test_multisig_replay_protection(#[case] auth_scheme: AuthScheme) -> any .await?; // Execute transaction with signatures - should succeed (first execution) - let tx_context_execute = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? + let tx_context_execute = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1.clone()) .add_signature(public_keys[1].to_commitment(), msg, sig_2.clone()) - .auth_args(salt) .build()? .execute() .await?; @@ -398,6 +389,7 @@ async fn test_multisig_replay_protection(#[case] auth_scheme: AuthScheme) -> any mock_chain.prove_next_block()?; // Attempt to execute the same transaction again - should fail due to replay protection + // Must rebuild from the updated mock chain to pick up the new account state let tx_context_replay = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? .add_signature(public_keys[0].to_commitment(), msg, sig_1) @@ -488,24 +480,21 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow .with_dynamically_linked_library(AuthMultisig::code())? .compile_tx_script(tx_script_code)?; - let advice_inputs = AdviceInputs { - map: advice_map.clone(), - ..Default::default() - }; + let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; // Pass the MULTISIG_CONFIG_HASH as the tx_script_args let tx_script_args: Word = multisig_config_hash; - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain + // Build base transaction context + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) + .tx_script(tx_script) .tx_script_args(tx_script_args) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()?; + .extend_advice_inputs(advice_inputs) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -522,14 +511,9 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow .await?; // Execute transaction with signatures - should succeed - let update_approvers_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script) - .tx_script_args(multisig_config_hash) + let update_approvers_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) - .auth_args(salt) - .extend_advice_inputs(advice_inputs) .build()? .execute() .await?; @@ -633,14 +617,20 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow new_mock_chain_builder.add_output_note(RawOutputNote::Full(input_note_new.clone())); let new_mock_chain = new_mock_chain_builder.build().unwrap(); - // Execute transaction without signatures first to get tx summary - let tx_context_init_new = new_mock_chain + // Build base transaction context for the new signers + let tx_context_builder_new = new_mock_chain .build_tx_context(updated_multisig_account.id(), &[input_note_new.id()], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .auth_args(salt_new) - .build()?; + .auth_args(salt_new); - let tx_summary_new = match tx_context_init_new.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary_new = match tx_context_builder_new + .clone() + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) + .build()? + .execute() + .await + .unwrap_err() + { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -663,13 +653,11 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow // ================================================================================ // Execute transaction with new signatures - should succeed - let tx_context_execute_new = new_mock_chain - .build_tx_context(updated_multisig_account.id(), &[input_note_new.id()], &[])? + let tx_context_execute_new = tx_context_builder_new .extend_expected_output_notes(vec![RawOutputNote::Full(output_note_new)]) .add_signature(new_public_keys[0].to_commitment(), msg_new, sig_1_new) .add_signature(new_public_keys[1].to_commitment(), msg_new, sig_2_new) .add_signature(new_public_keys[2].to_commitment(), msg_new, sig_3_new) - .auth_args(salt_new) .build()? .execute() .await?; @@ -739,16 +727,16 @@ async fn test_multisig_update_signers_remove_owner( let salt = Word::from([Felt::new_unchecked(3); 4]); - // Execute without signatures to get tx summary - let tx_context_init = mock_chain + // Build base transaction context + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) + .tx_script(tx_script) .tx_script_args(multisig_config_hash) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()?; + .extend_advice_inputs(advice_inputs) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute without signatures to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -771,16 +759,11 @@ async fn test_multisig_update_signers_remove_owner( .await?; // Execute with signatures - let update_approvers_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script) - .tx_script_args(multisig_config_hash) + let update_approvers_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_1) .add_signature(public_keys[1].to_commitment(), msg, sig_2) .add_signature(public_keys[2].to_commitment(), msg, sig_3) .add_signature(public_keys[3].to_commitment(), msg, sig_4) - .auth_args(salt) - .extend_advice_inputs(advice_inputs) .build()? .execute() .await?; @@ -1009,24 +992,21 @@ async fn test_multisig_new_approvers_cannot_sign_before_update( .with_dynamically_linked_library(AuthMultisig::code())? .compile_tx_script(tx_script_code)?; - let advice_inputs = AdviceInputs { - map: advice_map.clone(), - ..Default::default() - }; + let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() }; // Pass the MULTISIG_CONFIG_HASH as the tx_script_args let tx_script_args: Word = multisig_config_hash; - // Execute transaction without signatures first to get tx summary - let tx_context_init = mock_chain + // Build base transaction context + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) + .tx_script(tx_script) .tx_script_args(tx_script_args) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()?; + .extend_advice_inputs(advice_inputs) + .auth_args(salt); - let tx_summary = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures first to get tx summary + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -1046,14 +1026,9 @@ async fn test_multisig_new_approvers_cannot_sign_before_update( .await?; // Try to execute transaction with NEW signatures - should FAIL - let tx_context_with_new_sigs = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(tx_script.clone()) - .tx_script_args(multisig_config_hash) + let tx_context_with_new_sigs = tx_context_builder .add_signature(new_public_keys[0].to_commitment(), msg, new_sig_1) .add_signature(new_public_keys[1].to_commitment(), msg, new_sig_2) - .auth_args(salt) - .extend_advice_inputs(advice_inputs.clone()) .build()?; // SECTION 4: Verify that only the CURRENT approvers can sign the update transaction @@ -1119,12 +1094,12 @@ async fn test_multisig_proc_threshold_overrides( // 2. consume without signatures let salt = Word::from([Felt::ONE; 4]); - let tx_context = mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[note.id()], &[])? - .auth_args(salt) - .build()?; + .auth_args(salt); - let tx_summary = match tx_context.execute().await.unwrap_err() { + // consume without signatures + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, error => panic!("expected abort with tx summary: {error:?}"), }; @@ -1137,10 +1112,8 @@ async fn test_multisig_proc_threshold_overrides( .await?; // 4. execute with signature - let tx_result = mock_chain - .build_tx_context(multisig_account.id(), &[note.id()], &[])? + let tx_result = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig) - .auth_args(salt) .build()? .execute() .await; @@ -1171,15 +1144,15 @@ async fn test_multisig_proc_threshold_overrides( let send_note_transaction_script = multisig_account_interface.build_send_notes_script(&[output_note.clone().into()], None)?; - // Execute transaction without signatures to get tx summary - let tx_context_init = mock_chain + // Build base transaction context for note sending + let tx_context_builder2 = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) - .tx_script(send_note_transaction_script.clone()) - .auth_args(salt2) - .build()?; + .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + .tx_script(send_note_transaction_script) + .auth_args(salt2); - let tx_summary2 = match tx_context_init.execute().await.unwrap_err() { + // Execute transaction without signatures to get tx summary + let tx_summary2 = match tx_context_builder2.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -1192,15 +1165,12 @@ async fn test_multisig_proc_threshold_overrides( .await?; // Try to execute with only 1 signature - should FAIL - let tx_context_one_sig = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) + let result = tx_context_builder2 + .clone() .add_signature(public_keys[0].to_commitment(), msg2, sig_1) - .tx_script(send_note_transaction_script.clone()) - .auth_args(salt2) - .build()?; - - let result = tx_context_one_sig.execute().await; + .build()? + .execute() + .await; match result { Err(TransactionExecutorError::Unauthorized(_)) => { // Expected: transaction should fail with insufficient signatures @@ -1219,13 +1189,9 @@ async fn test_multisig_proc_threshold_overrides( .await?; // Execute with 2 signatures - should SUCCEED - let result = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) + let result = tx_context_builder2 .add_signature(public_keys[0].to_commitment(), msg2, sig_1) .add_signature(public_keys[1].to_commitment(), msg2, sig_2) - .auth_args(salt2) - .tx_script(send_note_transaction_script) .build()? .execute() .await; @@ -1295,12 +1261,11 @@ async fn test_multisig_set_procedure_threshold( // 1) Set override to 1 (requires default 2 signatures). let set_salt = Word::from([Felt::new_unchecked(50); 4]); - let set_init = mock_chain + let set_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(set_script.clone()) - .auth_args(set_salt) - .build()?; - let set_summary = match set_init.execute().await.unwrap_err() { + .tx_script(set_script) + .auth_args(set_salt); + let set_summary = match set_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -1313,12 +1278,9 @@ async fn test_multisig_set_procedure_threshold( .get_signature(public_keys[1].to_commitment(), &set_summary) .await?; - let set_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(set_script) + let set_tx = set_builder .add_signature(public_keys[0].to_commitment(), set_msg, set_sig_1) .add_signature(public_keys[1].to_commitment(), set_msg, set_sig_2) - .auth_args(set_salt) .build()? .execute() .await?; @@ -1330,11 +1292,10 @@ async fn test_multisig_set_procedure_threshold( // 2) Verify receive_asset can now execute with one signature. let one_sig_salt = Word::from([Felt::new_unchecked(51); 4]); - let one_sig_init = mock_chain + let one_sig_builder = mock_chain .build_tx_context(multisig_account.id(), &[one_sig_note.id()], &[])? - .auth_args(one_sig_salt) - .build()?; - let one_sig_summary = match one_sig_init.execute().await.unwrap_err() { + .auth_args(one_sig_salt); + let one_sig_summary = match one_sig_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -1344,10 +1305,8 @@ async fn test_multisig_set_procedure_threshold( .get_signature(public_keys[0].to_commitment(), &one_sig_summary) .await?; - let one_sig_tx = mock_chain - .build_tx_context(multisig_account.id(), &[one_sig_note.id()], &[])? + let one_sig_tx = one_sig_builder .add_signature(public_keys[0].to_commitment(), one_sig_msg, one_sig) - .auth_args(one_sig_salt) .build()? .execute() .await @@ -1373,12 +1332,11 @@ async fn test_multisig_set_procedure_threshold( .compile_tx_script(clear_script_code)?; let clear_salt = Word::from([Felt::new_unchecked(52); 4]); - let clear_init = mock_chain + let clear_builder = mock_chain .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(clear_script.clone()) - .auth_args(clear_salt) - .build()?; - let clear_summary = match clear_init.execute().await.unwrap_err() { + .tx_script(clear_script) + .auth_args(clear_salt); + let clear_summary = match clear_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, error => panic!("expected abort with tx effects: {error:?}"), }; @@ -1391,12 +1349,9 @@ async fn test_multisig_set_procedure_threshold( .get_signature(public_keys[1].to_commitment(), &clear_summary) .await?; - let clear_tx = mock_chain - .build_tx_context(multisig_account.id(), &[], &[])? - .tx_script(clear_script) + let clear_tx = clear_builder .add_signature(public_keys[0].to_commitment(), clear_msg, clear_sig_1) .add_signature(public_keys[1].to_commitment(), clear_msg, clear_sig_2) - .auth_args(clear_salt) .build()? .execute() .await?; @@ -1408,24 +1363,22 @@ async fn test_multisig_set_procedure_threshold( // 4) After clear, one signature should no longer be sufficient for receive_asset. let clear_check_salt = Word::from([Felt::new_unchecked(53); 4]); - let clear_check_init = mock_chain + let clear_check_builder = mock_chain .build_tx_context(multisig_account.id(), &[clear_check_note.id()], &[])? - .auth_args(clear_check_salt) - .build()?; - let clear_check_summary = match clear_check_init.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + .auth_args(clear_check_salt); + let clear_check_summary = + match clear_check_builder.clone().build()?.execute().await.unwrap_err() { + TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, + error => panic!("expected abort with tx effects: {error:?}"), + }; let clear_check_msg = clear_check_summary.as_ref().to_commitment(); let clear_check_summary = SigningInputs::TransactionSummary(clear_check_summary); let clear_check_sig = authenticators[0] .get_signature(public_keys[0].to_commitment(), &clear_check_summary) .await?; - let clear_check_result = mock_chain - .build_tx_context(multisig_account.id(), &[clear_check_note.id()], &[])? + let clear_check_result = clear_check_builder .add_signature(public_keys[0].to_commitment(), clear_check_msg, clear_check_sig) - .auth_args(clear_check_salt) .build()? .execute() .await; diff --git a/crates/miden-testing/tests/auth/multisig_smart.rs b/crates/miden-testing/tests/auth/multisig_smart.rs index dbafddee0d..b05b476d0f 100644 --- a/crates/miden-testing/tests/auth/multisig_smart.rs +++ b/crates/miden-testing/tests/auth/multisig_smart.rs @@ -103,14 +103,11 @@ async fn test_multisig_smart_receive_asset_policy_overrides_default_three_of_thr let mut mock_chain = mock_chain_builder.build()?; let salt = Word::from([Felt::new_unchecked(1); 4]); - let tx_summary = match mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[note.id()], &[])? - .auth_args(salt) - .build()? - .execute() - .await - .unwrap_err() - { + .auth_args(salt); + + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, error => panic!("expected abort with tx summary: {error:?}"), }; @@ -121,10 +118,8 @@ async fn test_multisig_smart_receive_asset_policy_overrides_default_three_of_thr .get_signature(public_keys[0].to_commitment(), &tx_summary_signing) .await?; - let tx_result = mock_chain - .build_tx_context(multisig_account.id(), &[note.id()], &[])? + let tx_result = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, one_signature) - .auth_args(salt) .build()? .execute() .await; @@ -333,18 +328,15 @@ async fn test_multisig_smart_update_signers_and_thresholds( let salt = Word::from([Felt::new_unchecked(3); 4]); - // Dry-run to obtain the tx summary that the current approvers must sign. - let tx_summary = match mock_chain + let tx_context_builder = mock_chain .build_tx_context(account_id, &[], &[])? - .tx_script(update_signers_script.clone()) + .tx_script(update_signers_script) .tx_script_args(multisig_config_hash) - .extend_advice_inputs(advice_inputs.clone()) - .auth_args(salt) - .build()? - .execute() - .await - .unwrap_err() - { + .extend_advice_inputs(advice_inputs) + .auth_args(salt); + + // Dry-run to obtain the tx summary that the current approvers must sign. + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, error => panic!("expected abort with tx summary: {error:?}"), }; @@ -358,12 +350,7 @@ async fn test_multisig_smart_update_signers_and_thresholds( .get_signature(public_keys[1].to_commitment(), &signing_inputs) .await?; - let executed_tx = mock_chain - .build_tx_context(account_id, &[], &[])? - .tx_script(update_signers_script) - .tx_script_args(multisig_config_hash) - .extend_advice_inputs(advice_inputs) - .auth_args(salt) + let executed_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_0) .add_signature(public_keys[1].to_commitment(), msg, sig_1) .build()? @@ -439,16 +426,13 @@ async fn test_multisig_smart_set_procedure_policy( let salt = Word::from([Felt::new_unchecked(4); 4]); - // Dry-run to obtain the tx summary that the approvers must sign. - let tx_summary = match mock_chain + let tx_context_builder = mock_chain .build_tx_context(account_id, &[], &[])? - .tx_script(set_policy_script.clone()) - .auth_args(salt) - .build()? - .execute() - .await - .unwrap_err() - { + .tx_script(set_policy_script) + .auth_args(salt); + + // Dry-run to obtain the tx summary that the approvers must sign. + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, error => panic!("expected abort with tx summary: {error:?}"), }; @@ -462,10 +446,7 @@ async fn test_multisig_smart_set_procedure_policy( .get_signature(public_keys[1].to_commitment(), &signing_inputs) .await?; - let executed_tx = mock_chain - .build_tx_context(account_id, &[], &[])? - .tx_script(set_policy_script) - .auth_args(salt) + let executed_tx = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_0) .add_signature(public_keys[1].to_commitment(), msg, sig_1) .build()? @@ -546,16 +527,13 @@ async fn test_multisig_smart_unpolicied_proc_call_requires_default_threshold() - let salt = Word::from([Felt::new_unchecked(42); 4]); - // Dry-run to capture the tx summary. - let tx_summary = match mock_chain + let tx_context_builder = mock_chain .build_tx_context(multisig_account.id(), &[note.id()], &[])? - .tx_script(set_policy_script.clone()) - .auth_args(salt) - .build()? - .execute() - .await - .unwrap_err() - { + .tx_script(set_policy_script) + .auth_args(salt); + + // Dry-run to capture the tx summary. + let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, error => panic!("expected dry-run abort with tx summary: {error:?}"), }; @@ -574,10 +552,8 @@ async fn test_multisig_smart_unpolicied_proc_call_requires_default_threshold() - // With only 1 signature (matching the low receive_asset policy), the tx must fail because // the unpolicied set_procedure_policy call contributes `default_threshold = 3`. - let one_sig_result = mock_chain - .build_tx_context(multisig_account.id(), &[note.id()], &[])? - .tx_script(set_policy_script.clone()) - .auth_args(salt) + let one_sig_result = tx_context_builder + .clone() .add_signature(public_keys[0].to_commitment(), msg, sig_0.clone()) .build()? .execute() @@ -590,10 +566,7 @@ async fn test_multisig_smart_unpolicied_proc_call_requires_default_threshold() - } // With all 3 signatures the unpolicied default contribution is met and the tx succeeds. - let three_sig_result = mock_chain - .build_tx_context(multisig_account.id(), &[note.id()], &[])? - .tx_script(set_policy_script) - .auth_args(salt) + let three_sig_result = tx_context_builder .add_signature(public_keys[0].to_commitment(), msg, sig_0) .add_signature(public_keys[1].to_commitment(), msg, sig_1) .add_signature(public_keys[2].to_commitment(), msg, sig_2) From c8e701fb3808511ab3a62dad8ab52e3d8012e55b Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 26 May 2026 11:02:07 +0200 Subject: [PATCH 2/2] chore: add unwrap_unauthorized_err --- .../tests/auth/guarded_multisig.rs | 55 ++++--- .../tests/auth/hybrid_multisig.rs | 63 +++++--- crates/miden-testing/tests/auth/multisig.rs | 150 ++++++++++-------- .../tests/auth/multisig_smart.rs | 62 ++++---- crates/miden-tx/src/errors/mod.rs | 10 ++ 5 files changed, 201 insertions(+), 139 deletions(-) diff --git a/crates/miden-testing/tests/auth/guarded_multisig.rs b/crates/miden-testing/tests/auth/guarded_multisig.rs index 4676a29f77..52967863ca 100644 --- a/crates/miden-testing/tests/auth/guarded_multisig.rs +++ b/crates/miden-testing/tests/auth/guarded_multisig.rs @@ -204,10 +204,13 @@ async fn test_guarded_multisig_signature_required( .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) .auth_args(salt); - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let tx_summary_signing = SigningInputs::TransactionSummary(tx_summary); @@ -311,10 +314,13 @@ async fn test_guarded_multisig_update_guardian_public_key( .tx_script(update_guardian_script) .auth_args(update_salt); - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let update_msg = tx_summary.as_ref().to_commitment(); let tx_summary_signing = SigningInputs::TransactionSummary(tx_summary); @@ -462,10 +468,13 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( .tx_script(update_guardian_script) .auth_args(salt); - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let tx_summary_signing = SigningInputs::TransactionSummary(tx_summary); @@ -541,10 +550,13 @@ async fn test_guarded_multisig_update_guardian_public_key_must_be_called_alone( .extend_expected_output_notes(vec![RawOutputNote::Full(output_note)]) .auth_args(salt); - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let tx_summary_signing = SigningInputs::TransactionSummary(tx_summary); @@ -673,10 +685,13 @@ async fn test_guarded_multisig_update_guardian_enforces_no_notes( tx_context_builder = tx_context_builder.extend_expected_output_notes(vec![RawOutputNote::Full(out)]); } - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected dry-run abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let signing = SigningInputs::TransactionSummary(tx_summary); diff --git a/crates/miden-testing/tests/auth/hybrid_multisig.rs b/crates/miden-testing/tests/auth/hybrid_multisig.rs index 2dc3612cc6..fc1c6bc657 100644 --- a/crates/miden-testing/tests/auth/hybrid_multisig.rs +++ b/crates/miden-testing/tests/auth/hybrid_multisig.rs @@ -15,7 +15,6 @@ use miden_standards::note::P2idNote; use miden_standards::testing::account_interface::get_public_keys_from_account; use miden_testing::utils::create_spawn_note; use miden_testing::{Auth, MockChainBuilder}; -use miden_tx::TransactionExecutorError; use miden_tx::auth::{BasicAuthenticator, SigningInputs, TransactionAuthenticator}; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -147,10 +146,13 @@ async fn test_multisig_2_of_2_with_note_creation() -> anyhow::Result<()> { .auth_args(salt); // Execute transaction without signatures - should fail - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from both approvers let msg = tx_summary.as_ref().to_commitment(); @@ -230,10 +232,13 @@ async fn test_multisig_2_of_4_all_signer_combinations() -> anyhow::Result<()> { mock_chain.build_tx_context(multisig_account.id(), &[], &[])?.auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from the specific combination of signers let msg = tx_summary.as_ref().to_commitment(); @@ -369,10 +374,13 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { .auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from both approvers let msg = tx_summary.as_ref().to_commitment(); @@ -499,17 +507,14 @@ async fn test_multisig_update_signers() -> anyhow::Result<()> { .auth_args(salt_new); // Execute transaction without signatures first to get tx summary - let tx_summary_new = match tx_context_builder_new + let tx_summary_new = tx_context_builder_new .clone() .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) .build()? .execute() .await .unwrap_err() - { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + .unwrap_unauthorized_err(); // Get signatures from 3 of the 4 new approvers (threshold is 3) let msg_new = tx_summary_new.as_ref().to_commitment(); @@ -624,10 +629,13 @@ async fn test_multisig_update_signers_remove_owner() -> anyhow::Result<()> { .auth_args(salt); // Execute without signatures to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from 4 of the 5 original approvers (threshold is 4) let msg = tx_summary.as_ref().to_commitment(); @@ -845,10 +853,13 @@ async fn test_multisig_new_approvers_cannot_sign_before_update() -> anyhow::Resu .auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // SECTION 3: Try to sign the transaction with the NEW approvers (should fail) // ================================================================================ diff --git a/crates/miden-testing/tests/auth/multisig.rs b/crates/miden-testing/tests/auth/multisig.rs index e72a6f6d96..710c893458 100644 --- a/crates/miden-testing/tests/auth/multisig.rs +++ b/crates/miden-testing/tests/auth/multisig.rs @@ -192,10 +192,13 @@ async fn test_multisig_2_of_2_with_note_creation( .auth_args(salt); // Execute transaction without signatures - should fail - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from both approvers let msg = tx_summary.as_ref().to_commitment(); @@ -286,10 +289,13 @@ async fn test_multisig_2_of_4_all_signer_combinations( mock_chain.build_tx_context(multisig_account.id(), &[], &[])?.auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => anyhow::bail!("expected abort with tx effects: {error}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from the specific combination of signers let msg = tx_summary.as_ref().to_commitment(); @@ -360,10 +366,13 @@ async fn test_multisig_replay_protection(#[case] auth_scheme: AuthScheme) -> any mock_chain.build_tx_context(multisig_account.id(), &[], &[])?.auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from 2 of the 3 approvers let msg = tx_summary.as_ref().to_commitment(); @@ -494,10 +503,13 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow .auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from both approvers let msg = tx_summary.as_ref().to_commitment(); @@ -623,17 +635,14 @@ async fn test_multisig_update_signers(#[case] auth_scheme: AuthScheme) -> anyhow .auth_args(salt_new); // Execute transaction without signatures first to get tx summary - let tx_summary_new = match tx_context_builder_new + let tx_summary_new = tx_context_builder_new .clone() .extend_expected_output_notes(vec![RawOutputNote::Full(output_note.clone())]) .build()? .execute() .await .unwrap_err() - { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + .unwrap_unauthorized_err(); // Get signatures from 3 of the 4 new approvers (threshold is 3) let msg_new = tx_summary_new.as_ref().to_commitment(); @@ -736,10 +745,13 @@ async fn test_multisig_update_signers_remove_owner( .auth_args(salt); // Execute without signatures to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signatures from 4 of the 5 original approvers (threshold is 4) let msg = tx_summary.as_ref().to_commitment(); @@ -1006,10 +1018,13 @@ async fn test_multisig_new_approvers_cannot_sign_before_update( .auth_args(salt); // Execute transaction without signatures first to get tx summary - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // SECTION 3: Try to sign the transaction with the NEW approvers (should fail) // ================================================================================ @@ -1099,10 +1114,13 @@ async fn test_multisig_proc_threshold_overrides( .auth_args(salt); // consume without signatures - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, - error => panic!("expected abort with tx summary: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // 3. get signature from one approver let msg = tx_summary.as_ref().to_commitment(); @@ -1152,10 +1170,13 @@ async fn test_multisig_proc_threshold_overrides( .auth_args(salt2); // Execute transaction without signatures to get tx summary - let tx_summary2 = match tx_context_builder2.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let tx_summary2 = tx_context_builder2 + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); // Get signature from only ONE approver let msg2 = tx_summary2.as_ref().to_commitment(); let tx_summary2_signing = SigningInputs::TransactionSummary(tx_summary2.clone()); @@ -1171,14 +1192,8 @@ async fn test_multisig_proc_threshold_overrides( .build()? .execute() .await; - match result { - Err(TransactionExecutorError::Unauthorized(_)) => { - // Expected: transaction should fail with insufficient signatures - }, - _ => panic!( - "Transaction should fail with Unauthorized error when only 1 signature provided for note sending" - ), - } + // Expected: transaction should fail with insufficient signatures + result.unwrap_err().unwrap_unauthorized_err(); // Now get signatures from BOTH approvers let sig_1 = authenticators[0] @@ -1265,10 +1280,13 @@ async fn test_multisig_set_procedure_threshold( .build_tx_context(multisig_account.id(), &[], &[])? .tx_script(set_script) .auth_args(set_salt); - let set_summary = match set_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let set_summary = set_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let set_msg = set_summary.as_ref().to_commitment(); let set_summary = SigningInputs::TransactionSummary(set_summary); let set_sig_1 = authenticators[0] @@ -1295,10 +1313,13 @@ async fn test_multisig_set_procedure_threshold( let one_sig_builder = mock_chain .build_tx_context(multisig_account.id(), &[one_sig_note.id()], &[])? .auth_args(one_sig_salt); - let one_sig_summary = match one_sig_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let one_sig_summary = one_sig_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let one_sig_msg = one_sig_summary.as_ref().to_commitment(); let one_sig_summary = SigningInputs::TransactionSummary(one_sig_summary); let one_sig = authenticators[0] @@ -1336,10 +1357,13 @@ async fn test_multisig_set_procedure_threshold( .build_tx_context(multisig_account.id(), &[], &[])? .tx_script(clear_script) .auth_args(clear_salt); - let clear_summary = match clear_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let clear_summary = clear_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let clear_msg = clear_summary.as_ref().to_commitment(); let clear_summary = SigningInputs::TransactionSummary(clear_summary); let clear_sig_1 = authenticators[0] @@ -1366,11 +1390,13 @@ async fn test_multisig_set_procedure_threshold( let clear_check_builder = mock_chain .build_tx_context(multisig_account.id(), &[clear_check_note.id()], &[])? .auth_args(clear_check_salt); - let clear_check_summary = - match clear_check_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, - error => panic!("expected abort with tx effects: {error:?}"), - }; + let clear_check_summary = clear_check_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let clear_check_msg = clear_check_summary.as_ref().to_commitment(); let clear_check_summary = SigningInputs::TransactionSummary(clear_check_summary); let clear_check_sig = authenticators[0] diff --git a/crates/miden-testing/tests/auth/multisig_smart.rs b/crates/miden-testing/tests/auth/multisig_smart.rs index b05b476d0f..da5f38ad50 100644 --- a/crates/miden-testing/tests/auth/multisig_smart.rs +++ b/crates/miden-testing/tests/auth/multisig_smart.rs @@ -19,7 +19,6 @@ use miden_standards::errors::standards::{ ERR_AUTH_TRANSACTION_MUST_NOT_INCLUDE_OUTPUT_NOTES, }; use miden_testing::{MockChainBuilder, assert_transaction_executor_error}; -use miden_tx::TransactionExecutorError; use miden_tx::auth::{SigningInputs, TransactionAuthenticator}; use rstest::rstest; @@ -107,10 +106,13 @@ async fn test_multisig_smart_receive_asset_policy_overrides_default_three_of_thr .build_tx_context(multisig_account.id(), &[note.id()], &[])? .auth_args(salt); - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, - error => panic!("expected abort with tx summary: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let tx_summary_signing = SigningInputs::TransactionSummary(tx_summary); @@ -194,10 +196,7 @@ async fn test_multisig_smart_enforces_note_restrictions_on_tx_with_input_notes( ); }, ProcedurePolicyNoteRestriction::None | ProcedurePolicyNoteRestriction::NoOutputNotes => { - match result { - Err(TransactionExecutorError::Unauthorized(_)) => {}, - other => panic!("expected Unauthorized (no signatures provided), got: {other:?}"), - } + result.unwrap_err().unwrap_unauthorized_err(); }, } @@ -272,10 +271,7 @@ async fn test_multisig_smart_enforces_note_restrictions_on_tx_with_output_notes( ); }, ProcedurePolicyNoteRestriction::None | ProcedurePolicyNoteRestriction::NoInputNotes => { - match result { - Err(TransactionExecutorError::Unauthorized(_)) => {}, - other => panic!("expected Unauthorized (no signatures provided), got: {other:?}"), - } + result.unwrap_err().unwrap_unauthorized_err(); }, } @@ -336,10 +332,13 @@ async fn test_multisig_smart_update_signers_and_thresholds( .auth_args(salt); // Dry-run to obtain the tx summary that the current approvers must sign. - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, - error => panic!("expected abort with tx summary: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let signing_inputs = SigningInputs::TransactionSummary(tx_summary); @@ -432,10 +431,13 @@ async fn test_multisig_smart_set_procedure_policy( .auth_args(salt); // Dry-run to obtain the tx summary that the approvers must sign. - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, - error => panic!("expected abort with tx summary: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let signing_inputs = SigningInputs::TransactionSummary(tx_summary); @@ -533,10 +535,13 @@ async fn test_multisig_smart_unpolicied_proc_call_requires_default_threshold() - .auth_args(salt); // Dry-run to capture the tx summary. - let tx_summary = match tx_context_builder.clone().build()?.execute().await.unwrap_err() { - TransactionExecutorError::Unauthorized(tx_summary) => tx_summary, - error => panic!("expected dry-run abort with tx summary: {error:?}"), - }; + let tx_summary = tx_context_builder + .clone() + .build()? + .execute() + .await + .unwrap_err() + .unwrap_unauthorized_err(); let msg = tx_summary.as_ref().to_commitment(); let signing = SigningInputs::TransactionSummary(tx_summary); @@ -558,12 +563,7 @@ async fn test_multisig_smart_unpolicied_proc_call_requires_default_threshold() - .build()? .execute() .await; - match one_sig_result { - Err(TransactionExecutorError::Unauthorized(_)) => {}, - other => { - panic!("expected Unauthorized with 1 sig (escalation would let it pass): {other:?}") - }, - } + one_sig_result.unwrap_err().unwrap_unauthorized_err(); // With all 3 signatures the unpolicied default contribution is met and the tx succeeds. let three_sig_result = tx_context_builder diff --git a/crates/miden-tx/src/errors/mod.rs b/crates/miden-tx/src/errors/mod.rs index f56aea7131..4a57afacc6 100644 --- a/crates/miden-tx/src/errors/mod.rs +++ b/crates/miden-tx/src/errors/mod.rs @@ -150,6 +150,16 @@ pub enum TransactionExecutorError { MissingAuthenticator, } +#[cfg(any(test, feature = "testing"))] +impl TransactionExecutorError { + pub fn unwrap_unauthorized_err(self) -> Box { + match self { + TransactionExecutorError::Unauthorized(transaction_summary) => transaction_summary, + other => panic!("expected TransactionExecutorError::Unauthorized, got {other}"), + } + } +} + // TRANSACTION PROVER ERROR // ================================================================================================