mpl_token_auth_rules/state/v2/constraint/
program_owned_tree.rs1use solana_program::{msg, program_error::ProgramError, pubkey::PUBKEY_BYTES};
2
3use crate::{
4 error::RuleSetError,
5 state::RuleResult,
6 state::{
7 try_from_bytes,
8 v2::{Constraint, ConstraintType, Str32, HEADER_SECTION},
9 Header,
10 },
11 utils::{compute_merkle_root, is_zeroed},
12};
13
14pub struct ProgramOwnedTree<'a> {
24 pub pubkey_field: &'a Str32,
26 pub proof_field: &'a Str32,
28 pub root: &'a [u8; PUBKEY_BYTES],
30}
31
32impl<'a> ProgramOwnedTree<'a> {
33 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, RuleSetError> {
35 let pubkey_field = try_from_bytes::<Str32>(0, Str32::SIZE, bytes)?;
36 let mut cursor = Str32::SIZE;
37
38 let proof_field = try_from_bytes::<Str32>(cursor, Str32::SIZE, bytes)?;
39 cursor += Str32::SIZE;
40
41 let root = try_from_bytes::<[u8; 32]>(cursor, PUBKEY_BYTES, bytes)?;
42
43 Ok(Self {
44 pubkey_field,
45 proof_field,
46 root,
47 })
48 }
49
50 pub fn serialize(
52 pubkey_field: String,
53 proof_field: String,
54 root: &[u8; PUBKEY_BYTES],
55 ) -> Result<Vec<u8>, RuleSetError> {
56 let length = (Str32::SIZE + Str32::SIZE + PUBKEY_BYTES) as u32;
57 let mut data = Vec::with_capacity(HEADER_SECTION + length as usize);
58
59 Header::serialize(ConstraintType::ProgramOwnedTree, length, &mut data);
61
62 let mut field_bytes = [0u8; Str32::SIZE];
65 field_bytes[..pubkey_field.len()].copy_from_slice(pubkey_field.as_bytes());
66 data.extend(field_bytes);
67 let mut field_bytes = [0u8; Str32::SIZE];
69 field_bytes[..proof_field.len()].copy_from_slice(proof_field.as_bytes());
70 data.extend(field_bytes);
71 data.extend_from_slice(root);
73
74 Ok(data)
75 }
76}
77
78impl<'a> Constraint<'a> for ProgramOwnedTree<'a> {
79 fn constraint_type(&self) -> ConstraintType {
80 ConstraintType::ProgramOwnedTree
81 }
82
83 fn validate(
84 &self,
85 accounts: &std::collections::HashMap<
86 solana_program::pubkey::Pubkey,
87 &solana_program::account_info::AccountInfo,
88 >,
89 payload: &crate::payload::Payload,
90 _update_rule_state: bool,
91 _rule_set_state_pda: &Option<&solana_program::account_info::AccountInfo>,
92 _rule_authority: &Option<&solana_program::account_info::AccountInfo>,
93 ) -> RuleResult {
94 msg!("Validating ProgramOwnedTree");
95
96 let key = match payload.get_pubkey(&self.pubkey_field.to_string()) {
98 Some(pubkey) => pubkey,
99 _ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
100 };
101
102 let account = match accounts.get(key) {
104 Some(account) => account,
105 _ => return RuleResult::Error(RuleSetError::MissingAccount.into()),
106 };
107
108 let data = match account.data.try_borrow() {
109 Ok(data) => data,
110 Err(_) => return RuleResult::Error(ProgramError::AccountBorrowFailed),
111 };
112
113 if is_zeroed(&data) {
115 if data.len() == 0 {
117 msg!("Account data is empty");
118 } else {
119 msg!("Account data is zeroed");
120 }
121
122 return RuleResult::Error(RuleSetError::DataIsEmpty.into());
123 }
124
125 let leaf = account.owner;
127
128 let merkle_proof = match payload.get_merkle_proof(&self.proof_field.to_string()) {
130 Some(merkle_proof) => merkle_proof,
131 _ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
132 };
133
134 let computed_root = compute_merkle_root(leaf, merkle_proof);
136 if computed_root == *self.root {
137 RuleResult::Success(self.constraint_type().to_error())
138 } else {
139 RuleResult::Failure(self.constraint_type().to_error())
140 }
141 }
142}