diff --git a/p-token/src/entrypoint-runtime-verification.rs b/p-token/src/entrypoint-runtime-verification.rs index ecec8b39..fdea239a 100644 --- a/p-token/src/entrypoint-runtime-verification.rs +++ b/p-token/src/entrypoint-runtime-verification.rs @@ -652,6 +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]) + }); } // special test for basic domain data access @@ -742,6 +747,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 | diff --git a/specs/shared/inner_test_validate_owner.rs b/specs/shared/inner_test_validate_owner.rs index 5f25fdf4..2abd0abd 100644 --- a/specs/shared/inner_test_validate_owner.rs +++ b/specs/shared/inner_test_validate_owner.rs @@ -1,3 +1,65 @@ +/// 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) + }) + }); + 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(); + + // 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)] 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..0526d482 --- /dev/null +++ b/specs/shared/test_validate_owner_multisig.rs @@ -0,0 +1,34 @@ +/// 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 +}