pub mod constraint;
mod rule_set_v2;
mod rule_v2;
pub use constraint::*;
pub use rule_set_v2::*;
pub use rule_v2::*;
use bytemuck::{AnyBitPattern, NoUninit, Pod, Zeroable};
use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey};
use std::{collections::HashMap, fmt::Display};
use crate::{error::RuleSetError, payload::Payload, state::RuleResult, types::MAX_NAME_LENGTH};
pub const U64_BYTES: usize = std::mem::size_of::<u64>();
pub(crate) fn try_from_bytes<T: AnyBitPattern>(
start: usize,
length: usize,
bytes: &[u8],
) -> Result<&T, RuleSetError> {
if start + length > bytes.len() {
msg!(
"Invalid range: start + length > bytes.len() ({} + {} > {})",
start,
length,
bytes.len()
);
return Err(RuleSetError::RuleSetReadFailed);
}
bytemuck::try_from_bytes::<T>(&bytes[start..start + length]).map_err(|error| {
msg!("{}", error);
RuleSetError::RuleSetReadFailed
})
}
pub(crate) fn try_cast_slice<A: NoUninit, B: AnyBitPattern>(
bytes: &[A],
) -> Result<&[B], RuleSetError> {
bytemuck::try_cast_slice(bytes).map_err(|error| {
msg!("{}", error);
RuleSetError::RuleSetReadFailed
})
}
#[repr(C)]
#[derive(Copy, Clone, Pod, Zeroable)]
pub struct Str32 {
pub value: [u8; MAX_NAME_LENGTH],
}
impl Str32 {
pub const SIZE: usize = MAX_NAME_LENGTH;
}
impl Display for Str32 {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let end_index = self
.value
.iter()
.position(|&x| x == b'\0')
.unwrap_or(MAX_NAME_LENGTH);
let value = String::from_utf8_lossy(&self.value[..end_index]);
formatter.write_str(&value)
}
}
pub trait Constraint<'a> {
fn validate(
&self,
accounts: &HashMap<Pubkey, &AccountInfo>,
payload: &Payload,
update_rule_state: bool,
rule_set_state_pda: &Option<&AccountInfo>,
rule_authority: &Option<&AccountInfo>,
) -> RuleResult;
fn constraint_type(&self) -> ConstraintType;
}
#[repr(u32)]
#[derive(Clone, Copy)]
pub enum ConstraintType {
Uninitialized,
AdditionalSigner,
All,
Amount,
Any,
Frequency,
IsWallet,
Namespace,
Not,
Pass,
PDAMatch,
ProgramOwned,
ProgramOwnedList,
ProgramOwnedTree,
PubkeyListMatch,
PubkeyMatch,
PubkeyTreeMatch,
}
impl ConstraintType {
pub fn to_error(&self) -> ProgramError {
match self {
ConstraintType::Uninitialized => RuleSetError::InvalidConstraintType.into(),
ConstraintType::AdditionalSigner { .. } => {
RuleSetError::AdditionalSignerCheckFailed.into()
}
ConstraintType::All
| ConstraintType::Any
| ConstraintType::Namespace
| ConstraintType::Not
| ConstraintType::Pass => RuleSetError::UnexpectedRuleSetFailure.into(),
ConstraintType::Amount => RuleSetError::AmountCheckFailed.into(),
ConstraintType::Frequency { .. } => RuleSetError::FrequencyCheckFailed.into(),
ConstraintType::IsWallet { .. } => RuleSetError::IsWalletCheckFailed.into(),
ConstraintType::PDAMatch { .. } => RuleSetError::PDAMatchCheckFailed.into(),
ConstraintType::ProgramOwned { .. } => RuleSetError::ProgramOwnedCheckFailed.into(),
ConstraintType::ProgramOwnedList => RuleSetError::ProgramOwnedListCheckFailed.into(),
ConstraintType::ProgramOwnedTree { .. } => {
RuleSetError::ProgramOwnedTreeCheckFailed.into()
}
ConstraintType::PubkeyListMatch { .. } => {
RuleSetError::PubkeyListMatchCheckFailed.into()
}
ConstraintType::PubkeyMatch { .. } => RuleSetError::PubkeyMatchCheckFailed.into(),
ConstraintType::PubkeyTreeMatch { .. } => {
RuleSetError::PubkeyTreeMatchCheckFailed.into()
}
}
}
}
impl TryFrom<u32> for ConstraintType {
type Error = RuleSetError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(ConstraintType::Uninitialized),
1 => Ok(ConstraintType::AdditionalSigner),
2 => Ok(ConstraintType::All),
3 => Ok(ConstraintType::Amount),
4 => Ok(ConstraintType::Any),
5 => Ok(ConstraintType::Frequency),
6 => Ok(ConstraintType::IsWallet),
7 => Ok(ConstraintType::Namespace),
8 => Ok(ConstraintType::Not),
9 => Ok(ConstraintType::Pass),
10 => Ok(ConstraintType::PDAMatch),
11 => Ok(ConstraintType::ProgramOwned),
12 => Ok(ConstraintType::ProgramOwnedList),
13 => Ok(ConstraintType::ProgramOwnedTree),
14 => Ok(ConstraintType::PubkeyListMatch),
15 => Ok(ConstraintType::PubkeyMatch),
16 => Ok(ConstraintType::PubkeyTreeMatch),
_ => Err(RuleSetError::InvalidConstraintType),
}
}
}
#[repr(u64)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Operator {
Lt,
LtEq,
Eq,
GtEq,
Gt,
}
impl TryFrom<u64> for Operator {
type Error = RuleSetError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
match value {
0 => Ok(Operator::Lt),
1 => Ok(Operator::LtEq),
2 => Ok(Operator::Eq),
3 => Ok(Operator::GtEq),
4 => Ok(Operator::Gt),
_ => Err(RuleSetError::InvalidCompareOp),
}
}
}