lib.rs 16 KB

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