mpl_token_auth_rules/state/v2/constraint/
program_owned.rs

1use solana_program::{
2    msg,
3    program_error::ProgramError,
4    pubkey::{Pubkey, PUBKEY_BYTES},
5};
6
7use crate::{
8    error::RuleSetError,
9    state::RuleResult,
10    state::{
11        try_from_bytes,
12        v2::{Constraint, ConstraintType, Str32, HEADER_SECTION},
13        Header,
14    },
15    utils::is_zeroed,
16};
17
18/// Constraint representing a test where a `Pubkey` must be owned by a given program.
19///
20/// This constraint requires a `PayloadType` value of `PayloadType::Pubkey`.  The `field` value in
21/// the rule is used to locate the `Pubkey` in the payload for which the owner must be the
22/// program in the rule.  Note this same `Pubkey` account must also be provided to `Validate`
23/// via the `additional_rule_accounts` argument.  This is so that the `Pubkey`'s owner can be
24/// found from its `AccountInfo` struct.
25pub struct ProgramOwned<'a> {
26    /// The program that must own the `Pubkey`.
27    pub program: &'a Pubkey,
28    /// The field in the `Payload` to be compared.
29    pub field: &'a Str32,
30}
31
32impl<'a> ProgramOwned<'a> {
33    /// Deserialize a constraint from a byte array.
34    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, RuleSetError> {
35        let program = try_from_bytes::<Pubkey>(0, PUBKEY_BYTES, bytes)?;
36        let field = try_from_bytes::<Str32>(PUBKEY_BYTES, Str32::SIZE, bytes)?;
37
38        Ok(Self { program, field })
39    }
40
41    /// Serialize a constraint into a byte array.
42    pub fn serialize(field: String, program: Pubkey) -> Result<Vec<u8>, RuleSetError> {
43        let length = (PUBKEY_BYTES + Str32::SIZE) as u32;
44        let mut data = Vec::with_capacity(HEADER_SECTION + length as usize);
45
46        // Header
47        Header::serialize(ConstraintType::ProgramOwned, length, &mut data);
48
49        // Constraint
50        // - program
51        data.extend(program.as_ref());
52        // - field
53        let mut field_bytes = [0u8; Str32::SIZE];
54        field_bytes[..field.len()].copy_from_slice(field.as_bytes());
55        data.extend(field_bytes);
56
57        Ok(data)
58    }
59}
60
61impl<'a> Constraint<'a> for ProgramOwned<'a> {
62    fn constraint_type(&self) -> ConstraintType {
63        ConstraintType::ProgramOwned
64    }
65
66    fn validate(
67        &self,
68        accounts: &std::collections::HashMap<
69            solana_program::pubkey::Pubkey,
70            &solana_program::account_info::AccountInfo,
71        >,
72        payload: &crate::payload::Payload,
73        _update_rule_state: bool,
74        _rule_set_state_pda: &Option<&solana_program::account_info::AccountInfo>,
75        _rule_authority: &Option<&solana_program::account_info::AccountInfo>,
76    ) -> RuleResult {
77        msg!("Validating ProgramOwned");
78
79        let key = match payload.get_pubkey(&self.field.to_string()) {
80            Some(pubkey) => pubkey,
81            _ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
82        };
83
84        if let Some(account) = accounts.get(key) {
85            let data = match account.data.try_borrow() {
86                Ok(data) => data,
87                Err(_) => return RuleResult::Error(ProgramError::AccountBorrowFailed),
88            };
89
90            if is_zeroed(&data) {
91                // Print helpful errors.
92                if data.len() == 0 {
93                    msg!("Account data is empty");
94                } else {
95                    msg!("Account data is zeroed");
96                }
97
98                // Account must have nonzero data to count as program-owned.
99                return RuleResult::Error(self.constraint_type().to_error());
100            } else if *account.owner == *self.program {
101                return RuleResult::Success(self.constraint_type().to_error());
102            }
103        } else {
104            return RuleResult::Error(RuleSetError::MissingAccount.into());
105        }
106
107        RuleResult::Failure(self.constraint_type().to_error())
108    }
109}