lib.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. //! `anchor_client` provides an RPC client to send transactions and fetch
  2. //! deserialized accounts from Solana programs written in `anchor_lang`.
  3. use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
  4. use anchor_lang::solana_program::program_error::ProgramError;
  5. use anchor_lang::solana_program::pubkey::Pubkey;
  6. use anchor_lang::solana_program::system_program;
  7. use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
  8. use regex::Regex;
  9. use solana_account_decoder::UiAccountEncoding;
  10. use solana_client::client_error::ClientError as SolanaClientError;
  11. use solana_client::pubsub_client::{PubsubClient, PubsubClientError, PubsubClientSubscription};
  12. use solana_client::rpc_client::RpcClient;
  13. use solana_client::rpc_config::{
  14. RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcTransactionLogsConfig,
  15. RpcTransactionLogsFilter,
  16. };
  17. use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
  18. use solana_client::rpc_response::{Response as RpcResponse, RpcLogsResponse};
  19. use solana_sdk::account::Account;
  20. use solana_sdk::bs58;
  21. use solana_sdk::commitment_config::CommitmentConfig;
  22. use solana_sdk::signature::{Signature, Signer};
  23. use solana_sdk::transaction::Transaction;
  24. use std::convert::Into;
  25. use std::iter::Map;
  26. use std::rc::Rc;
  27. use std::vec::IntoIter;
  28. use thiserror::Error;
  29. pub use anchor_lang;
  30. pub use cluster::Cluster;
  31. pub use solana_client;
  32. pub use solana_sdk;
  33. mod cluster;
  34. const PROGRAM_LOG: &str = "Program log: ";
  35. const PROGRAM_DATA: &str = "Program data: ";
  36. /// EventHandle unsubscribes from a program event stream on drop.
  37. pub type EventHandle = PubsubClientSubscription<RpcResponse<RpcLogsResponse>>;
  38. /// Client defines the base configuration for building RPC clients to
  39. /// communicate with Anchor programs running on a Solana cluster. It's
  40. /// primary use is to build a `Program` client via the `program` method.
  41. pub struct Client {
  42. cfg: Config,
  43. }
  44. impl Client {
  45. pub fn new(cluster: Cluster, payer: Rc<dyn Signer>) -> Self {
  46. Self {
  47. cfg: Config {
  48. cluster,
  49. payer,
  50. options: None,
  51. },
  52. }
  53. }
  54. pub fn new_with_options(
  55. cluster: Cluster,
  56. payer: Rc<dyn Signer>,
  57. options: CommitmentConfig,
  58. ) -> Self {
  59. Self {
  60. cfg: Config {
  61. cluster,
  62. payer,
  63. options: Some(options),
  64. },
  65. }
  66. }
  67. pub fn program(&self, program_id: Pubkey) -> Program {
  68. Program {
  69. program_id,
  70. cfg: Config {
  71. cluster: self.cfg.cluster.clone(),
  72. options: self.cfg.options,
  73. payer: self.cfg.payer.clone(),
  74. },
  75. }
  76. }
  77. }
  78. // Internal configuration for a client.
  79. #[derive(Debug)]
  80. struct Config {
  81. cluster: Cluster,
  82. payer: Rc<dyn Signer>,
  83. options: Option<CommitmentConfig>,
  84. }
  85. /// Program is the primary client handle to be used to build and send requests.
  86. #[derive(Debug)]
  87. pub struct Program {
  88. program_id: Pubkey,
  89. cfg: Config,
  90. }
  91. impl Program {
  92. pub fn payer(&self) -> Pubkey {
  93. self.cfg.payer.pubkey()
  94. }
  95. /// Returns a request builder.
  96. pub fn request(&self) -> RequestBuilder {
  97. RequestBuilder::from(
  98. self.program_id,
  99. self.cfg.cluster.url(),
  100. self.cfg.payer.clone(),
  101. self.cfg.options,
  102. RequestNamespace::Global,
  103. )
  104. }
  105. /// Returns a request builder for program state.
  106. pub fn state_request(&self) -> RequestBuilder {
  107. RequestBuilder::from(
  108. self.program_id,
  109. self.cfg.cluster.url(),
  110. self.cfg.payer.clone(),
  111. self.cfg.options,
  112. RequestNamespace::State { new: false },
  113. )
  114. }
  115. /// Returns the account at the given address.
  116. pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
  117. let rpc_client = RpcClient::new_with_commitment(
  118. self.cfg.cluster.url().to_string(),
  119. self.cfg.options.unwrap_or_default(),
  120. );
  121. let account = rpc_client
  122. .get_account_with_commitment(&address, CommitmentConfig::processed())?
  123. .value
  124. .ok_or(ClientError::AccountNotFound)?;
  125. let mut data: &[u8] = &account.data;
  126. T::try_deserialize(&mut data).map_err(Into::into)
  127. }
  128. /// Returns all program accounts of the given type matching the given filters
  129. pub fn accounts<T: AccountDeserialize + Discriminator>(
  130. &self,
  131. filters: Vec<RpcFilterType>,
  132. ) -> Result<Vec<(Pubkey, T)>, ClientError> {
  133. self.accounts_lazy(filters)?.collect()
  134. }
  135. /// Returns all program accounts of the given type matching the given filters as an iterator
  136. /// Deserialization is executed lazily
  137. pub fn accounts_lazy<T: AccountDeserialize + Discriminator>(
  138. &self,
  139. filters: Vec<RpcFilterType>,
  140. ) -> Result<ProgramAccountsIterator<T>, ClientError> {
  141. let account_type_filter = RpcFilterType::Memcmp(Memcmp {
  142. offset: 0,
  143. bytes: MemcmpEncodedBytes::Base58(bs58::encode(T::discriminator()).into_string()),
  144. encoding: None,
  145. });
  146. let config = RpcProgramAccountsConfig {
  147. filters: Some([vec![account_type_filter], filters].concat()),
  148. account_config: RpcAccountInfoConfig {
  149. encoding: Some(UiAccountEncoding::Base64),
  150. data_slice: None,
  151. commitment: None,
  152. },
  153. with_context: None,
  154. };
  155. Ok(ProgramAccountsIterator {
  156. inner: self
  157. .rpc()
  158. .get_program_accounts_with_config(&self.id(), config)?
  159. .into_iter()
  160. .map(|(key, account)| {
  161. Ok((key, T::try_deserialize(&mut (&account.data as &[u8]))?))
  162. }),
  163. })
  164. }
  165. pub fn state<T: AccountDeserialize>(&self) -> Result<T, ClientError> {
  166. self.account(anchor_lang::__private::state::address(&self.program_id))
  167. }
  168. pub fn rpc(&self) -> RpcClient {
  169. RpcClient::new_with_commitment(
  170. self.cfg.cluster.url().to_string(),
  171. self.cfg.options.unwrap_or_default(),
  172. )
  173. }
  174. pub fn id(&self) -> Pubkey {
  175. self.program_id
  176. }
  177. pub fn on<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
  178. &self,
  179. f: impl Fn(&EventContext, T) + Send + 'static,
  180. ) -> Result<EventHandle, ClientError> {
  181. let addresses = vec![self.program_id.to_string()];
  182. let filter = RpcTransactionLogsFilter::Mentions(addresses);
  183. let ws_url = self.cfg.cluster.ws_url().to_string();
  184. let cfg = RpcTransactionLogsConfig {
  185. commitment: self.cfg.options,
  186. };
  187. let self_program_str = self.program_id.to_string();
  188. let (client, receiver) = PubsubClient::logs_subscribe(&ws_url, filter, cfg)?;
  189. std::thread::spawn(move || {
  190. loop {
  191. match receiver.recv() {
  192. Ok(logs) => {
  193. let ctx = EventContext {
  194. signature: logs.value.signature.parse().unwrap(),
  195. slot: logs.context.slot,
  196. };
  197. let mut logs = &logs.value.logs[..];
  198. if !logs.is_empty() {
  199. if let Ok(mut execution) = Execution::new(&mut logs) {
  200. for l in logs {
  201. // Parse the log.
  202. let (event, new_program, did_pop) = {
  203. if self_program_str == execution.program() {
  204. handle_program_log(&self_program_str, l).unwrap_or_else(
  205. |e| {
  206. println!("Unable to parse log: {}", e);
  207. std::process::exit(1);
  208. },
  209. )
  210. } else {
  211. let (program, did_pop) =
  212. handle_system_log(&self_program_str, l);
  213. (None, program, did_pop)
  214. }
  215. };
  216. // Emit the event.
  217. if let Some(e) = event {
  218. f(&ctx, e);
  219. }
  220. // Switch program context on CPI.
  221. if let Some(new_program) = new_program {
  222. execution.push(new_program);
  223. }
  224. // Program returned.
  225. if did_pop {
  226. execution.pop();
  227. }
  228. }
  229. }
  230. }
  231. }
  232. Err(_err) => {
  233. return;
  234. }
  235. }
  236. }
  237. });
  238. Ok(client)
  239. }
  240. }
  241. /// Iterator with items of type (Pubkey, T). Used to lazily deserialize account structs.
  242. /// Wrapper type hides the inner type from usages so the implementation can be changed.
  243. pub struct ProgramAccountsIterator<T> {
  244. inner: Map<IntoIter<(Pubkey, Account)>, AccountConverterFunction<T>>,
  245. }
  246. /// Function type that accepts solana accounts and returns deserialized anchor accounts
  247. type AccountConverterFunction<T> = fn((Pubkey, Account)) -> Result<(Pubkey, T), ClientError>;
  248. impl<T> Iterator for ProgramAccountsIterator<T> {
  249. type Item = Result<(Pubkey, T), ClientError>;
  250. fn next(&mut self) -> Option<Self::Item> {
  251. self.inner.next()
  252. }
  253. }
  254. fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
  255. self_program_str: &str,
  256. l: &str,
  257. ) -> Result<(Option<T>, Option<String>, bool), ClientError> {
  258. // Log emitted from the current program.
  259. if let Some(log) = l
  260. .strip_prefix(PROGRAM_LOG)
  261. .or_else(|| l.strip_prefix(PROGRAM_DATA))
  262. {
  263. let borsh_bytes = match anchor_lang::__private::base64::decode(&log) {
  264. Ok(borsh_bytes) => borsh_bytes,
  265. _ => {
  266. #[cfg(feature = "debug")]
  267. println!("Could not base64 decode log: {}", log);
  268. return Ok((None, None, false));
  269. }
  270. };
  271. let mut slice: &[u8] = &borsh_bytes[..];
  272. let disc: [u8; 8] = {
  273. let mut disc = [0; 8];
  274. disc.copy_from_slice(&borsh_bytes[..8]);
  275. slice = &slice[8..];
  276. disc
  277. };
  278. let mut event = None;
  279. if disc == T::discriminator() {
  280. let e: T = anchor_lang::AnchorDeserialize::deserialize(&mut slice)
  281. .map_err(|e| ClientError::LogParseError(e.to_string()))?;
  282. event = Some(e);
  283. }
  284. Ok((event, None, false))
  285. }
  286. // System log.
  287. else {
  288. let (program, did_pop) = handle_system_log(self_program_str, l);
  289. Ok((None, program, did_pop))
  290. }
  291. }
  292. fn handle_system_log(this_program_str: &str, log: &str) -> (Option<String>, bool) {
  293. if log.starts_with(&format!("Program {} log:", this_program_str)) {
  294. (Some(this_program_str.to_string()), false)
  295. } else if log.contains("invoke") {
  296. (Some("cpi".to_string()), false) // Any string will do.
  297. } else {
  298. let re = Regex::new(r"^Program (.*) success*$").unwrap();
  299. if re.is_match(log) {
  300. (None, true)
  301. } else {
  302. (None, false)
  303. }
  304. }
  305. }
  306. struct Execution {
  307. stack: Vec<String>,
  308. }
  309. impl Execution {
  310. pub fn new(logs: &mut &[String]) -> Result<Self, ClientError> {
  311. let l = &logs[0];
  312. *logs = &logs[1..];
  313. let re = Regex::new(r"^Program (.*) invoke.*$").unwrap();
  314. let c = re
  315. .captures(l)
  316. .ok_or_else(|| ClientError::LogParseError(l.to_string()))?;
  317. let program = c
  318. .get(1)
  319. .ok_or_else(|| ClientError::LogParseError(l.to_string()))?
  320. .as_str()
  321. .to_string();
  322. Ok(Self {
  323. stack: vec![program],
  324. })
  325. }
  326. pub fn program(&self) -> String {
  327. assert!(!self.stack.is_empty());
  328. self.stack[self.stack.len() - 1].clone()
  329. }
  330. pub fn push(&mut self, new_program: String) {
  331. self.stack.push(new_program);
  332. }
  333. pub fn pop(&mut self) {
  334. assert!(!self.stack.is_empty());
  335. self.stack.pop().unwrap();
  336. }
  337. }
  338. #[derive(Debug)]
  339. pub struct EventContext {
  340. pub signature: Signature,
  341. pub slot: u64,
  342. }
  343. #[derive(Debug, Error)]
  344. pub enum ClientError {
  345. #[error("Account not found")]
  346. AccountNotFound,
  347. #[error("{0}")]
  348. AnchorError(#[from] anchor_lang::error::Error),
  349. #[error("{0}")]
  350. ProgramError(#[from] ProgramError),
  351. #[error("{0}")]
  352. SolanaClientError(#[from] SolanaClientError),
  353. #[error("{0}")]
  354. SolanaClientPubsubError(#[from] PubsubClientError),
  355. #[error("Unable to parse log: {0}")]
  356. LogParseError(String),
  357. }
  358. /// `RequestBuilder` provides a builder interface to create and send
  359. /// transactions to a cluster.
  360. pub struct RequestBuilder<'a> {
  361. cluster: String,
  362. program_id: Pubkey,
  363. accounts: Vec<AccountMeta>,
  364. options: CommitmentConfig,
  365. instructions: Vec<Instruction>,
  366. payer: Rc<dyn Signer>,
  367. // Serialized instruction data for the target RPC.
  368. instruction_data: Option<Vec<u8>>,
  369. signers: Vec<&'a dyn Signer>,
  370. // True if the user is sending a state instruction.
  371. namespace: RequestNamespace,
  372. }
  373. #[derive(PartialEq)]
  374. pub enum RequestNamespace {
  375. Global,
  376. State {
  377. // True if the request is to the state's new ctor.
  378. new: bool,
  379. },
  380. Interface,
  381. }
  382. impl<'a> RequestBuilder<'a> {
  383. pub fn from(
  384. program_id: Pubkey,
  385. cluster: &str,
  386. payer: Rc<dyn Signer>,
  387. options: Option<CommitmentConfig>,
  388. namespace: RequestNamespace,
  389. ) -> Self {
  390. Self {
  391. program_id,
  392. payer,
  393. cluster: cluster.to_string(),
  394. accounts: Vec::new(),
  395. options: options.unwrap_or_default(),
  396. instructions: Vec::new(),
  397. instruction_data: None,
  398. signers: Vec::new(),
  399. namespace,
  400. }
  401. }
  402. #[must_use]
  403. pub fn payer(mut self, payer: Rc<dyn Signer>) -> Self {
  404. self.payer = payer;
  405. self
  406. }
  407. #[must_use]
  408. pub fn cluster(mut self, url: &str) -> Self {
  409. self.cluster = url.to_string();
  410. self
  411. }
  412. #[must_use]
  413. pub fn instruction(mut self, ix: Instruction) -> Self {
  414. self.instructions.push(ix);
  415. self
  416. }
  417. #[must_use]
  418. pub fn program(mut self, program_id: Pubkey) -> Self {
  419. self.program_id = program_id;
  420. self
  421. }
  422. #[must_use]
  423. pub fn accounts(mut self, accounts: impl ToAccountMetas) -> Self {
  424. let mut metas = accounts.to_account_metas(None);
  425. self.accounts.append(&mut metas);
  426. self
  427. }
  428. #[must_use]
  429. pub fn options(mut self, options: CommitmentConfig) -> Self {
  430. self.options = options;
  431. self
  432. }
  433. #[must_use]
  434. pub fn args(mut self, args: impl InstructionData) -> Self {
  435. self.instruction_data = Some(args.data());
  436. self
  437. }
  438. /// Invokes the `#[state]`'s `new` constructor.
  439. #[allow(clippy::wrong_self_convention)]
  440. #[must_use]
  441. pub fn new(mut self, args: impl InstructionData) -> Self {
  442. assert!(self.namespace == RequestNamespace::State { new: false });
  443. self.namespace = RequestNamespace::State { new: true };
  444. self.instruction_data = Some(args.data());
  445. self
  446. }
  447. #[must_use]
  448. pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
  449. self.signers.push(signer);
  450. self
  451. }
  452. pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
  453. let mut accounts = match self.namespace {
  454. RequestNamespace::State { new } => match new {
  455. false => vec![AccountMeta::new(
  456. anchor_lang::__private::state::address(&self.program_id),
  457. false,
  458. )],
  459. true => vec![
  460. AccountMeta::new_readonly(self.payer.pubkey(), true),
  461. AccountMeta::new(
  462. anchor_lang::__private::state::address(&self.program_id),
  463. false,
  464. ),
  465. AccountMeta::new_readonly(
  466. Pubkey::find_program_address(&[], &self.program_id).0,
  467. false,
  468. ),
  469. AccountMeta::new_readonly(system_program::ID, false),
  470. AccountMeta::new_readonly(self.program_id, false),
  471. ],
  472. },
  473. _ => Vec::new(),
  474. };
  475. accounts.extend_from_slice(&self.accounts);
  476. let mut instructions = self.instructions.clone();
  477. if let Some(ix_data) = &self.instruction_data {
  478. instructions.push(Instruction {
  479. program_id: self.program_id,
  480. data: ix_data.clone(),
  481. accounts,
  482. });
  483. }
  484. Ok(instructions)
  485. }
  486. pub fn send(self) -> Result<Signature, ClientError> {
  487. let instructions = self.instructions()?;
  488. let mut signers = self.signers;
  489. signers.push(&*self.payer);
  490. let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
  491. let tx = {
  492. let latest_hash = rpc_client.get_latest_blockhash()?;
  493. Transaction::new_signed_with_payer(
  494. &instructions,
  495. Some(&self.payer.pubkey()),
  496. &signers,
  497. latest_hash,
  498. )
  499. };
  500. rpc_client
  501. .send_and_confirm_transaction(&tx)
  502. .map_err(Into::into)
  503. }
  504. }
  505. #[cfg(test)]
  506. mod tests {
  507. use super::*;
  508. #[test]
  509. fn new_execution() {
  510. let mut logs: &[String] =
  511. &["Program 7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw invoke [1]".to_string()];
  512. let exe = Execution::new(&mut logs).unwrap();
  513. assert_eq!(
  514. exe.stack[0],
  515. "7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw".to_string()
  516. );
  517. }
  518. #[test]
  519. fn handle_system_log_pop() {
  520. let log = "Program 7Y8VDzehoewALqJfyxZYMgYCnMTCDhWuGfJKUvjYWATw success";
  521. let (program, did_pop) = handle_system_log("asdf", log);
  522. assert_eq!(program, None);
  523. assert!(did_pop);
  524. }
  525. #[test]
  526. fn handle_system_log_no_pop() {
  527. let log = "Program 7swsTUiQ6KUK4uFYquQKg4epFRsBnvbrTf2fZQCa2sTJ qwer";
  528. let (program, did_pop) = handle_system_log("asdf", log);
  529. assert_eq!(program, None);
  530. assert!(!did_pop);
  531. }
  532. }