lib.rs 16 KB

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