switchboard_solana/attestation_program/client/
request.rs1use crate::*;
2
3use kv_log_macro::info;
4use solana_client::rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig};
5use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel};
6use solana_sdk::signer::Signer;
7use std::sync::Arc;
8
9#[derive(Default, Debug, Clone)]
10pub struct FunctionRequestFilters {
11 pub attestation_queue: Option<Pubkey>,
12 pub authority: Option<Pubkey>,
13 pub is_triggered: Option<bool>,
14 pub is_active: Option<bool>,
15 pub queue_idx: Option<u32>,
19}
20
21impl FunctionRequestFilters {
22 pub fn to_vec(&self) -> Vec<solana_client::rpc_filter::RpcFilterType> {
23 let mut filters = vec![FunctionRequestAccountData::get_discriminator_filter()];
24
25 if let Some(attestation_queue) = &self.attestation_queue {
27 filters.push(FunctionRequestAccountData::get_queue_filter(
28 attestation_queue,
29 ));
30 }
31
32 if let Some(authority) = &self.authority {
34 filters.push(FunctionRequestAccountData::get_authority_filter(authority));
35 }
36
37 if self.is_triggered.is_some() && self.is_active.is_some() {
39 filters.push(FunctionRequestAccountData::get_is_triggered_and_active_filter());
40 } else {
41 if let Some(is_triggered) = &self.is_triggered {
43 if *is_triggered {
44 filters.push(FunctionRequestAccountData::get_is_triggered_filter());
45 }
46 }
47
48 if let Some(is_active) = &self.is_active {
50 if *is_active {
51 filters.push(FunctionRequestAccountData::get_is_active_filter());
52 }
53 }
54 }
55
56 if let Some(queue_idx) = &self.queue_idx {
58 filters.push(FunctionRequestAccountData::get_queue_idx_filter(queue_idx));
59 }
60
61 filters
62 }
63}
64
65impl FunctionRequestAccountData {
66 pub async fn get_program_accounts(
72 rpc: &solana_client::nonblocking::rpc_client::RpcClient,
73 filters: FunctionRequestFilters,
74 commitment: Option<CommitmentLevel>,
75 ) -> Result<Vec<(Pubkey, FunctionRequestAccountData)>, SbError> {
76 let mut requests = vec![];
77
78 let accounts = rpc
79 .get_program_accounts_with_config(
80 &SWITCHBOARD_ATTESTATION_PROGRAM_ID,
81 RpcProgramAccountsConfig {
82 filters: Some(filters.to_vec()),
83 account_config: RpcAccountInfoConfig {
84 encoding: Some(solana_account_decoder::UiAccountEncoding::Base64Zstd),
85 commitment: Some(CommitmentConfig {
86 commitment: commitment.unwrap_or(CommitmentLevel::Processed),
87 }),
88 ..Default::default()
89 },
90 ..Default::default()
91 },
92 )
93 .await
94 .map_err(|e| SbError::CustomError {
95 message: "Failed to get program accounts".to_string(),
96 source: Arc::new(e),
97 })?;
98
99 for (pubkey, account) in accounts {
100 if let Ok(request_data) =
101 FunctionRequestAccountData::try_deserialize(&mut &account.data[..])
102 {
103 requests.push((pubkey, request_data));
104 }
105 }
106
107 Ok(requests)
108 }
109
110 pub async fn get_or_create_from_seed(
111 rpc: &solana_client::nonblocking::rpc_client::RpcClient,
112 payer: std::sync::Arc<Keypair>,
113 function: Pubkey,
114 seed: Option<&str>,
115 params: Option<FunctionRequestInitAndTriggerParams>,
116 ) -> Result<Pubkey, SbError> {
117 let params = params.unwrap_or_default();
118 let request_keypair = crate::keypair_from_base_seed(
119 format!("request-{}-{}", function, seed.unwrap_or("default")).as_str(),
120 payer.secret().to_bytes().to_vec(),
121 Some(params.try_to_vec().unwrap()),
122 )
123 .unwrap();
124 let request_pubkey = request_keypair.pubkey();
125
126 if let Err(SbError::AccountNotFound) =
127 FunctionRequestAccountData::fetch_async(rpc, request_pubkey).await
128 {
129 info!(
130 "[Request] creating new request account {} ...",
131 request_pubkey
132 );
133
134 let function_data = FunctionAccountData::fetch_async(rpc, function)
136 .await
137 .unwrap();
138 if function_data.requests_require_authorization != 0
139 && payer.pubkey() != function_data.authority
140 {
141 return Err(SbError::Message("MissingAuthoritySigner"));
142 }
143
144 let req_init_ixn = FunctionRequestInitAndTrigger::build_ix(
146 &FunctionRequestInitAndTriggerAccounts {
147 request: request_pubkey,
148 authority: payer.pubkey(),
149 function,
150 function_authority: None,
151 attestation_queue: function_data.attestation_queue,
152 payer: payer.pubkey(),
153 },
154 ¶ms,
155 )
156 .unwrap();
157
158 let tx = crate::ix_to_tx(
159 &[req_init_ixn],
160 &[&*payer, &request_keypair],
161 rpc.get_latest_blockhash().await.unwrap_or_default(),
162 )
163 .unwrap();
164
165 let signature = rpc.send_and_confirm_transaction(&tx).await.unwrap();
166
167 info!(
168 "[Request] request {} initialized. Tx Signature: {}",
169 request_pubkey, signature
170 );
171 };
172
173 Ok(request_pubkey)
174 }
175
176 pub fn fetch(
181 client: &solana_client::rpc_client::RpcClient,
182 pubkey: Pubkey,
183 ) -> std::result::Result<Self, switchboard_common::SbError> {
184 crate::client::fetch_borsh_account(client, pubkey)
185 }
186
187 pub async fn fetch_async(
188 client: &solana_client::nonblocking::rpc_client::RpcClient,
189 pubkey: Pubkey,
190 ) -> std::result::Result<Self, switchboard_common::SbError> {
191 crate::client::fetch_borsh_account_async(client, pubkey).await
192 }
193
194 pub fn fetch_sync<T: solana_sdk::client::SyncClient>(
195 client: &T,
196 pubkey: Pubkey,
197 ) -> std::result::Result<Self, switchboard_common::SbError> {
198 crate::client::fetch_borsh_account_sync(client, pubkey)
199 }
200
201 pub fn get_discriminator_filter() -> solana_client::rpc_filter::RpcFilterType {
204 solana_client::rpc_filter::RpcFilterType::Memcmp(
205 solana_client::rpc_filter::Memcmp::new_raw_bytes(
206 0,
207 FunctionRequestAccountData::discriminator().to_vec(),
208 ),
209 )
210 }
211
212 pub fn get_is_triggered_filter() -> solana_client::rpc_filter::RpcFilterType {
213 solana_client::rpc_filter::RpcFilterType::Memcmp(
214 solana_client::rpc_filter::Memcmp::new_raw_bytes(8, vec![1u8]),
215 )
216 }
217
218 pub fn get_is_active_filter() -> solana_client::rpc_filter::RpcFilterType {
219 solana_client::rpc_filter::RpcFilterType::Memcmp(
220 solana_client::rpc_filter::Memcmp::new_raw_bytes(
221 9,
222 vec![RequestStatus::RequestPending as u8],
223 ),
224 )
225 }
226
227 pub fn get_is_triggered_and_active_filter() -> solana_client::rpc_filter::RpcFilterType {
228 solana_client::rpc_filter::RpcFilterType::Memcmp(
229 solana_client::rpc_filter::Memcmp::new_raw_bytes(
230 8,
231 vec![1u8, RequestStatus::RequestPending as u8],
232 ),
233 )
234 }
235
236 pub fn get_queue_filter(queue_pubkey: &Pubkey) -> solana_client::rpc_filter::RpcFilterType {
237 solana_client::rpc_filter::RpcFilterType::Memcmp(
238 solana_client::rpc_filter::Memcmp::new_raw_bytes(138, queue_pubkey.to_bytes().into()),
239 )
240 }
241
242 pub fn get_queue_idx_filter(queue_idx: &u32) -> solana_client::rpc_filter::RpcFilterType {
243 solana_client::rpc_filter::RpcFilterType::Memcmp(
244 solana_client::rpc_filter::Memcmp::new_raw_bytes(275, queue_idx.to_le_bytes().to_vec()),
245 )
246 }
247
248 pub fn get_authority_filter(
249 authority_pubkey: &Pubkey,
250 ) -> solana_client::rpc_filter::RpcFilterType {
251 solana_client::rpc_filter::RpcFilterType::Memcmp(
252 solana_client::rpc_filter::Memcmp::new_raw_bytes(
253 10,
254 authority_pubkey.to_bytes().into(),
255 ),
256 )
257 }
258
259 pub fn get_is_ready_filters(
260 queue_pubkey: &Pubkey,
261 ) -> Vec<solana_client::rpc_filter::RpcFilterType> {
262 vec![
263 FunctionRequestAccountData::get_discriminator_filter(),
264 FunctionRequestAccountData::get_is_triggered_filter(),
265 FunctionRequestAccountData::get_is_active_filter(),
266 FunctionRequestAccountData::get_queue_filter(queue_pubkey),
267 ]
268 }
269
270 pub fn calc_container_params_hash(&self) -> [u8; 32] {
271 solana_program::hash::hash(&self.container_params).to_bytes()
272 }
273}