eth.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. use async_std::sync::{Arc, Mutex};
  2. use std::convert::TryInto;
  3. use std::time::Duration;
  4. use async_executor::Executor;
  5. use async_trait::async_trait;
  6. use hash_db::Hasher;
  7. use keccak_hasher::KeccakHasher;
  8. use lazy_static::lazy_static;
  9. use log::{debug, error};
  10. use num_bigint::{BigUint, RandBigInt};
  11. use serde::{Deserialize, Serialize};
  12. use serde_json::{json, Value};
  13. use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
  14. use crate::{
  15. rpc::jsonrpc,
  16. rpc::jsonrpc::JsonResult,
  17. serial::{deserialize, serialize},
  18. util::{generate_id, NetworkName},
  19. Error, Result,
  20. };
  21. pub const ETH_NATIVE_TOKEN_ID: &str = "0x0000000000000000000000000000000000000000";
  22. // An ERC-20 token transfer transaction's data is as follows:
  23. //
  24. // 1. The first 4 bytes of the keccak256 hash of "transfer(address,uint256)".
  25. // 2. The address of the recipient, left-zero-padded to be 32 bytes.
  26. // 3. The amount to be transferred: amount * 10^decimals
  27. // This is the entire ERC20 ABI
  28. lazy_static! {
  29. static ref ERC20_NAME_METHOD: [u8; 4] = {
  30. let method = b"name()";
  31. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  32. };
  33. static ref ERC20_APPROVE_METHOD: [u8; 4] = {
  34. let method = b"approve(address,uint256)";
  35. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  36. };
  37. static ref ERC20_TOTALSUPPLY_METHOD: [u8; 4] = {
  38. let method = b"totalSupply()";
  39. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  40. };
  41. static ref ERC20_TRANSFERFROM_METHOD: [u8; 4] = {
  42. let method = b"transferFrom(address,address,uint256)";
  43. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  44. };
  45. static ref ERC20_DECIMALS_METHOD: [u8; 4] = {
  46. let method = b"decimals()";
  47. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  48. };
  49. static ref ERC20_VERSION_METHOD: [u8; 4] = {
  50. let method = b"version()";
  51. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  52. };
  53. static ref ERC20_BALANCEOF_METHOD: [u8; 4] = {
  54. let method = b"balanceOf(address)";
  55. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  56. };
  57. static ref ERC20_SYMBOL_METHOD: [u8; 4] = {
  58. let method = b"symbol()";
  59. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  60. };
  61. static ref ERC20_TRANSFER_METHOD: [u8; 4] = {
  62. let method = b"transfer(address,uint256)";
  63. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  64. };
  65. static ref ERC20_APPROVEANDCALL_METHOD: [u8; 4] = {
  66. let method = b"approveAndCall(address,uint256,bytes)";
  67. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  68. };
  69. static ref ERC20_ALLOWANCE_METHOD: [u8; 4] = {
  70. let method = b"allowance(address,address)";
  71. KeccakHasher::hash(method)[0..4].try_into().expect("nope")
  72. };
  73. }
  74. pub fn erc20_transfer_data(recipient: &str, amount: BigUint) -> String {
  75. let rec = recipient.trim_start_matches("0x");
  76. let rec_padded = format!("{:0>64}", rec);
  77. let amnt_bytes = amount.to_bytes_be();
  78. let amnt_hex = hex::encode(amnt_bytes);
  79. let amnt_hex_padded = format!("{:0>64}", amnt_hex);
  80. format!(
  81. "0x{}{}{}",
  82. hex::encode(*ERC20_TRANSFER_METHOD),
  83. rec_padded,
  84. amnt_hex_padded
  85. )
  86. }
  87. pub fn erc20_balanceof_data(account: &str) -> String {
  88. let acc = account.trim_start_matches("0x");
  89. let acc_padded = format!("{:0>64}", acc);
  90. format!("0x{}{}", hex::encode(*ERC20_BALANCEOF_METHOD), acc_padded)
  91. }
  92. fn to_eth_hex(val: BigUint) -> String {
  93. let bytes = val.to_bytes_be();
  94. let h = hex::encode(bytes);
  95. format!("0x{}", h.trim_start_matches('0'))
  96. }
  97. /// Generate a 256-bit ETH private key.
  98. pub fn generate_privkey() -> String {
  99. let mut rng = rand::thread_rng();
  100. let token = rng.gen_bigint(256);
  101. let token_bytes = token.to_bytes_le().1;
  102. let key = KeccakHasher::hash(&token_bytes);
  103. hex::encode(key)
  104. }
  105. #[allow(non_snake_case)]
  106. #[derive(Serialize, Deserialize, Debug, Clone)]
  107. pub struct EthTx {
  108. pub from: String,
  109. pub to: String,
  110. #[serde(skip_serializing_if = "Option::is_none")]
  111. pub gas: Option<String>,
  112. #[serde(skip_serializing_if = "Option::is_none")]
  113. pub gasPrice: Option<String>,
  114. #[serde(skip_serializing_if = "Option::is_none")]
  115. pub value: Option<String>,
  116. #[serde(skip_serializing_if = "Option::is_none")]
  117. pub data: Option<String>,
  118. #[serde(skip_serializing_if = "Option::is_none")]
  119. pub nonce: Option<String>,
  120. }
  121. impl EthTx {
  122. pub fn new(
  123. from: &str,
  124. to: &str,
  125. gas: Option<BigUint>,
  126. gas_price: Option<BigUint>,
  127. value: Option<BigUint>,
  128. data: Option<String>,
  129. nonce: Option<String>,
  130. ) -> Self {
  131. let gas_hex = gas.map(to_eth_hex);
  132. let gasprice_hex = gas_price.map(to_eth_hex);
  133. let value_hex = value.map(to_eth_hex);
  134. EthTx {
  135. from: from.to_string(),
  136. to: to.to_string(),
  137. gas: gas_hex,
  138. gasPrice: gasprice_hex,
  139. value: value_hex,
  140. data,
  141. nonce,
  142. }
  143. }
  144. }
  145. // JSON-RPC interface to Geth.
  146. // https://eth.wiki/json-rpc/API
  147. // https://geth.ethereum.org/docs/rpc/
  148. //
  149. // geth can be started with: $ geth --ropsten --syncmode light
  150. // It should then show an Unix socket endpoint like so:
  151. // INFO [10-25|19:47:32.845] IPC endpoint opened: url=/home/x/.ethereum/ropsten/geth.ipc
  152. //
  153. pub struct EthClient {
  154. socket_path: String,
  155. subscriptions: Arc<Mutex<Vec<String>>>,
  156. notify_channel: (
  157. async_channel::Sender<TokenNotification>,
  158. async_channel::Receiver<TokenNotification>,
  159. ),
  160. }
  161. impl EthClient {
  162. pub fn new(socket_path: String) -> Arc<Self> {
  163. let notify_channel = async_channel::unbounded();
  164. let subscriptions = Arc::new(Mutex::new(Vec::new()));
  165. Arc::new(Self {
  166. socket_path,
  167. subscriptions,
  168. notify_channel,
  169. })
  170. }
  171. async fn handle_subscribe_request(
  172. self: Arc<Self>,
  173. private: String,
  174. addr: String,
  175. drk_pub_key: jubjub::SubgroupPoint,
  176. ) -> Result<()> {
  177. if self.subscriptions.lock().await.contains(&addr) {
  178. return Ok(());
  179. }
  180. let decimals = 18;
  181. let prev_balance = self.get_current_balance(&addr, None).await?;
  182. let mut current_balance;
  183. let iter_interval = 1;
  184. let mut sub_iter = 0;
  185. loop {
  186. if sub_iter > 60 * 10 {
  187. // 10 minutes
  188. self.unsubscribe(&addr).await;
  189. return Err(crate::Error::ClientFailed("Deposit for expired".into()));
  190. }
  191. sub_iter += iter_interval;
  192. async_std::task::sleep(Duration::from_secs(iter_interval)).await;
  193. current_balance = self.get_current_balance(&addr, None).await?;
  194. if current_balance != prev_balance {
  195. break;
  196. }
  197. }
  198. let send_notification = self.notify_channel.0.clone();
  199. self.unsubscribe(&addr).await;
  200. if current_balance < prev_balance {
  201. return Err(crate::Error::ClientFailed(
  202. "New balance is less than previous balance".into(),
  203. ));
  204. }
  205. let amnt = current_balance - prev_balance;
  206. send_notification
  207. .send(TokenNotification {
  208. network: NetworkName::Solana,
  209. token_id: generate_id(ETH_NATIVE_TOKEN_ID, &NetworkName::Solana)?,
  210. drk_pub_key,
  211. // TODO FIX
  212. received_balance: amnt.to_u64_digits()[0],
  213. decimals: decimals as u16,
  214. })
  215. .await
  216. .map_err(Error::from)?;
  217. Ok(())
  218. }
  219. async fn unsubscribe(self: Arc<Self>, pubkey: &String) {
  220. let mut subscriptions = self.subscriptions.lock().await;
  221. let index = subscriptions.iter().position(|p| p == pubkey);
  222. if let Some(ind) = index {
  223. debug!(target: "ETH BRIDGE", "Removing subscription from list");
  224. subscriptions.remove(ind);
  225. }
  226. }
  227. async fn request(&self, r: jsonrpc::JsonRequest) -> Result<Value> {
  228. debug!(target: "ETH RPC", "--> {}", serde_json::to_string(&r)?);
  229. let reply: JsonResult = match jsonrpc::send_unix_request(&self.socket_path, json!(r)).await
  230. {
  231. Ok(v) => v,
  232. Err(e) => return Err(e),
  233. };
  234. match reply {
  235. JsonResult::Resp(r) => {
  236. debug!(target: "ETH RPC", "<-- {}", serde_json::to_string(&r)?);
  237. Ok(r.result)
  238. }
  239. JsonResult::Err(e) => {
  240. debug!(target: "ETH RPC", "<-- {}", serde_json::to_string(&e)?);
  241. Err(Error::JsonRpcError(e.error.message.to_string()))
  242. }
  243. JsonResult::Notif(n) => {
  244. debug!(target: "ETH RPC", "<-- {}", serde_json::to_string(&n)?);
  245. Err(Error::JsonRpcError("Unexpected reply".to_string()))
  246. }
  247. }
  248. }
  249. pub async fn import_privkey(&self, key: &str, passphrase: &str) -> Result<Value> {
  250. let req = jsonrpc::request(json!("personal_importRawKey"), json!([key, passphrase]));
  251. Ok(self.request(req).await?)
  252. }
  253. /*
  254. pub async fn estimate_gas(&self, tx: &EthTx) -> Result<Value> {
  255. let req = jsonrpc::request(json!("eth_estimateGas"), json!([tx]));
  256. Ok(self.request(req).await?)
  257. }
  258. */
  259. pub async fn block_number(&self) -> Result<Value> {
  260. let req = jsonrpc::request(json!("eth_blockNumber"), json!([]));
  261. Ok(self.request(req).await?)
  262. }
  263. pub async fn get_eth_balance(&self, acc: &str, block: &str) -> Result<Value> {
  264. let req = jsonrpc::request(json!("eth_getBalance"), json!([acc, block]));
  265. Ok(self.request(req).await?)
  266. }
  267. pub async fn get_erc20_balance(&self, acc: &str, mint: &str) -> Result<Value> {
  268. let tx = EthTx::new(
  269. acc,
  270. mint,
  271. None,
  272. None,
  273. None,
  274. Some(erc20_balanceof_data(acc)),
  275. None,
  276. );
  277. let req = jsonrpc::request(json!("eth_call"), json!([tx, "latest"]));
  278. Ok(self.request(req).await?)
  279. }
  280. pub async fn get_current_balance(&self, acc: &str, _mint: Option<&str>) -> Result<BigUint> {
  281. // Latest known block, used to calculate present balance.
  282. let block = self.block_number().await?;
  283. let block = block.as_str().unwrap();
  284. // Native ETH balance
  285. let hexbalance = self.get_eth_balance(&acc, block).await?;
  286. let hexbalance = hexbalance.as_str().unwrap().trim_start_matches("0x");
  287. let balance = BigUint::parse_bytes(hexbalance.as_bytes(), 16).unwrap();
  288. Ok(balance)
  289. }
  290. pub async fn send_transaction(&self, tx: &EthTx, passphrase: &str) -> Result<Value> {
  291. let req = jsonrpc::request(json!("personal_sendTransaction"), json!([tx, passphrase]));
  292. Ok(self.request(req).await?)
  293. }
  294. }
  295. #[async_trait]
  296. impl NetworkClient for EthClient {
  297. async fn subscribe(
  298. self: Arc<Self>,
  299. drk_pub_key: jubjub::SubgroupPoint,
  300. _mint_address: Option<String>,
  301. executor: Arc<Executor<'_>>,
  302. ) -> Result<TokenSubscribtion> {
  303. let private_key = generate_privkey();
  304. // TODO fix
  305. let addr: String = self
  306. .import_privkey(&private_key, "testpass")
  307. .await?
  308. .as_str()
  309. .unwrap()
  310. .to_string();
  311. let private = private_key.clone();
  312. let addr_cloned = addr.clone();
  313. executor
  314. .spawn(async move {
  315. let result = self
  316. .handle_subscribe_request(private, addr_cloned, drk_pub_key)
  317. .await;
  318. if let Err(e) = result {
  319. error!(target: "SOL BRIDGE SUBSCRIPTION","{}", e.to_string());
  320. }
  321. })
  322. .detach();
  323. let private_key: Vec<u8> = serialize(&private_key);
  324. Ok(TokenSubscribtion {
  325. private_key,
  326. public_key: addr,
  327. })
  328. }
  329. async fn subscribe_with_keypair(
  330. self: Arc<Self>,
  331. _private_key: Vec<u8>,
  332. public_key: Vec<u8>,
  333. _drk_pub_key: jubjub::SubgroupPoint,
  334. _mint_address: Option<String>,
  335. _executor: Arc<Executor<'_>>,
  336. ) -> Result<String> {
  337. let public_key: String = deserialize(&public_key)?;
  338. Ok(public_key)
  339. }
  340. async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
  341. Ok(self.notify_channel.1.clone())
  342. }
  343. async fn send(
  344. self: Arc<Self>,
  345. _address: Vec<u8>,
  346. _mint: Option<String>,
  347. _amount: u64,
  348. ) -> Result<()> {
  349. Ok(())
  350. }
  351. }
  352. #[allow(unused_imports)]
  353. mod tests {
  354. use super::*;
  355. use num_bigint::ToBigUint;
  356. use std::str::FromStr;
  357. #[test]
  358. fn test_erc20_transfer_data() {
  359. let recipient = "0x5b7b3b499fb69c40c365343cb0dc842fe8c23887";
  360. let amnt = BigUint::from_str("34765403556934000640").unwrap();
  361. assert_eq!(erc20_transfer_data(recipient, amnt), "0xa9059cbb0000000000000000000000005b7b3b499fb69c40c365343cb0dc842fe8c23887000000000000000000000000000000000000000000000001e27786570c272000");
  362. }
  363. }