Skip to main content

antegen_fiber_program/state/
fiber.rs

1use crate::constants::*;
2use crate::errors::AntegenFiberError;
3use crate::state::instruction::*;
4use anchor_lang::prelude::*;
5use anchor_lang::solana_program::instruction::Instruction;
6
7/// Current version stamped onto newly written `FiberVersionedState` accounts.
8pub const CURRENT_FIBER_VERSION: u8 = 1;
9
10/// Per-fiber hard cap on lookup tables — matches Solana's v0-tx ALT cap.
11pub const MAX_LOOKUP_TABLES_PER_FIBER: usize = 4;
12
13/// Trait for processing fiber instructions
14pub trait FiberInstructionProcessor {
15    /// Get the decompiled instruction from the fiber's compiled data,
16    /// replacing PAYER_PUBKEY with the provided executor
17    fn get_instruction(&self, executor: &Pubkey) -> Result<Instruction>;
18}
19
20/// Legacy fiber state — predates ALT support. Still readable on mainnet,
21/// never re-allocated. New writes always produce `FiberVersionedState`.
22#[account]
23#[derive(Debug, InitSpace)]
24pub struct FiberState {
25    /// The thread this fiber belongs to
26    pub thread: Pubkey,
27    /// The compiled instruction data
28    #[max_len(1024)]
29    pub compiled_instruction: Vec<u8>,
30    /// When this fiber was last executed
31    pub last_executed: i64,
32    /// Total number of executions
33    pub exec_count: u64,
34    /// Priority fee in microlamports for compute unit price (0 = no priority fee)
35    pub priority_fee: u64,
36}
37
38impl FiberState {
39    /// Derive the pubkey of a fiber account.
40    pub fn pubkey(thread: Pubkey, fiber_index: u8) -> Pubkey {
41        Pubkey::find_program_address(
42            &[SEED_THREAD_FIBER, thread.as_ref(), &[fiber_index]],
43            &crate::ID,
44        )
45        .0
46    }
47}
48
49impl FiberInstructionProcessor for FiberState {
50    fn get_instruction(&self, executor: &Pubkey) -> Result<Instruction> {
51        decompile_with_payer(&self.compiled_instruction, executor)
52    }
53}
54
55/// Versioned fiber state. Carries `version` as the first field so future
56/// migrations have a leading gate, and trailing `lookup_tables` so v0 message
57/// compilation can attach an ALT union without runtime account churn.
58#[account]
59#[derive(Debug, InitSpace)]
60pub struct FiberVersionedState {
61    /// State version. Currently 1.
62    pub version: u8,
63    /// The thread this fiber belongs to
64    pub thread: Pubkey,
65    /// The compiled instruction data
66    #[max_len(1024)]
67    pub compiled_instruction: Vec<u8>,
68    /// When this fiber was last executed
69    pub last_executed: i64,
70    /// Total number of executions
71    pub exec_count: u64,
72    /// Priority fee in microlamports for compute unit price (0 = no priority fee)
73    pub priority_fee: u64,
74    /// Address Lookup Tables consumed by this fiber. Capped at 4 (Solana v0 limit).
75    #[max_len(4)]
76    pub lookup_tables: Vec<Pubkey>,
77}
78
79impl FiberVersionedState {
80    pub fn pubkey(thread: Pubkey, fiber_index: u8) -> Pubkey {
81        FiberState::pubkey(thread, fiber_index)
82    }
83}
84
85impl FiberInstructionProcessor for FiberVersionedState {
86    fn get_instruction(&self, executor: &Pubkey) -> Result<Instruction> {
87        decompile_with_payer(&self.compiled_instruction, executor)
88    }
89}
90
91/// Discriminator-tagged read view over either fiber shape on disk.
92///
93/// Implements [`AccountDeserialize`] so callers use the same
94/// `try_deserialize(&mut buf)` shape as any other Anchor account type — the
95/// trait impl peeks the leading 8-byte discriminator and routes to the
96/// matching state struct's deserializer.
97#[derive(Debug)]
98pub enum Fiber {
99    Legacy(FiberState),
100    V1(FiberVersionedState),
101}
102
103impl anchor_lang::AccountDeserialize for Fiber {
104    fn try_deserialize(buf: &mut &[u8]) -> Result<Self> {
105        Self::try_deserialize_unchecked(buf)
106    }
107
108    fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
109        if buf.len() < 8 {
110            return Err(error!(AntegenFiberError::InvalidFiberData));
111        }
112        let disc = &buf[..8];
113        if disc == FiberVersionedState::DISCRIMINATOR {
114            let state = FiberVersionedState::try_deserialize(buf)?;
115            Ok(Self::V1(state))
116        } else if disc == FiberState::DISCRIMINATOR {
117            let state = FiberState::try_deserialize(buf)?;
118            Ok(Self::Legacy(state))
119        } else {
120            Err(error!(AntegenFiberError::InvalidFiberData))
121        }
122    }
123}
124
125impl Fiber {
126    pub fn is_legacy(&self) -> bool {
127        matches!(self, Self::Legacy(_))
128    }
129
130    pub fn thread(&self) -> Pubkey {
131        match self {
132            Self::Legacy(s) => s.thread,
133            Self::V1(s) => s.thread,
134        }
135    }
136
137    pub fn compiled_instruction(&self) -> &[u8] {
138        match self {
139            Self::Legacy(s) => &s.compiled_instruction,
140            Self::V1(s) => &s.compiled_instruction,
141        }
142    }
143
144    pub fn priority_fee(&self) -> u64 {
145        match self {
146            Self::Legacy(s) => s.priority_fee,
147            Self::V1(s) => s.priority_fee,
148        }
149    }
150
151    pub fn lookup_tables(&self) -> &[Pubkey] {
152        match self {
153            Self::Legacy(_) => &[],
154            Self::V1(s) => &s.lookup_tables,
155        }
156    }
157}
158
159impl FiberInstructionProcessor for Fiber {
160    fn get_instruction(&self, executor: &Pubkey) -> Result<Instruction> {
161        match self {
162            Self::Legacy(s) => s.get_instruction(executor),
163            Self::V1(s) => s.get_instruction(executor),
164        }
165    }
166}
167
168fn decompile_with_payer(compiled_instruction: &[u8], executor: &Pubkey) -> Result<Instruction> {
169    let compiled = CompiledInstructionV0::try_from_slice(compiled_instruction)?;
170    let mut instruction = decompile_instruction(&compiled)?;
171
172    for acc in instruction.accounts.iter_mut() {
173        if acc.pubkey.eq(&PAYER_PUBKEY) {
174            acc.pubkey = *executor;
175        }
176    }
177
178    Ok(instruction)
179}