sol.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. use std::convert::TryFrom;
  2. use std::str::FromStr;
  3. use std::time::Duration;
  4. use async_native_tls::TlsConnector;
  5. use async_std::sync::{Arc, Mutex};
  6. use async_trait::async_trait;
  7. use futures::{SinkExt, StreamExt};
  8. use log::{debug, error};
  9. use rand::rngs::OsRng;
  10. use serde::Serialize;
  11. use serde_json::{json, Value};
  12. use solana_client::{blockhash_query::BlockhashQuery, rpc_client::RpcClient};
  13. use solana_sdk::{
  14. native_token::lamports_to_sol,
  15. program_pack::Pack,
  16. pubkey::Pubkey,
  17. signature::{Signature, Signer},
  18. signer::keypair::Keypair,
  19. system_instruction,
  20. transaction::Transaction,
  21. };
  22. use spl_associated_token_account::{create_associated_token_account, get_associated_token_address};
  23. use tungstenite::Message;
  24. use crate::rpc::{jsonrpc, jsonrpc::JsonResult, websockets};
  25. use crate::serial::{deserialize, serialize, Decodable, Encodable};
  26. use crate::util::{generate_id, NetworkName};
  27. use crate::{Error, Result};
  28. use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
  29. pub const SOL_NATIVE_TOKEN_ID: &str = "So11111111111111111111111111111111111111112";
  30. #[derive(Serialize)]
  31. struct SubscribeParams {
  32. encoding: Value,
  33. commitment: Value,
  34. }
  35. pub struct SolClient {
  36. main_keypair: Keypair,
  37. // Subscriptions vector of pubkey
  38. subscriptions: Arc<Mutex<Vec<Pubkey>>>,
  39. notify_channel: (
  40. async_channel::Sender<TokenNotification>,
  41. async_channel::Receiver<TokenNotification>,
  42. ),
  43. rpc_server: &'static str,
  44. wss_server: &'static str,
  45. }
  46. impl SolClient {
  47. pub async fn new(main_keypair: Vec<u8>, network: &str) -> Result<Arc<Self>> {
  48. let main_keypair: Keypair = deserialize(&main_keypair)?;
  49. let notify_channel = async_channel::unbounded();
  50. debug!("Main SOL wallet pubkey: {:?}", &main_keypair.pubkey());
  51. let (rpc_server, wss_server) = match network {
  52. "mainnet" => (
  53. "https://api.mainnet-beta.solana.com",
  54. "wss://api.devnet.solana.com",
  55. ),
  56. "devnet" => (
  57. "https://api.devnet.solana.com",
  58. "wss://api.devnet.solana.com",
  59. ),
  60. "testnet" => (
  61. "https://api.testnet.solana.com",
  62. "wss://api.testnet.solana.com",
  63. ),
  64. "localhost" => ("http://localhost:8899", "ws://localhost:8900"),
  65. _ => return Err(Error::NotSupportedNetwork),
  66. };
  67. Ok(Arc::new(Self {
  68. main_keypair,
  69. subscriptions: Arc::new(Mutex::new(Vec::new())),
  70. notify_channel,
  71. rpc_server,
  72. wss_server,
  73. }))
  74. }
  75. // TODO: Make this function more robust. Currently we just call it
  76. // and put it in the background. This means no errors are actually
  77. // handled, and it just fails silently.
  78. async fn handle_subscribe_request(
  79. self: Arc<Self>,
  80. keypair: Keypair,
  81. drk_pub_key: jubjub::SubgroupPoint,
  82. mint: Option<Pubkey>,
  83. ) -> SolResult<()> {
  84. debug!(target: "SOL BRIDGE", "handle_subscribe_request()");
  85. // Derive token pubkey if mint was provided.
  86. let pubkey = if mint.is_some() {
  87. get_associated_token_address(&keypair.pubkey(), &mint.unwrap())
  88. } else {
  89. keypair.pubkey()
  90. };
  91. // Check if we're already subscribed
  92. if self.subscriptions.lock().await.contains(&pubkey) {
  93. return Ok(());
  94. }
  95. let rpc = RpcClient::new(self.rpc_server.to_string());
  96. // Fetch the current balance.
  97. let (prev_balance, decimals) = if mint.is_none() {
  98. (rpc.get_balance(&pubkey).map_err(SolFailed::from)?, 9)
  99. } else {
  100. match get_account_token_balance(&rpc, &pubkey, &mint.unwrap()) {
  101. Ok(v) => v,
  102. Err(_) => {
  103. let (exists, decimals) = account_is_initialized_mint(&rpc, &mint.unwrap());
  104. if !exists {
  105. debug!("Could not figure out the number of decimals in SPL token");
  106. return Err(SolFailed::MintIsNotValid(mint.unwrap().to_string()));
  107. }
  108. (0, decimals)
  109. }
  110. }
  111. };
  112. // WebSocket connection
  113. let builder = native_tls::TlsConnector::builder();
  114. let tls = TlsConnector::from(builder);
  115. let (stream, _) = websockets::connect(self.wss_server, tls).await?;
  116. let (mut write, mut read) = stream.split();
  117. // Subscription request build
  118. let sub_params = SubscribeParams {
  119. encoding: json!("jsonParsed"),
  120. commitment: json!("finalized"),
  121. };
  122. let subscription = jsonrpc::request(
  123. json!("accountSubscribe"),
  124. json!([json!(pubkey.to_string()), json!(sub_params)]),
  125. );
  126. debug!(target: "SOLANA RPC", "--> {}", serde_json::to_string(&subscription)?);
  127. write
  128. .send(Message::text(serde_json::to_string(&subscription)?))
  129. .await?;
  130. // Subscription ID used for unsubscribing later.
  131. let mut sub_id: i64 = 0;
  132. // The balance we are going to receive from the JSONRPC notification
  133. let cur_balance: u64;
  134. let ping_payload: Vec<u8> = vec![42, 33, 31, 42];
  135. loop {
  136. // TODO: Might be a bit fragile
  137. let message = read.next().await.ok_or(Error::TungsteniteError)?;
  138. let message = message.unwrap();
  139. match message.clone() {
  140. Message::Pong(_) => {
  141. async_std::task::sleep(Duration::from_secs(1)).await;
  142. write.send(Message::Ping(ping_payload.clone())).await?;
  143. continue;
  144. }
  145. _ => {}
  146. }
  147. match serde_json::from_slice(&message.into_data())? {
  148. JsonResult::Resp(r) => {
  149. // ACK
  150. debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&r)?);
  151. self.subscriptions.lock().await.push(pubkey);
  152. sub_id = r.result.as_i64().unwrap();
  153. // Start sending pings
  154. write.send(Message::Ping(ping_payload.clone())).await?;
  155. }
  156. JsonResult::Err(e) => {
  157. debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&e)?);
  158. // TODO: Try removing pubkey from subscriptions here?
  159. return Err(SolFailed::RpcError(e.error.message.to_string()));
  160. }
  161. JsonResult::Notif(n) => {
  162. // Account updated
  163. debug!(target: "SOLANA RPC", "Got WebSocket notification");
  164. let params = n.params["result"]["value"].clone();
  165. if mint.is_some() {
  166. cur_balance = params["data"]["parsed"]["info"]["tokenAmount"]["amount"]
  167. .as_str()
  168. .unwrap()
  169. .parse()
  170. .map_err(|e| SolFailed::from(Error::from(e)))?;
  171. } else {
  172. cur_balance = params["lamports"].as_u64().unwrap();
  173. }
  174. break;
  175. }
  176. }
  177. }
  178. // I miss goto/defer.
  179. let index = self
  180. .subscriptions
  181. .lock()
  182. .await
  183. .iter()
  184. .position(|p| p == &pubkey);
  185. if let Some(ind) = index {
  186. debug!("Removing subscription from list");
  187. self.subscriptions.lock().await.remove(ind);
  188. }
  189. let unsubscription = jsonrpc::request(json!("accountUnsubscribe"), json!([sub_id]));
  190. write
  191. .send(Message::text(serde_json::to_string(&unsubscription)?))
  192. .await?;
  193. if cur_balance < prev_balance {
  194. error!("New balance is less than previous balance");
  195. return Err(SolFailed::Notification(
  196. "New balance is less than previous balance".into(),
  197. ));
  198. }
  199. if mint.is_some() {
  200. let amnt = cur_balance - prev_balance;
  201. let ui_amnt = amnt / u64::pow(10, decimals as u32);
  202. self.notify_channel
  203. .0
  204. .send(TokenNotification {
  205. network: NetworkName::Solana,
  206. token_id: generate_id(&mint.unwrap().to_string(), &NetworkName::Solana)?,
  207. drk_pub_key,
  208. received_balance: amnt,
  209. })
  210. .await
  211. .map_err(Error::from)?;
  212. debug!(target: "SOL BRIDGE", "Received {} {:?} tokens", ui_amnt, mint.unwrap());
  213. let _ = self.send_tok_to_main_wallet(&rpc, &mint.unwrap(), amnt, decimals, &keypair)?;
  214. } else {
  215. let amnt = cur_balance - prev_balance;
  216. let ui_amnt = lamports_to_sol(amnt);
  217. self.notify_channel
  218. .0
  219. .send(TokenNotification {
  220. network: NetworkName::Solana,
  221. token_id: generate_id(SOL_NATIVE_TOKEN_ID, &NetworkName::Solana)?,
  222. drk_pub_key,
  223. received_balance: amnt,
  224. })
  225. .await
  226. .map_err(Error::from)?;
  227. debug!(target: "SOL BRIDGE", "Received {} SOL", ui_amnt);
  228. let _ = self.send_sol_to_main_wallet(&rpc, amnt, &keypair)?;
  229. }
  230. Ok(())
  231. }
  232. fn send_tok_to_main_wallet(
  233. self: Arc<Self>,
  234. rpc: &RpcClient,
  235. mint: &Pubkey,
  236. amount: u64,
  237. decimals: u64,
  238. keypair: &Keypair,
  239. ) -> SolResult<Signature> {
  240. debug!(target: "SOL BRIDGE", "Sending {} {:?} tokens to main wallet",
  241. amount / u64::pow(10, decimals as u32), mint);
  242. // The token account from our main wallet
  243. let main_tok_pk = get_associated_token_address(&self.main_keypair.pubkey(), mint);
  244. // The token account from the deposit wallet
  245. let temp_tok_pk = get_associated_token_address(&keypair.pubkey(), mint);
  246. let mut instructions = vec![];
  247. match rpc.get_account_data(&main_tok_pk) {
  248. Ok(v) => {
  249. match spl_token::state::Account::unpack_from_slice(&v) {
  250. // It's valid token data, and we consider account initialized.
  251. Ok(_) => {}
  252. // Some other unexpected data.
  253. Err(_) => {
  254. return Err(SolFailed::SolClientError(
  255. "Invalid data on derived account".into(),
  256. ));
  257. }
  258. }
  259. }
  260. Err(_) => {
  261. // Unitinialized, so we add a creation instruction
  262. debug!("Main wallet token account is uninitialized. Adding init instruction.");
  263. let init_ix = create_associated_token_account(
  264. &self.main_keypair.pubkey(), // fee payer
  265. &self.main_keypair.pubkey(), // wallet
  266. mint,
  267. );
  268. instructions.push(init_ix);
  269. }
  270. }
  271. // Transfer tokens from the deposit wallet to the main wallet
  272. let transfer_ix = spl_token::instruction::transfer_checked(
  273. &spl_token::id(),
  274. &temp_tok_pk,
  275. mint,
  276. &main_tok_pk,
  277. &keypair.pubkey(),
  278. &[],
  279. amount,
  280. decimals as u8,
  281. )?;
  282. instructions.push(transfer_ix);
  283. // Close the account and reap the rent if there's no more tokens on it.
  284. let (tok_balance, _) = get_account_token_balance(&rpc, &temp_tok_pk, mint)?;
  285. if tok_balance - amount == 0 {
  286. debug!(target: "SOL BRIDGE", "Adding account close instruction because resulting balance is 0");
  287. let close_ix = spl_token::instruction::close_account(
  288. &spl_token::id(),
  289. &temp_tok_pk,
  290. &self.main_keypair.pubkey(),
  291. &keypair.pubkey(),
  292. &[],
  293. )?;
  294. instructions.push(close_ix);
  295. }
  296. let tx = Transaction::new_with_payer(&instructions, Some(&self.main_keypair.pubkey()));
  297. let signature = sign_and_send_transaction(&rpc, tx, vec![&self.main_keypair, keypair])?;
  298. debug!(target: "SOL BRIDGE", "Sent tokens to main wallet: {}", signature);
  299. Ok(signature)
  300. }
  301. fn send_sol_to_main_wallet(
  302. self: Arc<Self>,
  303. rpc: &RpcClient,
  304. amount: u64,
  305. keypair: &Keypair,
  306. ) -> SolResult<Signature> {
  307. debug!(target: "SOL BRIDGE", "Sending {} SOL to main wallet", lamports_to_sol(amount));
  308. let ix =
  309. system_instruction::transfer(&keypair.pubkey(), &self.main_keypair.pubkey(), amount);
  310. let tx = Transaction::new_with_payer(&[ix], Some(&self.main_keypair.pubkey()));
  311. let signature = sign_and_send_transaction(&rpc, tx, vec![&self.main_keypair, keypair])?;
  312. debug!(target: "SOL BRIDGE", "Sent {} SOL to main wallet: {}", lamports_to_sol(amount), signature);
  313. Ok(signature)
  314. }
  315. fn check_mint_address(&self, mint_address: Option<String>) -> SolResult<Option<Pubkey>> {
  316. if let Some(mint_addr) = mint_address {
  317. let pubkey = match Pubkey::from_str(&mint_addr) {
  318. Ok(v) => v,
  319. Err(e) => return Err(SolFailed::BadSolAddress(e.to_string())),
  320. };
  321. let rpc = RpcClient::new(self.rpc_server.to_string());
  322. if !account_is_initialized_mint(&rpc, &pubkey).0 {
  323. return Err(SolFailed::MintIsNotValid(mint_addr));
  324. }
  325. Ok(Some(pubkey))
  326. } else {
  327. Ok(None)
  328. }
  329. }
  330. }
  331. #[async_trait]
  332. impl NetworkClient for SolClient {
  333. async fn subscribe(
  334. self: Arc<Self>,
  335. drk_pub_key: jubjub::SubgroupPoint,
  336. mint_address: Option<String>,
  337. ) -> Result<TokenSubscribtion> {
  338. let keypair = Keypair::generate(&mut OsRng);
  339. let public_key = keypair.pubkey().to_string();
  340. let secret_key = serialize(&keypair);
  341. let mint = self.check_mint_address(mint_address)?;
  342. smol::spawn(self.handle_subscribe_request(keypair, drk_pub_key, mint)).detach();
  343. Ok(TokenSubscribtion {
  344. secret_key,
  345. public_key,
  346. })
  347. }
  348. // in solana case private key it's the same as keypair
  349. async fn subscribe_with_keypair(
  350. self: Arc<Self>,
  351. private_key: Vec<u8>,
  352. _public_key: Vec<u8>,
  353. drk_pub_key: jubjub::SubgroupPoint,
  354. mint_address: Option<String>,
  355. ) -> Result<String> {
  356. let keypair: Keypair = deserialize(&private_key)?;
  357. let public_key = keypair.pubkey().to_string();
  358. let mint = self.check_mint_address(mint_address)?;
  359. smol::spawn(self.handle_subscribe_request(keypair, drk_pub_key, mint)).detach();
  360. Ok(public_key)
  361. }
  362. async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
  363. Ok(self.notify_channel.1.clone())
  364. }
  365. async fn send(self: Arc<Self>, address: Vec<u8>, amount: u64) -> Result<()> {
  366. let rpc = RpcClient::new(self.rpc_server.to_string());
  367. let address: Pubkey = deserialize(&address)?;
  368. let instruction =
  369. system_instruction::transfer(&self.main_keypair.pubkey(), &address, amount);
  370. let mut tx = Transaction::new_with_payer(&[instruction], Some(&self.main_keypair.pubkey()));
  371. let bhq = BlockhashQuery::default();
  372. match bhq.get_blockhash_and_fee_calculator(&rpc, rpc.commitment()) {
  373. Err(_) => panic!("Couldn't connect to RPC"),
  374. Ok(v) => tx.sign(&[&self.main_keypair], v.0),
  375. }
  376. let _signature = rpc
  377. .send_and_confirm_transaction(&tx)
  378. .map_err(SolFailed::from)?;
  379. Ok(())
  380. }
  381. }
  382. /// Gets account token balance for given mint.
  383. /// Returns: (amount, decimals)
  384. pub fn get_account_token_balance(
  385. rpc: &RpcClient,
  386. address: &Pubkey,
  387. mint: &Pubkey,
  388. ) -> SolResult<(u64, u64)> {
  389. let mint_account = rpc.get_account(mint)?;
  390. let token_account = rpc.get_account(address)?;
  391. let mint_data = spl_token::state::Mint::unpack_from_slice(&mint_account.data)?;
  392. let token_data = spl_token::state::Account::unpack_from_slice(&token_account.data)?;
  393. Ok((token_data.amount, mint_data.decimals as u64))
  394. }
  395. /// Check if given account is a valid token mint
  396. pub fn account_is_initialized_mint(rpc: &RpcClient, mint: &Pubkey) -> (bool, u64) {
  397. match rpc.get_token_supply(mint) {
  398. Ok(v) => (true, v.decimals as u64),
  399. Err(_) => (false, 0),
  400. }
  401. }
  402. pub fn sign_and_send_transaction(
  403. rpc: &RpcClient,
  404. mut tx: Transaction,
  405. signers: Vec<&Keypair>,
  406. ) -> SolResult<Signature> {
  407. let bhq = BlockhashQuery::default();
  408. match bhq.get_blockhash_and_fee_calculator(rpc, rpc.commitment()) {
  409. Err(_) => return Err(SolFailed::RpcError("Couldn't connect to RPC".into())),
  410. Ok(v) => tx.sign(&signers, v.0),
  411. }
  412. match rpc.send_and_confirm_transaction(&tx) {
  413. Ok(s) => Ok(s),
  414. Err(_) => Err(SolFailed::RpcError("Failed to send transaction".into())),
  415. }
  416. }
  417. impl Encodable for Keypair {
  418. fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
  419. let key: Vec<u8> = self.to_bytes().to_vec();
  420. let len = key.encode(s)?;
  421. Ok(len)
  422. }
  423. }
  424. impl Decodable for Keypair {
  425. fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
  426. let key: Vec<u8> = Decodable::decode(&mut d)?;
  427. let key = Keypair::from_bytes(key.as_slice()).map_err(|_| {
  428. crate::Error::from(SolFailed::DecodeAndEncodeError(
  429. "load keypair from slice".into(),
  430. ))
  431. })?;
  432. Ok(key)
  433. }
  434. }
  435. impl Encodable for Pubkey {
  436. fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
  437. let key = self.to_string();
  438. let len = key.encode(s)?;
  439. Ok(len)
  440. }
  441. }
  442. impl Decodable for Pubkey {
  443. fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
  444. let key: String = Decodable::decode(&mut d)?;
  445. let key = Pubkey::try_from(key.as_str()).map_err(|_| {
  446. crate::Error::from(SolFailed::DecodeAndEncodeError(
  447. "load public key from slice".into(),
  448. ))
  449. })?;
  450. Ok(key)
  451. }
  452. }
  453. #[derive(Debug)]
  454. pub enum SolFailed {
  455. NotEnoughValue(u64),
  456. BadSolAddress(String),
  457. DecodeAndEncodeError(String),
  458. WebSocketError(String),
  459. RpcError(String),
  460. SolClientError(String),
  461. Notification(String),
  462. ProgramError(String),
  463. MintIsNotValid(String),
  464. JsonError(String),
  465. ParseError(String),
  466. SolError(String),
  467. }
  468. impl std::error::Error for SolFailed {}
  469. impl std::fmt::Display for SolFailed {
  470. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  471. match self {
  472. SolFailed::NotEnoughValue(i) => {
  473. write!(f, "There is no enough value {}", i)
  474. }
  475. SolFailed::BadSolAddress(ref err) => {
  476. write!(f, "Bad Sol Address: {}", err)
  477. }
  478. SolFailed::DecodeAndEncodeError(ref err) => {
  479. write!(f, "Decode and decode keys error: {}", err)
  480. }
  481. SolFailed::WebSocketError(i) => {
  482. write!(f, "WebSocket Error: {}", i)
  483. }
  484. SolFailed::RpcError(i) => {
  485. write!(f, "Rpc Error: {}", i)
  486. }
  487. SolFailed::ParseError(i) => {
  488. write!(f, "Parse Error: {}", i)
  489. }
  490. SolFailed::SolClientError(i) => {
  491. write!(f, "Solana Client Error: {}", i)
  492. }
  493. SolFailed::Notification(i) => {
  494. write!(f, "Received Notification Error: {}", i)
  495. }
  496. SolFailed::ProgramError(i) => {
  497. write!(f, "ProgramError Error: {}", i)
  498. }
  499. SolFailed::MintIsNotValid(i) => {
  500. write!(f, "Given mint is not valid: {}", i)
  501. }
  502. SolFailed::JsonError(i) => {
  503. write!(f, "JsonError: {}", i)
  504. }
  505. SolFailed::SolError(i) => {
  506. write!(f, "SolFailed: {}", i)
  507. }
  508. }
  509. }
  510. }
  511. impl From<solana_sdk::pubkey::ParsePubkeyError> for SolFailed {
  512. fn from(err: solana_sdk::pubkey::ParsePubkeyError) -> SolFailed {
  513. SolFailed::ParseError(err.to_string())
  514. }
  515. }
  516. impl From<tungstenite::Error> for SolFailed {
  517. fn from(err: tungstenite::Error) -> SolFailed {
  518. SolFailed::WebSocketError(err.to_string())
  519. }
  520. }
  521. impl From<solana_client::client_error::ClientError> for SolFailed {
  522. fn from(err: solana_client::client_error::ClientError) -> SolFailed {
  523. SolFailed::SolError(err.to_string())
  524. }
  525. }
  526. impl From<solana_sdk::program_error::ProgramError> for SolFailed {
  527. fn from(err: solana_sdk::program_error::ProgramError) -> SolFailed {
  528. SolFailed::ProgramError(err.to_string())
  529. }
  530. }
  531. impl From<crate::error::Error> for SolFailed {
  532. fn from(err: crate::error::Error) -> SolFailed {
  533. SolFailed::SolError(err.to_string())
  534. }
  535. }
  536. impl From<serde_json::Error> for SolFailed {
  537. fn from(err: serde_json::Error) -> SolFailed {
  538. SolFailed::JsonError(err.to_string())
  539. }
  540. }
  541. pub type SolResult<T> = std::result::Result<T, SolFailed>;