From d6aa70713c552bdb55165660b19866a469329fa5 Mon Sep 17 00:00:00 2001 From: mariaKt Date: Mon, 23 Feb 2026 10:06:24 -0600 Subject: [PATCH 1/6] Added version of inner_test_validate_owner that just computes the expected result. --- specs/shared/inner_test_validate_owner.rs | 130 +++++++++++++++++++--- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/specs/shared/inner_test_validate_owner.rs b/specs/shared/inner_test_validate_owner.rs index 5f25fdf4..a1109392 100644 --- a/specs/shared/inner_test_validate_owner.rs +++ b/specs/shared/inner_test_validate_owner.rs @@ -1,3 +1,86 @@ +/// Computes the expected result of mod.rs::validate_owner. +#[inline(never)] +fn expected_validate_owner_result( + expected_owner: &Pubkey, + owner_account_info: &AccountInfo, + tx_signers: &[AccountInfo], + maybe_multisig_is_initialised: Option>, +) -> Result<(), ProgramError> { + if expected_owner != key!(owner_account_info) { + return Err(ProgramError::Custom(4)); + } + // We add the `maybe_multisig_is_initialised.is_some()` to not branch vacuously in the + // non-multisig cases + else if maybe_multisig_is_initialised.is_some() + && owner_account_info.data_len() == Multisig::LEN + && (owner!(owner_account_info) == &PROGRAM_ID) + { + // Guaranteed to succeed by `cheatcode_is_multisig` + let multisig_is_initialised = maybe_multisig_is_initialised.unwrap(); + if multisig_is_initialised.is_err() { + return Err(ProgramError::InvalidAccountData); + } else if !multisig_is_initialised.unwrap() { + return Err(ProgramError::UninitializedAccount); + } else { + let multisig = get_multisig(owner_account_info); + + // Did all declared and allowd signers sign? + // let unsigned_exists = tx_signers.iter().any(|potential_signer| { + // multisig.signers.iter().any(|registered_key| { + // registered_key == key!(potential_signer) && !is_signer!(potential_signer) + // }) + // }); + let mut unsigned_exists = false; + for potential_signer in tx_signers.iter() { + for registered_key in multisig.signers.iter() { + if registered_key == key!(potential_signer) && !is_signer!(potential_signer) { + unsigned_exists = true; + break; + } + } + if unsigned_exists { + break; + } + } + if unsigned_exists { + return Err(ProgramError::MissingRequiredSignature); + } + + // Were enough signatures received? + // let signers_count = multisig.signers + // .iter() + // .filter_map(|registered_key| { + // tx_signers.iter().find(|potential_signer| { + // key!(potential_signer) == registered_key && is_signer!(potential_signer) + // }) + // }) + // .count(); + let mut signers_count: usize = 0; + for registered_key in multisig.signers.iter() { + for potential_signer in tx_signers.iter() { + if key!(potential_signer) == registered_key && is_signer!(potential_signer) { + signers_count += 1; + break; + } + } + } + + // Check if we have enough signers + if signers_count < multisig.m as usize { + return Err(ProgramError::MissingRequiredSignature); + } + + return Ok(()); + } + } + // Non-multisig case - check if owner_account_info.is_signer() + else if !is_signer!(owner_account_info) { + return Err(ProgramError::MissingRequiredSignature); + } + + Ok(()) +} + /// This function encapsulates the specification of validating the signature /// requirements In particular, code from mod.rs::validate_owner is checked #[inline(never)] @@ -30,25 +113,46 @@ fn inner_test_validate_owner( let multisig = get_multisig(owner_account_info); // Did all declared and allowd signers sign? - let unsigned_exists = tx_signers.iter().any(|potential_signer| { - multisig.signers.iter().any(|registered_key| { - registered_key == key!(potential_signer) && !is_signer!(potential_signer) - }) - }); + // let unsigned_exists = tx_signers.iter().any(|potential_signer| { + // multisig.signers.iter().any(|registered_key| { + // registered_key == key!(potential_signer) && !is_signer!(potential_signer) + // }) + // }); + let mut unsigned_exists = false; + for potential_signer in tx_signers.iter() { + for registered_key in multisig.signers.iter() { + if registered_key == key!(potential_signer) && !is_signer!(potential_signer) { + unsigned_exists = true; + break; + } + } + if unsigned_exists { + break; + } + } if unsigned_exists { assert_eq!(result, Err(ProgramError::MissingRequiredSignature)); return result; } // Were enough signatures received? - let signers_count = multisig.signers - .iter() - .filter_map(|registered_key| { - tx_signers.iter().find(|potential_signer| { - key!(potential_signer) == registered_key && is_signer!(potential_signer) - }) - }) - .count(); + // let signers_count = multisig.signers + // .iter() + // .filter_map(|registered_key| { + // tx_signers.iter().find(|potential_signer| { + // key!(potential_signer) == registered_key && is_signer!(potential_signer) + // }) + // }) + // .count(); + let mut signers_count: usize = 0; + for registered_key in multisig.signers.iter() { + for potential_signer in tx_signers.iter() { + if key!(potential_signer) == registered_key && is_signer!(potential_signer) { + signers_count += 1; + break; + } + } + } // Check if we have enough signers if signers_count < multisig.m as usize { From f912ce19fc445bf2486bd9d6ffce253c74f520e8 Mon Sep 17 00:00:00 2001 From: mariaKt Date: Wed, 25 Feb 2026 10:10:47 -0600 Subject: [PATCH 2/6] Separate tests for inner_test_validate_owner. --- specs/shared/test_validate_owner.rs | 33 ++++++++++++++++ specs/shared/test_validate_owner_multisig.rs | 41 ++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 specs/shared/test_validate_owner.rs create mode 100644 specs/shared/test_validate_owner_multisig.rs diff --git a/specs/shared/test_validate_owner.rs b/specs/shared/test_validate_owner.rs new file mode 100644 index 00000000..c6fcdb4f --- /dev/null +++ b/specs/shared/test_validate_owner.rs @@ -0,0 +1,33 @@ +/// accounts[0] // Source Account Info (provides expected_owner) +/// accounts[1] // Owner Info +#[inline(never)] +fn test_validate_owner( + accounts: &[AccountInfo; 2], +) -> ProgramResult { + cheatcode_account!(&accounts[0]); // Source Account + cheatcode_account!(&accounts[1]); // Owner + + //-Initial State----------------------------------------------------------- + let src_old = get_account(&accounts[0]); + let expected_owner = src_old.owner; + let maybe_multisig_is_initialised = None; + + //-Process Instruction----------------------------------------------------- + let result = expected_validate_owner_result( + &expected_owner, + &accounts[1], + &accounts[2..], + maybe_multisig_is_initialised.clone(), + ); + + //-Assert Postconditions--------------------------------------------------- + inner_test_validate_owner( + &expected_owner, + &accounts[1], + &accounts[2..], + maybe_multisig_is_initialised, + result.clone(), + )?; + + result +} diff --git a/specs/shared/test_validate_owner_multisig.rs b/specs/shared/test_validate_owner_multisig.rs new file mode 100644 index 00000000..aaeb80c6 --- /dev/null +++ b/specs/shared/test_validate_owner_multisig.rs @@ -0,0 +1,41 @@ +/// accounts[0] // Source Account Info (provides expected_owner) +/// accounts[1] // Owner Info (multisig) +/// accounts[2..4] // Signers +#[inline(never)] +fn test_validate_owner_multisig( + accounts: &[AccountInfo; 5], +) -> ProgramResult { + cheatcode_account!(&accounts[0]); // Source Account + cheatcode_multisig!(&accounts[1]); // Owner (multisig) + + //-Initial State----------------------------------------------------------- + let src_old = get_account(&accounts[0]); + let expected_owner = src_old.owner; + let maybe_multisig_is_initialised = Some(get_multisig(&accounts[1]).is_initialized()); + + //-Process Instruction----------------------------------------------------- + // let result = expected_validate_owner_result( + // &expected_owner, + // &accounts[1], + // &accounts[2..], + // maybe_multisig_is_initialised.clone(), + // ); + + //-Assert Postconditions--------------------------------------------------- + // inner_test_validate_owner( + // &expected_owner, + // &accounts[1], + // &accounts[2..], + // maybe_multisig_is_initialised, + // result.clone(), + // )?; + + // result + + expected_validate_owner_result( + &expected_owner, + &accounts[1], + &accounts[2..], + maybe_multisig_is_initialised, + ) +} From a0032be9483ca4ebc3f22515d4ba13d5f5109c23 Mon Sep 17 00:00:00 2001 From: mariaKt Date: Wed, 25 Feb 2026 10:11:45 -0600 Subject: [PATCH 3/6] Changes to add test_validate_owner* to test suite. --- p-token/src/entrypoint-runtime-verification.rs | 4 ++++ p-token/test-properties/proofs.md | 2 ++ 2 files changed, 6 insertions(+) diff --git a/p-token/src/entrypoint-runtime-verification.rs b/p-token/src/entrypoint-runtime-verification.rs index ecec8b39..f1c878f4 100644 --- a/p-token/src/entrypoint-runtime-verification.rs +++ b/p-token/src/entrypoint-runtime-verification.rs @@ -652,6 +652,8 @@ fn inner_process_remaining_instruction( #[no_mangle] pub unsafe extern "C" fn use_tests(acc: &AccountInfo) { test_ptoken_domain_data(acc, acc, acc); + let _ = test_validate_owner(unsafe { &*(acc as *const AccountInfo as *const [AccountInfo; 2]) }); + let _ = test_validate_owner_multisig(unsafe { &*(acc as *const AccountInfo as *const [AccountInfo; 5]) }); } // special test for basic domain data access @@ -742,6 +744,8 @@ include!("../../specs/shared/test_process_transfer.rs"); include!("../../specs/shared/test_process_transfer_multisig.rs"); include!("../../specs/shared/test_process_amount_to_ui_amount.rs"); include!("../../specs/shared/test_process_ui_amount_to_amount.rs"); +include!("../../specs/shared/test_validate_owner.rs"); +include!("../../specs/shared/test_validate_owner_multisig.rs"); // Withdraw Excess Lamports test harnesses (p-token specific) include!("../../specs/withdraw-p-token.rs"); diff --git a/p-token/test-properties/proofs.md b/p-token/test-properties/proofs.md index 3ac6deda..883222cc 100644 --- a/p-token/test-properties/proofs.md +++ b/p-token/test-properties/proofs.md @@ -3,6 +3,8 @@ Proofs to run with `run-proofs.sh -a`: | Start symbol name | |------------------------------------------------| | test_ptoken_domain_data | +| test_validate_owner | +| test_validate_owner_multisig | | test_process_approve | | test_process_approve_checked | | test_process_withdraw_excess_lamports_account | From ba5acae4d5a15f777e876d7e884a5f93b9b13df2 Mon Sep 17 00:00:00 2001 From: mariaKt Date: Mon, 9 Mar 2026 17:19:05 -0500 Subject: [PATCH 4/6] Formatting. --- p-token/src/entrypoint-runtime-verification.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/p-token/src/entrypoint-runtime-verification.rs b/p-token/src/entrypoint-runtime-verification.rs index f1c878f4..fdea239a 100644 --- a/p-token/src/entrypoint-runtime-verification.rs +++ b/p-token/src/entrypoint-runtime-verification.rs @@ -652,8 +652,11 @@ fn inner_process_remaining_instruction( #[no_mangle] pub unsafe extern "C" fn use_tests(acc: &AccountInfo) { test_ptoken_domain_data(acc, acc, acc); - let _ = test_validate_owner(unsafe { &*(acc as *const AccountInfo as *const [AccountInfo; 2]) }); - let _ = test_validate_owner_multisig(unsafe { &*(acc as *const AccountInfo as *const [AccountInfo; 5]) }); + let _ = + test_validate_owner(unsafe { &*(acc as *const AccountInfo as *const [AccountInfo; 2]) }); + let _ = test_validate_owner_multisig(unsafe { + &*(acc as *const AccountInfo as *const [AccountInfo; 5]) + }); } // special test for basic domain data access From 9cf072fdaad182c307493fceefa13debd3d9532b Mon Sep 17 00:00:00 2001 From: mariaKt Date: Wed, 11 Mar 2026 16:39:14 -0500 Subject: [PATCH 5/6] Restored closures after testing --- specs/shared/inner_test_validate_owner.rs | 94 +++++++---------------- 1 file changed, 26 insertions(+), 68 deletions(-) diff --git a/specs/shared/inner_test_validate_owner.rs b/specs/shared/inner_test_validate_owner.rs index a1109392..2abd0abd 100644 --- a/specs/shared/inner_test_validate_owner.rs +++ b/specs/shared/inner_test_validate_owner.rs @@ -25,45 +25,24 @@ fn expected_validate_owner_result( let multisig = get_multisig(owner_account_info); // Did all declared and allowd signers sign? - // let unsigned_exists = tx_signers.iter().any(|potential_signer| { - // multisig.signers.iter().any(|registered_key| { - // registered_key == key!(potential_signer) && !is_signer!(potential_signer) - // }) - // }); - let mut unsigned_exists = false; - for potential_signer in tx_signers.iter() { - for registered_key in multisig.signers.iter() { - if registered_key == key!(potential_signer) && !is_signer!(potential_signer) { - unsigned_exists = true; - break; - } - } - if unsigned_exists { - break; - } - } + let unsigned_exists = tx_signers.iter().any(|potential_signer| { + multisig.signers.iter().any(|registered_key| { + registered_key == key!(potential_signer) && !is_signer!(potential_signer) + }) + }); if unsigned_exists { return Err(ProgramError::MissingRequiredSignature); } // Were enough signatures received? - // let signers_count = multisig.signers - // .iter() - // .filter_map(|registered_key| { - // tx_signers.iter().find(|potential_signer| { - // key!(potential_signer) == registered_key && is_signer!(potential_signer) - // }) - // }) - // .count(); - let mut signers_count: usize = 0; - for registered_key in multisig.signers.iter() { - for potential_signer in tx_signers.iter() { - if key!(potential_signer) == registered_key && is_signer!(potential_signer) { - signers_count += 1; - break; - } - } - } + let signers_count = multisig.signers + .iter() + .filter_map(|registered_key| { + tx_signers.iter().find(|potential_signer| { + key!(potential_signer) == registered_key && is_signer!(potential_signer) + }) + }) + .count(); // Check if we have enough signers if signers_count < multisig.m as usize { @@ -113,46 +92,25 @@ fn inner_test_validate_owner( let multisig = get_multisig(owner_account_info); // Did all declared and allowd signers sign? - // let unsigned_exists = tx_signers.iter().any(|potential_signer| { - // multisig.signers.iter().any(|registered_key| { - // registered_key == key!(potential_signer) && !is_signer!(potential_signer) - // }) - // }); - let mut unsigned_exists = false; - for potential_signer in tx_signers.iter() { - for registered_key in multisig.signers.iter() { - if registered_key == key!(potential_signer) && !is_signer!(potential_signer) { - unsigned_exists = true; - break; - } - } - if unsigned_exists { - break; - } - } + let unsigned_exists = tx_signers.iter().any(|potential_signer| { + multisig.signers.iter().any(|registered_key| { + registered_key == key!(potential_signer) && !is_signer!(potential_signer) + }) + }); if unsigned_exists { assert_eq!(result, Err(ProgramError::MissingRequiredSignature)); return result; } // Were enough signatures received? - // let signers_count = multisig.signers - // .iter() - // .filter_map(|registered_key| { - // tx_signers.iter().find(|potential_signer| { - // key!(potential_signer) == registered_key && is_signer!(potential_signer) - // }) - // }) - // .count(); - let mut signers_count: usize = 0; - for registered_key in multisig.signers.iter() { - for potential_signer in tx_signers.iter() { - if key!(potential_signer) == registered_key && is_signer!(potential_signer) { - signers_count += 1; - break; - } - } - } + let signers_count = multisig.signers + .iter() + .filter_map(|registered_key| { + tx_signers.iter().find(|potential_signer| { + key!(potential_signer) == registered_key && is_signer!(potential_signer) + }) + }) + .count(); // Check if we have enough signers if signers_count < multisig.m as usize { From b3b0f1df23cdb6564d53d23553213f6d6fdc4bce Mon Sep 17 00:00:00 2001 From: mariaKt Date: Fri, 13 Mar 2026 10:18:14 -0500 Subject: [PATCH 6/6] Used full (uncommented) test_validate_owner_multisig version. --- specs/shared/test_validate_owner_multisig.rs | 29 ++++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/specs/shared/test_validate_owner_multisig.rs b/specs/shared/test_validate_owner_multisig.rs index aaeb80c6..0526d482 100644 --- a/specs/shared/test_validate_owner_multisig.rs +++ b/specs/shared/test_validate_owner_multisig.rs @@ -14,28 +14,21 @@ fn test_validate_owner_multisig( let maybe_multisig_is_initialised = Some(get_multisig(&accounts[1]).is_initialized()); //-Process Instruction----------------------------------------------------- - // let result = expected_validate_owner_result( - // &expected_owner, - // &accounts[1], - // &accounts[2..], - // maybe_multisig_is_initialised.clone(), - // ); + let result = expected_validate_owner_result( + &expected_owner, + &accounts[1], + &accounts[2..], + maybe_multisig_is_initialised.clone(), + ); //-Assert Postconditions--------------------------------------------------- - // inner_test_validate_owner( - // &expected_owner, - // &accounts[1], - // &accounts[2..], - // maybe_multisig_is_initialised, - // result.clone(), - // )?; - - // result - - expected_validate_owner_result( + inner_test_validate_owner( &expected_owner, &accounts[1], &accounts[2..], maybe_multisig_is_initialised, - ) + result.clone(), + )?; + + result }