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::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.clone(), 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.len() > 0 {
  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)
  151. .unwrap_or_else(|e| {
  152. println!(
  153. "Unable to parse log: {}",
  154. e.to_string()
  155. );
  156. std::process::exit(1);
  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(ClientError::LogParseError(l.to_string()))?;
  244. let program = c
  245. .get(1)
  246. .ok_or(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.len() > 0);
  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.len() > 0);
  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. pub fn new(mut self, args: impl InstructionData) -> Self {
  358. assert!(self.namespace == RequestNamespace::State { new: false });
  359. self.namespace = RequestNamespace::State { new: true };
  360. self.instruction_data = Some(args.data());
  361. self
  362. }
  363. pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
  364. self.signers.push(signer);
  365. self
  366. }
  367. pub fn send(self) -> Result<Signature, ClientError> {
  368. let accounts = match self.namespace {
  369. RequestNamespace::State { new } => {
  370. let mut accounts = match new {
  371. false => vec![AccountMeta::new(
  372. anchor_lang::__private::state::address(&self.program_id),
  373. false,
  374. )],
  375. true => vec![
  376. AccountMeta::new_readonly(self.payer.pubkey(), true),
  377. AccountMeta::new(
  378. anchor_lang::__private::state::address(&self.program_id),
  379. false,
  380. ),
  381. AccountMeta::new_readonly(
  382. Pubkey::find_program_address(&[], &self.program_id).0,
  383. false,
  384. ),
  385. AccountMeta::new_readonly(system_program::ID, false),
  386. AccountMeta::new_readonly(self.program_id, false),
  387. AccountMeta::new_readonly(rent::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_eq!(did_pop, true);
  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_eq!(did_pop, false);
  446. }
  447. }