lib.rs 16 KB

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