#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Capability {
ReadsState = 0,
MutatesState = 1,
TouchesJournal = 2,
ExternalCall = 3,
MutatesTreasury = 4,
ReallocatesAccount = 5,
CreatesAccount = 6,
ClosesAccount = 7,
ModifiesAuthority = 8,
TransitionsState = 9,
}
impl Capability {
#[inline(always)]
pub const fn mask(self) -> u32 {
1u32 << (self as u8)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct CapabilitySet {
bits: u32,
}
impl CapabilitySet {
#[inline(always)]
pub const fn new() -> Self {
Self { bits: 0 }
}
#[inline(always)]
pub const fn with(self, cap: Capability) -> Self {
Self {
bits: self.bits | cap.mask(),
}
}
#[inline(always)]
pub const fn has(&self, cap: Capability) -> bool {
self.bits & cap.mask() != 0
}
#[inline(always)]
pub const fn bits(&self) -> u32 {
self.bits
}
#[inline(always)]
pub const fn count(&self) -> u32 {
self.bits.count_ones()
}
#[inline(always)]
pub const fn union(self, other: Self) -> Self {
Self {
bits: self.bits | other.bits,
}
}
#[inline(always)]
pub const fn is_subset_of(&self, other: &Self) -> bool {
(self.bits & other.bits) == self.bits
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PolicyRequirement {
Authority = 0,
JournalCapacity = 1,
PostMutationCheck = 2,
CpiGuard = 3,
RentExemption = 4,
InvariantCheck = 5,
StateSnapshot = 6,
LamportConservation = 7,
}
impl PolicyRequirement {
#[inline(always)]
pub const fn mask(self) -> u32 {
1u32 << (self as u8)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct RequirementSet {
bits: u32,
}
impl RequirementSet {
#[inline(always)]
pub const fn new() -> Self {
Self { bits: 0 }
}
#[inline(always)]
pub const fn with(self, req: PolicyRequirement) -> Self {
Self {
bits: self.bits | req.mask(),
}
}
#[inline(always)]
pub const fn has(&self, req: PolicyRequirement) -> bool {
self.bits & req.mask() != 0
}
#[inline(always)]
pub const fn bits(&self) -> u32 {
self.bits
}
}
#[derive(Clone, Copy)]
pub struct PolicyRule {
pub capability: Capability,
pub requirement: PolicyRequirement,
}
pub struct InstructionPolicy<const N: usize> {
rules: [PolicyRule; N],
count: usize,
}
impl<const N: usize> InstructionPolicy<N> {
#[inline(always)]
pub const fn new() -> Self {
Self {
rules: [PolicyRule {
capability: Capability::ReadsState,
requirement: PolicyRequirement::Authority,
}; N],
count: 0,
}
}
#[inline(always)]
pub const fn when(mut self, cap: Capability, req: PolicyRequirement) -> Self {
assert!(self.count < N, "policy rule overflow");
self.rules[self.count] = PolicyRule {
capability: cap,
requirement: req,
};
self.count += 1;
self
}
#[inline]
pub const fn resolve(&self, caps: &CapabilitySet) -> RequirementSet {
let mut reqs = RequirementSet::new();
let mut i = 0;
while i < self.count {
if caps.has(self.rules[i].capability) {
reqs = reqs.with(self.rules[i].requirement);
}
i += 1;
}
reqs
}
#[inline(always)]
pub const fn rule_count(&self) -> usize {
self.count
}
}
impl Default for CapabilitySet {
fn default() -> Self {
Self::new()
}
}
impl Default for RequirementSet {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> Default for InstructionPolicy<N> {
fn default() -> Self {
Self::new()
}
}
pub const TREASURY_WRITE_POLICY: InstructionPolicy<4> = InstructionPolicy::new()
.when(Capability::MutatesState, PolicyRequirement::Authority)
.when(Capability::MutatesState, PolicyRequirement::StateSnapshot)
.when(
Capability::MutatesTreasury,
PolicyRequirement::LamportConservation,
)
.when(
Capability::MutatesTreasury,
PolicyRequirement::InvariantCheck,
);
pub const TREASURY_WRITE_CAPS: CapabilitySet = CapabilitySet::new()
.with(Capability::MutatesState)
.with(Capability::MutatesTreasury);
pub const JOURNAL_TOUCH_POLICY: InstructionPolicy<3> = InstructionPolicy::new()
.when(Capability::MutatesState, PolicyRequirement::Authority)
.when(
Capability::TouchesJournal,
PolicyRequirement::JournalCapacity,
)
.when(Capability::TouchesJournal, PolicyRequirement::StateSnapshot);
pub const JOURNAL_TOUCH_CAPS: CapabilitySet = CapabilitySet::new()
.with(Capability::MutatesState)
.with(Capability::TouchesJournal);
pub const EXTERNAL_CALL_POLICY: InstructionPolicy<3> = InstructionPolicy::new()
.when(Capability::ExternalCall, PolicyRequirement::CpiGuard)
.when(
Capability::ExternalCall,
PolicyRequirement::PostMutationCheck,
)
.when(Capability::ExternalCall, PolicyRequirement::StateSnapshot);
pub const EXTERNAL_CALL_CAPS: CapabilitySet = CapabilitySet::new().with(Capability::ExternalCall);
pub const SHARD_MUTATION_POLICY: InstructionPolicy<3> = InstructionPolicy::new()
.when(Capability::MutatesState, PolicyRequirement::Authority)
.when(Capability::MutatesState, PolicyRequirement::StateSnapshot)
.when(Capability::MutatesState, PolicyRequirement::InvariantCheck);
pub const SHARD_MUTATION_CAPS: CapabilitySet = CapabilitySet::new().with(Capability::MutatesState);
pub const MIGRATION_SENSITIVE_POLICY: InstructionPolicy<4> = InstructionPolicy::new()
.when(Capability::ReallocatesAccount, PolicyRequirement::Authority)
.when(
Capability::ReallocatesAccount,
PolicyRequirement::RentExemption,
)
.when(
Capability::ReallocatesAccount,
PolicyRequirement::StateSnapshot,
)
.when(
Capability::ReallocatesAccount,
PolicyRequirement::InvariantCheck,
);
pub const MIGRATION_SENSITIVE_CAPS: CapabilitySet = CapabilitySet::new()
.with(Capability::MutatesState)
.with(Capability::ReallocatesAccount);
pub const AUTHORITY_CHANGE_POLICY: InstructionPolicy<4> = InstructionPolicy::new()
.when(Capability::ModifiesAuthority, PolicyRequirement::Authority)
.when(Capability::ModifiesAuthority, PolicyRequirement::CpiGuard)
.when(
Capability::ModifiesAuthority,
PolicyRequirement::PostMutationCheck,
)
.when(
Capability::ModifiesAuthority,
PolicyRequirement::InvariantCheck,
);
pub const AUTHORITY_CHANGE_CAPS: CapabilitySet = CapabilitySet::new()
.with(Capability::MutatesState)
.with(Capability::ModifiesAuthority);
pub const READ_ONLY_AUDIT_POLICY: InstructionPolicy<1> =
InstructionPolicy::new().when(Capability::ReadsState, PolicyRequirement::StateSnapshot);
pub const READ_ONLY_AUDIT_CAPS: CapabilitySet = CapabilitySet::new().with(Capability::ReadsState);
pub const ACCOUNT_INIT_POLICY: InstructionPolicy<3> = InstructionPolicy::new()
.when(Capability::CreatesAccount, PolicyRequirement::Authority)
.when(Capability::CreatesAccount, PolicyRequirement::RentExemption)
.when(
Capability::CreatesAccount,
PolicyRequirement::InvariantCheck,
);
pub const ACCOUNT_INIT_CAPS: CapabilitySet = CapabilitySet::new().with(Capability::CreatesAccount);
pub const ACCOUNT_CLOSE_POLICY: InstructionPolicy<3> = InstructionPolicy::new()
.when(Capability::ClosesAccount, PolicyRequirement::Authority)
.when(Capability::ClosesAccount, PolicyRequirement::StateSnapshot)
.when(
Capability::ClosesAccount,
PolicyRequirement::LamportConservation,
);
pub const ACCOUNT_CLOSE_CAPS: CapabilitySet = CapabilitySet::new().with(Capability::ClosesAccount);
#[derive(Clone, Copy)]
pub struct PolicyPackDescriptor {
pub name: &'static str,
pub description: &'static str,
pub capabilities: &'static CapabilitySet,
pub requirements: &'static [(&'static str, &'static str)],
pub receipt_expected: bool,
pub invariant_hints: &'static [&'static str],
}
pub const NAMED_POLICY_PACKS: &[PolicyPackDescriptor] = &[
PolicyPackDescriptor {
name: "TreasuryWrite",
description: "Vault/treasury balance mutations. Enforces authority, snapshot, lamport conservation, and invariant checks.",
capabilities: &TREASURY_WRITE_CAPS,
requirements: &[
("MutatesState", "Authority, StateSnapshot"),
("MutatesTreasury", "LamportConservation, InvariantCheck"),
],
receipt_expected: true,
invariant_hints: &["balance_conservation", "authority_present"],
},
PolicyPackDescriptor {
name: "JournalTouch",
description: "Journal segment writes. Enforces authority, capacity guard, and snapshot.",
capabilities: &JOURNAL_TOUCH_CAPS,
requirements: &[
("MutatesState", "Authority"),
("TouchesJournal", "JournalCapacity, StateSnapshot"),
],
receipt_expected: true,
invariant_hints: &["journal_not_full"],
},
PolicyPackDescriptor {
name: "ExternalCall",
description: "CPI-invoking instructions. Enforces CPI guard, post-mutation check, and snapshot.",
capabilities: &EXTERNAL_CALL_CAPS,
requirements: &[
("ExternalCall", "CpiGuard, PostMutationCheck, StateSnapshot"),
],
receipt_expected: true,
invariant_hints: &["cpi_allowlisted"],
},
PolicyPackDescriptor {
name: "ShardMutation",
description: "Shard data modifications. Enforces authority, snapshot, and invariant checks.",
capabilities: &SHARD_MUTATION_CAPS,
requirements: &[
("MutatesState", "Authority, StateSnapshot, InvariantCheck"),
],
receipt_expected: true,
invariant_hints: &[],
},
PolicyPackDescriptor {
name: "MigrationSensitive",
description: "Account reallocation/migration. Enforces authority, rent exemption, snapshot, and invariant checks.",
capabilities: &MIGRATION_SENSITIVE_CAPS,
requirements: &[
("ReallocatesAccount", "Authority, RentExemption, StateSnapshot, InvariantCheck"),
],
receipt_expected: true,
invariant_hints: &["rent_exempt_after_realloc"],
},
PolicyPackDescriptor {
name: "AuthorityChange",
description: "Authority/permission modifications. Enforces authority, CPI guard, post-mutation, and invariant checks.",
capabilities: &AUTHORITY_CHANGE_CAPS,
requirements: &[
("ModifiesAuthority", "Authority, CpiGuard, PostMutationCheck, InvariantCheck"),
],
receipt_expected: true,
invariant_hints: &["new_authority_valid"],
},
PolicyPackDescriptor {
name: "ReadOnlyAudit",
description: "Read-only inspection/audit. Only requires snapshot for traceability.",
capabilities: &READ_ONLY_AUDIT_CAPS,
requirements: &[
("ReadsState", "StateSnapshot"),
],
receipt_expected: false,
invariant_hints: &[],
},
PolicyPackDescriptor {
name: "AccountInit",
description: "Account creation. Enforces authority, rent exemption, and invariant checks.",
capabilities: &ACCOUNT_INIT_CAPS,
requirements: &[
("CreatesAccount", "Authority, RentExemption, InvariantCheck"),
],
receipt_expected: true,
invariant_hints: &["header_initialized"],
},
PolicyPackDescriptor {
name: "AccountClose",
description: "Account closure. Enforces authority, snapshot, and lamport conservation.",
capabilities: &ACCOUNT_CLOSE_CAPS,
requirements: &[
("ClosesAccount", "Authority, StateSnapshot, LamportConservation"),
],
receipt_expected: true,
invariant_hints: &["sentinel_written"],
},
];
impl Capability {
#[inline]
pub const fn name(self) -> &'static str {
match self {
Self::ReadsState => "ReadsState",
Self::MutatesState => "MutatesState",
Self::TouchesJournal => "TouchesJournal",
Self::ExternalCall => "ExternalCall",
Self::MutatesTreasury => "MutatesTreasury",
Self::ReallocatesAccount => "ReallocatesAccount",
Self::CreatesAccount => "CreatesAccount",
Self::ClosesAccount => "ClosesAccount",
Self::ModifiesAuthority => "ModifiesAuthority",
Self::TransitionsState => "TransitionsState",
}
}
}
impl PolicyRequirement {
#[inline]
pub const fn name(self) -> &'static str {
match self {
Self::Authority => "Authority",
Self::JournalCapacity => "JournalCapacity",
Self::PostMutationCheck => "PostMutationCheck",
Self::CpiGuard => "CpiGuard",
Self::RentExemption => "RentExemption",
Self::InvariantCheck => "InvariantCheck",
Self::StateSnapshot => "StateSnapshot",
Self::LamportConservation => "LamportConservation",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum PolicyClass {
Read = 0,
Write = 1,
Financial = 2,
Administrative = 3,
Lifecycle = 4,
CrossProgram = 5,
Governance = 6,
}
impl PolicyClass {
pub const fn name(self) -> &'static str {
match self {
Self::Read => "read",
Self::Write => "write",
Self::Financial => "financial",
Self::Administrative => "administrative",
Self::Lifecycle => "lifecycle",
Self::CrossProgram => "cross-program",
Self::Governance => "governance",
}
}
pub const fn is_mutating(self) -> bool {
!matches!(self, Self::Read)
}
pub const fn expects_receipt(self) -> bool {
!matches!(self, Self::Read)
}
pub const fn from_capabilities(caps: &CapabilitySet) -> Self {
if caps.has(Capability::MutatesTreasury) {
return Self::Financial;
}
if caps.has(Capability::ModifiesAuthority) {
return Self::Administrative;
}
if caps.has(Capability::CreatesAccount)
|| caps.has(Capability::ClosesAccount)
|| caps.has(Capability::ReallocatesAccount)
{
return Self::Lifecycle;
}
if caps.has(Capability::ExternalCall) {
return Self::CrossProgram;
}
if caps.has(Capability::MutatesState)
|| caps.has(Capability::TouchesJournal)
|| caps.has(Capability::TransitionsState)
{
return Self::Write;
}
Self::Read
}
}
impl core::fmt::Display for PolicyClass {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.name())
}
}