use solana_program::{
msg,
pubkey::{Pubkey, PUBKEY_BYTES},
};
use crate::{
error::RuleSetError,
state::RuleResult,
state::{
try_from_bytes,
v2::{Constraint, ConstraintType, Str32, HEADER_SECTION},
Header,
},
utils::assert_derivation,
};
const DEFAULT_PUBKEY: Pubkey = Pubkey::new_from_array([0u8; 32]);
pub struct PDAMatch<'a> {
pub program: &'a Pubkey,
pub pda_field: &'a Str32,
pub seeds_field: &'a Str32,
}
impl<'a> PDAMatch<'a> {
pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, RuleSetError> {
let program = try_from_bytes::<Pubkey>(0, PUBKEY_BYTES, bytes)?;
let mut cursor = PUBKEY_BYTES;
let pda_field = try_from_bytes::<Str32>(cursor, Str32::SIZE, bytes)?;
cursor += Str32::SIZE;
let seeds_field = try_from_bytes::<Str32>(cursor, Str32::SIZE, bytes)?;
Ok(Self {
program,
pda_field,
seeds_field,
})
}
pub fn serialize(
pda_field: String,
program: Option<Pubkey>,
seeds_field: String,
) -> Result<Vec<u8>, RuleSetError> {
let length = (PUBKEY_BYTES + Str32::SIZE + Str32::SIZE) as u32;
let mut data = Vec::with_capacity(HEADER_SECTION + length as usize);
Header::serialize(ConstraintType::PDAMatch, length, &mut data);
let program = program.unwrap_or(DEFAULT_PUBKEY);
data.extend(program.as_ref());
let mut field_bytes = [0u8; Str32::SIZE];
field_bytes[..pda_field.len()].copy_from_slice(pda_field.as_bytes());
data.extend(field_bytes);
let mut field_bytes = [0u8; Str32::SIZE];
field_bytes[..seeds_field.len()].copy_from_slice(seeds_field.as_bytes());
data.extend(field_bytes);
Ok(data)
}
}
impl<'a> Constraint<'a> for PDAMatch<'a> {
fn constraint_type(&self) -> ConstraintType {
ConstraintType::PDAMatch
}
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 PDAMatch");
let account = match payload.get_pubkey(&self.pda_field.to_string()) {
Some(pubkey) => pubkey,
_ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
};
let seeds = match payload.get_seeds(&self.seeds_field.to_string()) {
Some(seeds) => seeds,
_ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
};
let program = match self.program {
&DEFAULT_PUBKEY => {
match accounts.get(account) {
Some(account) => account.owner,
_ => return RuleResult::Error(RuleSetError::MissingAccount.into()),
}
}
_ => self.program,
};
let vec_of_slices = seeds
.seeds
.iter()
.map(Vec::as_slice)
.collect::<Vec<&[u8]>>();
if let Ok(_bump) = assert_derivation(program, account, &vec_of_slices) {
RuleResult::Success(self.constraint_type().to_error())
} else {
RuleResult::Failure(self.constraint_type().to_error())
}
}
}