use solana_program::{msg, pubkey::PUBKEY_BYTES};
use crate::{
error::RuleSetError,
state::RuleResult,
state::{
try_from_bytes,
v2::{Constraint, ConstraintType, Str32, HEADER_SECTION},
Header,
},
utils::compute_merkle_root,
};
pub struct PubkeyTreeMatch<'a> {
pub pubkey_field: &'a Str32,
pub proof_field: &'a Str32,
pub root: &'a [u8; PUBKEY_BYTES],
}
impl<'a> PubkeyTreeMatch<'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::PubkeyTreeMatch, 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 PubkeyTreeMatch<'a> {
fn constraint_type(&self) -> ConstraintType {
ConstraintType::PubkeyTreeMatch
}
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 PubkeyTreeMatch");
let leaf = match payload.get_pubkey(&self.pubkey_field.to_string()) {
Some(pubkey) => pubkey,
_ => return RuleResult::Error(RuleSetError::MissingPayloadValue.into()),
};
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())
}
}
}