use solana_program::{msg, program_error::ProgramError, pubkey::PUBKEY_BYTES};
use crate::{
error::RuleSetError,
state::RuleResult,
state::{
try_from_bytes,
v2::{Constraint, ConstraintType, Str32, HEADER_SECTION},
Header,
},
utils::{compute_merkle_root, is_zeroed},
};
pub struct ProgramOwnedTree<'a> {
pub pubkey_field: &'a Str32,
pub proof_field: &'a Str32,
pub root: &'a [u8; PUBKEY_BYTES],
}
impl<'a> ProgramOwnedTree<'a> {
pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, RuleSetError> {
let pubkey_field = try_from_bytes::<Str32>(0, Str32::SIZE, bytes)?;
let mut cursor = Str32::SIZE;
let proof_field = try_from_bytes::<Str32>(cursor, Str32::SIZE, bytes)?;
cursor += Str32::SIZE;
let root = try_from_bytes::<[u8; 32]>(cursor, PUBKEY_BYTES, bytes)?;
Ok(Self {
pubkey_field,
proof_field,
root,
})
}
pub fn serialize(
pubkey_field: String,
proof_field: String,
root: &[u8; PUBKEY_BYTES],
) -> Result<Vec<u8>, RuleSetError> {
let length = (Str32::SIZE + Str32::SIZE + PUBKEY_BYTES) as u32;
let mut data = Vec::with_capacity(HEADER_SECTION + length as usize);
Header::serialize(ConstraintType::ProgramOwnedTree, length, &mut data);
let mut field_bytes = [0u8; Str32::SIZE];
field_bytes[..pubkey_field.len()].copy_from_slice(pubkey_field.as_bytes());
data.extend(field_bytes);
let mut field_bytes = [0u8; Str32::SIZE];
field_bytes[..proof_field.len()].copy_from_slice(proof_field.as_bytes());
data.extend(field_bytes);
data.extend_from_slice(root);
Ok(data)
}
}
impl<'a> Constraint<'a> for ProgramOwnedTree<'a> {
fn constraint_type(&self) -> ConstraintType {
ConstraintType::ProgramOwnedTree
}
fn validate(
&self,
accounts: &std::collections::HashMap<
solana_program::pubkey::Pubkey,
&solana_program::account_info::AccountInfo,
>,
payload: &crate::payload::Payload,
_update_rule_state: bool,
_rule_set_state_pda: &Option<&solana_program::account_info::AccountInfo>,
_rule_authority: &Option<&solana_program::account_info::AccountInfo>,
) -> RuleResult {
msg!("Validating ProgramOwnedTree");
let key = match payload.get_pubkey(&self.pubkey_field.to_string()) {
Some(pubkey) => pubkey,
_ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
};
let account = match accounts.get(key) {
Some(account) => account,
_ => return RuleResult::Error(RuleSetError::MissingAccount.into()),
};
let data = match account.data.try_borrow() {
Ok(data) => data,
Err(_) => return RuleResult::Error(ProgramError::AccountBorrowFailed),
};
if is_zeroed(&data) {
if data.len() == 0 {
msg!("Account data is empty");
} else {
msg!("Account data is zeroed");
}
return RuleResult::Error(RuleSetError::DataIsEmpty.into());
}
let leaf = account.owner;
let merkle_proof = match payload.get_merkle_proof(&self.proof_field.to_string()) {
Some(merkle_proof) => merkle_proof,
_ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
};
let computed_root = compute_merkle_root(leaf, merkle_proof);
if computed_root == *self.root {
RuleResult::Success(self.constraint_type().to_error())
} else {
RuleResult::Failure(self.constraint_type().to_error())
}
}
}