1#[allow(unused)]
2#[allow(clippy::identity_op)]
3mod generated;
4mod hooked;
5pub mod registry;
6
7pub use generated::programs::LIGHTHOUSE_ID;
8pub use generated::programs::LIGHTHOUSE_ID as ID;
9use solana_program::pubkey::{Pubkey, PubkeyError};
10
11pub use generated::types;
12pub use lighthouse_common::{CompactU64, LEB128Vec};
13
14pub mod instructions {
15 pub use crate::generated::instructions::{
16 AssertAccountDataBuilder, AssertAccountDeltaBuilder, AssertAccountInfoBuilder,
17 AssertAccountInfoMultiBuilder, AssertBubblegumTreeConfigAccountBuilder,
18 AssertMerkleTreeAccountBuilder, AssertMintAccountBuilder, AssertMintAccountMultiBuilder,
19 AssertStakeAccountBuilder, AssertStakeAccountMultiBuilder, AssertSysvarClockBuilder,
20 AssertTokenAccountBuilder, AssertTokenAccountMultiBuilder,
21 AssertUpgradeableLoaderAccountBuilder, AssertUpgradeableLoaderAccountMultiBuilder,
22 MemoryCloseBuilder, MemoryWriteBuilder,
23 };
24}
25
26#[cfg(feature = "cpi")]
27pub mod cpi {
28 pub use crate::generated::instructions::{
29 AssertAccountDataCpiBuilder, AssertAccountDeltaCpiBuilder, AssertAccountInfoCpiBuilder,
30 AssertAccountInfoMultiCpiBuilder, AssertBubblegumTreeConfigAccountCpiBuilder,
31 AssertMerkleTreeAccountCpiBuilder, AssertMintAccountCpiBuilder,
32 AssertMintAccountMultiCpiBuilder, AssertStakeAccountCpiBuilder,
33 AssertStakeAccountMultiCpiBuilder, AssertSysvarClockCpiBuilder,
34 AssertTokenAccountCpiBuilder, AssertTokenAccountMultiCpiBuilder,
35 AssertUpgradeableLoaderAccountCpiBuilder, AssertUpgradeableLoaderAccountMultiCpiBuilder,
36 MemoryCloseCpiBuilder, MemoryWriteCpiBuilder,
37 };
38}
39
40pub mod errors {
41 pub use crate::generated::errors::*;
42}
43
44pub fn find_memory_pda(payer: Pubkey, memory_id: u8) -> (solana_program::pubkey::Pubkey, u8) {
45 solana_program::pubkey::Pubkey::find_program_address(
46 &["memory".to_string().as_ref(), payer.as_ref(), &[memory_id]],
47 &crate::ID,
48 )
49}
50
51pub fn find_memory_pda_bump_iterate(
52 payer: Pubkey,
53 memory_id: u8,
54 bump_skip: u8,
55 start_bump: Option<u8>,
56) -> Option<(solana_program::pubkey::Pubkey, u8)> {
57 let memory_ref = "memory".to_string();
58 let seeds = [memory_ref.as_ref(), payer.as_ref(), &[memory_id]];
59
60 let mut bump_seed = [start_bump.unwrap_or(std::u8::MAX)];
61 let mut bump_skip = bump_skip as usize;
62
63 for _ in 0..std::u8::MAX {
64 let mut seeds_with_bump = seeds.to_vec();
65 seeds_with_bump.push(&bump_seed);
66 match Pubkey::create_program_address(&seeds_with_bump, &crate::ID) {
67 Ok(address) => {
68 if bump_skip == 0 {
69 return Some((address, bump_seed[0]));
70 } else {
71 bump_skip -= 1;
72 }
73 }
74 Err(PubkeyError::InvalidSeeds) => {}
75 _ => break,
76 }
77 bump_seed[0] -= 1;
78
79 println!("bump_seed: {:?}", bump_seed[0])
80 }
81
82 None
83}
84
85#[cfg(feature = "sdk")]
86pub mod utils {
87 use crate::generated::types::AssertionResult;
88 use borsh::BorshDeserialize;
89 use solana_sdk::{
90 instruction::{AccountMeta, Instruction},
91 message::{legacy, v0, CompileError, Message, VersionedMessage},
92 signer::{Signer, SignerError},
93 transaction::{Transaction, VersionedTransaction},
94 };
95
96 #[derive(Debug, thiserror::Error)]
97 #[repr(u32)]
98 pub enum ClientError {
99 #[error("Transaction already signed")]
100 TransactionAlreadySigned,
101 #[error("Address table lookups not supported")]
102 AddressTableLookupsNotSupported,
103 #[error("Empty transaction")]
104 EmptyTransaction,
105 #[error("...")]
106 CompileError(CompileError),
107 #[error("...")]
108 SignerError(SignerError),
109 #[error("...")]
110 Base64DecodeError(base64::DecodeError),
111 #[error("...")]
112 IOError(std::io::Error),
113 }
114
115 #[allow(deprecated)]
116 pub fn parse_evaluation_payloads_from_logs(
117 logs: Vec<&String>,
118 ) -> Result<Vec<AssertionResult>, ClientError> {
119 logs.iter()
120 .filter(|log| log.contains("Program data: "))
121 .map(|log| {
122 let encoded = log.split("Program data: ").collect::<Vec<&str>>()[1];
123 let decoded = base64::decode(encoded).map_err(ClientError::Base64DecodeError)?;
124 AssertionResult::try_from_slice(&decoded).map_err(ClientError::IOError)
125 })
126 .collect()
127 }
128
129 pub fn append_instructions_to_transaction(
130 transaction: &Transaction,
131 ixs: Vec<Instruction>,
132 ) -> Result<Transaction, ClientError> {
133 if !transaction.signatures.is_empty() {
134 return Err(ClientError::TransactionAlreadySigned);
135 }
136
137 let mut merged_ixs = decompile_instruction_from_transaction(transaction)?;
138 merged_ixs.extend(ixs);
139
140 let transaction = Transaction::new_unsigned(Message::new(
141 &merged_ixs,
142 Some(
143 transaction
144 .message
145 .account_keys
146 .first()
147 .ok_or(ClientError::EmptyTransaction)?,
148 ),
149 ));
150
151 Ok(transaction)
152 }
153
154 pub fn append_instructions_to_versioned_transaction(
155 transaction: &VersionedTransaction,
156 ixs: Vec<Instruction>,
157 signers: &[&dyn Signer],
158 ) -> Result<VersionedTransaction, ClientError> {
159 if !transaction.signatures.is_empty() {
160 return Err(ClientError::TransactionAlreadySigned);
161 }
162
163 if transaction.message.address_table_lookups().is_some() {
164 return Err(ClientError::AddressTableLookupsNotSupported);
165 }
166
167 let mut merged_ixs = decompile_instruction_from_versioned_transaction(transaction)?;
168 merged_ixs.extend(ixs);
169
170 let payer = transaction
171 .message
172 .static_account_keys()
173 .first()
174 .ok_or(ClientError::EmptyTransaction)?;
175
176 let versioned_messsage = match transaction.message {
177 VersionedMessage::Legacy(_) => {
178 VersionedMessage::Legacy(legacy::Message::new(&merged_ixs, Some(payer)))
179 }
180 VersionedMessage::V0(_) => VersionedMessage::V0(
181 v0::Message::try_compile(
182 payer,
183 &merged_ixs,
184 &[],
185 *transaction.message.recent_blockhash(),
186 )
187 .map_err(ClientError::CompileError)?,
188 ),
189 };
190
191 let mut transaction = VersionedTransaction::try_new(versioned_messsage, signers)
192 .map_err(ClientError::SignerError)?;
193
194 transaction
195 .message
196 .set_recent_blockhash(*transaction.message.recent_blockhash());
197
198 Ok(transaction)
199 }
200
201 pub fn decompile_instruction_from_versioned_transaction(
202 transaction: &VersionedTransaction,
203 ) -> Result<Vec<Instruction>, ClientError> {
204 if !transaction.signatures.is_empty() {
205 return Err(ClientError::TransactionAlreadySigned);
206 }
207
208 if transaction.message.address_table_lookups().is_some() {
209 return Err(ClientError::AddressTableLookupsNotSupported);
210 }
211
212 let mut modified_ixs = vec![];
213 let compiled_ixs = transaction.message.instructions();
214
215 for instruction in compiled_ixs {
216 let account_keys = transaction.message.static_account_keys();
217
218 modified_ixs.push(Instruction {
219 program_id: account_keys[instruction.program_id_index as usize],
220 accounts: instruction
221 .accounts
222 .iter()
223 .map(|index| AccountMeta {
224 pubkey: account_keys[*index as usize],
225 is_signer: index < &transaction.message.header().num_required_signatures,
226 is_writable: transaction.message.is_maybe_writable(*index as usize),
227 })
228 .collect(),
229 data: instruction.data.clone(),
230 });
231 }
232
233 Ok(modified_ixs)
234 }
235
236 pub fn decompile_instruction_from_transaction(
237 transaction: &Transaction,
238 ) -> Result<Vec<Instruction>, ClientError> {
239 if !transaction.signatures.is_empty() {
240 return Err(ClientError::TransactionAlreadySigned);
241 }
242
243 let mut modified_ixs = vec![];
244
245 for instruction in &transaction.message.instructions {
246 modified_ixs.push(Instruction {
247 program_id: transaction.message.account_keys[instruction.program_id_index as usize],
248 accounts: instruction
249 .accounts
250 .iter()
251 .map(|index| AccountMeta {
252 pubkey: transaction.message.account_keys[*index as usize],
253 is_signer: index < &transaction.message.header.num_required_signatures,
254 is_writable: transaction.message.is_writable(*index as usize),
255 })
256 .collect(),
257 data: instruction.data.clone(),
258 });
259 }
260
261 Ok(modified_ixs)
262 }
263}