diff --git a/contracts/smart-account/src/tests/auth_test.rs b/contracts/smart-account/src/tests/auth_test.rs index b342dbe..66aa9c7 100644 --- a/contracts/smart-account/src/tests/auth_test.rs +++ b/contracts/smart-account/src/tests/auth_test.rs @@ -10,8 +10,8 @@ use crate::{ }, error::Error, tests::test_utils::{ - get_token_auth_context, get_update_signer_auth_context, setup, Ed25519TestSigner, - TestSignerTrait as _, + budget_snapshot, get_token_auth_context, get_update_signer_auth_context, + print_budget_delta, setup, Ed25519TestSigner, TestSignerTrait as _, }, }; @@ -31,6 +31,7 @@ fn test_auth_ed25519_happy_case() { let payload = BytesN::random(&env); let (signer_key, proof) = test_signer.sign(&env, &payload); let auth_payloads = SignatureProofs(map![&env, (signer_key.clone(), proof.clone())]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -38,6 +39,8 @@ fn test_auth_ed25519_happy_case() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } #[test] @@ -89,6 +92,7 @@ fn test_auth_ed25519_wrong_signature() { panic!("Invalid proof type"); }; let auth_payloads = SignatureProofs(map![&env, (signer_key.clone(), wrong_proof.clone())]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -96,6 +100,8 @@ fn test_auth_ed25519_wrong_signature() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } #[test] @@ -170,6 +176,7 @@ fn test_auth_multi_signature_admin_and_standard() { (standard_key.clone(), standard_proof.clone()) ]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -177,6 +184,8 @@ fn test_auth_multi_signature_admin_and_standard() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } #[test] @@ -202,6 +211,7 @@ fn test_auth_multi_signature_only_admin_needed() { let auth_payloads = SignatureProofs(map![&env, (admin_key.clone(), admin_proof.clone())]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -209,6 +219,8 @@ fn test_auth_multi_signature_only_admin_needed() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } #[test] @@ -234,6 +246,7 @@ fn test_auth_multi_signature_only_standard_needed() { let auth_payloads = SignatureProofs(map![&env, (standard_key.clone(), standard_proof.clone())]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -241,6 +254,8 @@ fn test_auth_multi_signature_only_standard_needed() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } // ============================================================================ @@ -352,6 +367,7 @@ fn test_auth_time_based_policy_within_window() { (restricted_key.clone(), restricted_proof.clone()) ]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -359,6 +375,8 @@ fn test_auth_time_based_policy_within_window() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } #[test] @@ -518,6 +536,7 @@ fn test_auth_idempotency() { ) .unwrap(); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &contract_id, &payload, @@ -525,6 +544,8 @@ fn test_auth_idempotency() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("check_auth", &b, &a); } #[test] diff --git a/contracts/smart-account/src/tests/plugin_test.rs b/contracts/smart-account/src/tests/plugin_test.rs index 7c814c8..d36096d 100644 --- a/contracts/smart-account/src/tests/plugin_test.rs +++ b/contracts/smart-account/src/tests/plugin_test.rs @@ -12,7 +12,10 @@ use crate::{ auth::{permissions::SignerRole, proof::SignatureProofs}, error::Error, interface::SmartAccountInterface, - tests::test_utils::{get_token_auth_context, setup, Ed25519TestSigner, TestSignerTrait as _}, + tests::test_utils::{ + budget_snapshot, get_token_auth_context, print_budget_delta, setup, Ed25519TestSigner, + TestSignerTrait as _, + }, }; // ----------------------------------------------------------------------------- @@ -67,10 +70,13 @@ fn test_uninstall_plugin_persists_removal() { let plugin_id = env.register(DummyPlugin, ()); // Install plugin + let b = budget_snapshot(&env); env.as_contract(&smart_account_id, || { SmartAccount::install_plugin(&env, plugin_id.clone()) }) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("install_plugin", &b, &a); // Verify plugin is installed by triggering on_auth let payload = BytesN::random(&env); @@ -80,6 +86,7 @@ fn test_uninstall_plugin_persists_removal() { (admin_key.clone(), admin_proof.clone()) ]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &smart_account_id, &payload, @@ -87,6 +94,8 @@ fn test_uninstall_plugin_persists_removal() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("__check_auth:plugin_on_auth", &b, &a); // Verify plugin is installed assert!(env.as_contract(&smart_account_id, || { @@ -101,10 +110,13 @@ fn test_uninstall_plugin_persists_removal() { ); // Uninstall plugin (FIX: removal now persisted to storage) + let b = budget_snapshot(&env); env.as_contract(&smart_account_id, || { SmartAccount::uninstall_plugin(&env, plugin_id.clone()) }) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("uninstall_plugin", &b, &a); // Verify plugin is uninstalled assert!(!env.as_contract(&smart_account_id, || { @@ -116,6 +128,7 @@ fn test_uninstall_plugin_persists_removal() { let (admin_key2, admin_proof2) = admin.sign(&env, &payload2); let auth_payloads2 = SignatureProofs(soroban_sdk::map![&env, (admin_key2, admin_proof2)]); + let b = budget_snapshot(&env); env.try_invoke_contract_check_auth::( &smart_account_id, &payload2, @@ -123,6 +136,8 @@ fn test_uninstall_plugin_persists_removal() { &vec![&env, get_token_auth_context(&env)], ) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("__check_auth:after_uninstall", &b, &a); // Verify plugin did NOT receive second on_auth call (count should still be 1) let count_after_uninstall = env.as_contract(&plugin_id, || DummyPlugin::get_count(&env)); diff --git a/contracts/smart-account/src/tests/policy_test.rs b/contracts/smart-account/src/tests/policy_test.rs index 3d9665a..2097b12 100644 --- a/contracts/smart-account/src/tests/policy_test.rs +++ b/contracts/smart-account/src/tests/policy_test.rs @@ -12,7 +12,7 @@ use crate::auth::permissions::{SignerPolicy, SignerRole}; use crate::auth::policy::{ExternalPolicy, TimeBasedPolicy}; use crate::auth::signer::{Signer, SignerKey}; use crate::error::Error; -use crate::tests::test_utils::{setup, Ed25519TestSigner}; +use crate::tests::test_utils::{budget_snapshot, print_budget_delta, setup, Ed25519TestSigner}; #[contract] pub struct DummyExternalPolicy; @@ -187,6 +187,7 @@ fn test_revoke_signer_with_external_polic_calls_on_revoke() { ), ); env.mock_all_auths(); + let b = budget_snapshot(&env); env.as_contract(&account_id, || { if let Signer::Ed25519(signer, _) = test_signer { let signer_key = SignerKey::Ed25519(signer.public_key); @@ -196,6 +197,8 @@ fn test_revoke_signer_with_external_polic_calls_on_revoke() { } }) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("revoke_signer:with_external_policy", &b, &a); ensure_policy_event_is_emmited(&env, policy_id, symbol_short!("ON_REVOKE")); } @@ -232,10 +235,13 @@ fn test_update_signer_with_external_polic_lifecycle() { ensure_policy_event_is_emmited(&env, policy_id_1.clone(), symbol_short!("ON_ADD")); + let b = budget_snapshot(&env); env.as_contract(&account_id, || { SmartAccount::update_signer(&env, test_signer_2) }) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("update_signer:external_policy_lifecycle", &b, &a); ensure_policy_event_is_emmited(&env, policy_id_1, symbol_short!("ON_REVOKE")); ensure_policy_event_is_emmited(&env, policy_id_2, symbol_short!("ON_ADD")); } else { @@ -266,9 +272,12 @@ fn test_update_signer_with_external_polic_lifecycle_with_same_policy() { ensure_policy_event_is_emmited(&env, policy_id_1.clone(), symbol_short!("ON_ADD")); + let b = budget_snapshot(&env); env.as_contract(&account_id, || { SmartAccount::update_signer(&env, test_signer_1) }) .unwrap(); + let a = budget_snapshot(&env); + print_budget_delta("update_signer:same_policy", &b, &a); ensure_policy_event_is_not_emmited(&env, policy_id_1.clone(), symbol_short!("ON_REVOKE")); } diff --git a/contracts/smart-account/src/tests/signer_management_test.rs b/contracts/smart-account/src/tests/signer_management_test.rs index 5350756..97aa3a2 100644 --- a/contracts/smart-account/src/tests/signer_management_test.rs +++ b/contracts/smart-account/src/tests/signer_management_test.rs @@ -7,7 +7,9 @@ use crate::{ auth::{permissions::SignerRole, proof::SignatureProofs, signer::SignerKey}, error::Error, interface::SmartAccountInterface, - tests::test_utils::{setup, Ed25519TestSigner, TestSignerTrait as _}, + tests::test_utils::{ + budget_snapshot, print_budget_delta, setup, Ed25519TestSigner, TestSignerTrait as _, + }, }; extern crate std; @@ -37,9 +39,12 @@ fn test_revoke_admin_signer_prevented() { let admin_signer_key = SignerKey::Ed25519(admin_signer.public_key(&env)); env.mock_all_auths(); + let b = budget_snapshot(&env); let result = env.as_contract(&contract_id, || { SmartAccount::revoke_signer(&env, admin_signer_key) }); + let a = budget_snapshot(&env); + print_budget_delta("revoke_signer:admin", &b, &a); assert_eq!(result.unwrap_err(), Error::CannotRevokeAdminSigner); } @@ -69,9 +74,12 @@ fn test_revoke_standard_signer_allowed() { let standard_signer_key = SignerKey::Ed25519(standard_signer.public_key(&env)); env.mock_all_auths(); + let b = budget_snapshot(&env); let result = env.as_contract(&contract_id, || { SmartAccount::revoke_signer(&env, standard_signer_key) }); + let a = budget_snapshot(&env); + print_budget_delta("revoke_signer:standard", &b, &a); assert!(result.is_ok()); } @@ -96,9 +104,12 @@ fn test_add_multiple_admin_signers_success() { // Call add_signer as contract (auth mocked) to simulate admin operation env.mock_all_auths(); + let b = budget_snapshot(&env); let result = env.as_contract(&contract_id, || { SmartAccount::add_signer(&env, admin2_signer.clone()) }); + let a = budget_snapshot(&env); + print_budget_delta("add_signer:admin2", &b, &a); // Should succeed - this tests that the admin count arithmetic works correctly assert!(result.is_ok(), "Adding second admin should succeed"); @@ -107,9 +118,12 @@ fn test_add_multiple_admin_signers_success() { let admin3 = Ed25519TestSigner::generate(SignerRole::Admin); let admin3_signer = admin3.into_signer(&env); + let b = budget_snapshot(&env); let result2 = env.as_contract(&contract_id, || { SmartAccount::add_signer(&env, admin3_signer) }); + let a = budget_snapshot(&env); + print_budget_delta("add_signer:admin3", &b, &a); assert!(result2.is_ok(), "Adding third admin should also succeed"); } @@ -133,9 +147,12 @@ fn test_admin_count_underflow_protection() { let admin1_standard = Ed25519TestSigner(admin1.0, SignerRole::Standard(vec![&env])); env.mock_all_auths(); + let b = budget_snapshot(&env); let result = env.as_contract(&contract_id, || { SmartAccount::update_signer(&env, admin1_standard.into_signer(&env)) }); + let a = budget_snapshot(&env); + print_budget_delta("update_signer:admin1_to_standard", &b, &a); assert!( result.is_ok(), @@ -145,9 +162,12 @@ fn test_admin_count_underflow_protection() { // Try to downgrade the last admin (should fail) let admin2_standard = Ed25519TestSigner(admin2.0, SignerRole::Standard(vec![&env])); + let b = budget_snapshot(&env); let result2 = env.as_contract(&contract_id, || { SmartAccount::update_signer(&env, admin2_standard.into_signer(&env)) }); + let a = budget_snapshot(&env); + print_budget_delta("update_signer:admin2_to_standard", &b, &a); assert_eq!( result2.unwrap_err(), diff --git a/contracts/smart-account/src/tests/test_utils.rs b/contracts/smart-account/src/tests/test_utils.rs index 338591f..7507250 100644 --- a/contracts/smart-account/src/tests/test_utils.rs +++ b/contracts/smart-account/src/tests/test_utils.rs @@ -1,4 +1,5 @@ #![cfg(test)] +extern crate std; use ed25519_dalek::Keypair; use ed25519_dalek::Signer as _; @@ -77,3 +78,27 @@ impl TestSignerTrait for Ed25519TestSigner { (signer_key, signature) } } + +pub struct BudgetSnapshot { + pub cpu: u64, + pub mem: u64, +} + +pub fn budget_snapshot(env: &Env) -> BudgetSnapshot { + let budget = env.cost_estimate().budget(); + BudgetSnapshot { + cpu: budget.cpu_instruction_cost(), + mem: budget.memory_bytes_cost(), + } +} + +pub fn print_budget_delta(label: &str, before: &BudgetSnapshot, after: &BudgetSnapshot) { + let cpu_delta = after.cpu.saturating_sub(before.cpu); + let mem_delta = after.mem.saturating_sub(before.mem); + std::println!( + "[Budget] {} cpu_insns={} mem_bytes={}", + label, + cpu_delta, + mem_delta + ); +}