darkfid.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. use drk::{
  2. blockchain::Rocks,
  3. cli::{Config, DarkfidConfig},
  4. client::Client,
  5. rpc::{
  6. jsonrpc::{error as jsonerr, request as jsonreq, response as jsonresp, send_request},
  7. jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
  8. rpcserver::{listen_and_serve, RequestHandler, RpcServerConfig},
  9. },
  10. serial::{deserialize, serialize},
  11. util::{
  12. assign_id, decimals, decode_base10, encode_base10, expand_path, join_config_path,
  13. DrkTokenList, NetworkName, SolTokenList,
  14. },
  15. wallet::WalletDb,
  16. Error, Result,
  17. };
  18. use async_trait::async_trait;
  19. use clap::clap_app;
  20. use log::debug;
  21. use serde_json::{json, Value};
  22. use async_std::sync::{Arc, Mutex};
  23. use std::collections::HashMap;
  24. use std::path::PathBuf;
  25. use std::str::FromStr;
  26. #[derive(Clone, Debug)]
  27. pub struct Cashiers {
  28. pub cashier_name: String,
  29. pub cashier_rpc_url: String,
  30. pub cashier_public_key: jubjub::SubgroupPoint,
  31. }
  32. struct Darkfid {
  33. config: DarkfidConfig,
  34. client: Arc<Mutex<Client>>,
  35. sol_tokenlist: SolTokenList,
  36. drk_tokenlist: DrkTokenList,
  37. cashiers: Vec<Cashiers>,
  38. }
  39. #[async_trait]
  40. impl RequestHandler for Darkfid {
  41. async fn handle_request(&self, req: JsonRequest) -> JsonResult {
  42. if req.params.as_array().is_none() {
  43. return JsonResult::Err(jsonerr(InvalidParams, None, req.id));
  44. }
  45. debug!(target: "RPC", "--> {}", serde_json::to_string(&req).unwrap());
  46. match req.method.as_str() {
  47. Some("say_hello") => return self.say_hello(req.id, req.params).await,
  48. Some("create_wallet") => return self.create_wallet(req.id, req.params).await,
  49. Some("key_gen") => return self.key_gen(req.id, req.params).await,
  50. Some("get_key") => return self.get_key(req.id, req.params).await,
  51. Some("get_balances") => return self.get_balances(req.id, req.params).await,
  52. Some("get_token_id") => return self.get_token_id(req.id, req.params).await,
  53. Some("features") => return self.features(req.id, req.params).await,
  54. Some("deposit") => return self.deposit(req.id, req.params).await,
  55. Some("withdraw") => return self.withdraw(req.id, req.params).await,
  56. Some("transfer") => return self.transfer(req.id, req.params).await,
  57. Some(_) | None => return JsonResult::Err(jsonerr(MethodNotFound, None, req.id)),
  58. };
  59. }
  60. }
  61. impl Darkfid {
  62. async fn new(config: DarkfidConfig, wallet: Arc<WalletDb>) -> Result<Self> {
  63. debug!(target: "DARKFID", "INIT WALLET WITH PATH {}", config.wallet_path);
  64. let mut cashiers = Vec::new();
  65. let mut cashier_public_keys = Vec::new();
  66. for cashier in config.clone().cashiers {
  67. let cashier_public: jubjub::SubgroupPoint =
  68. deserialize(&bs58::decode(cashier.cashier_public_key).into_vec()?)?;
  69. cashiers.push(Cashiers {
  70. cashier_name: cashier.cashier_name,
  71. cashier_rpc_url: cashier.cashier_rpc_url,
  72. cashier_public_key: cashier_public,
  73. });
  74. cashier_public_keys.push(cashier_public);
  75. }
  76. let rocks = Rocks::new(expand_path(&config.database_path.clone())?.as_path())?;
  77. let client = Client::new(
  78. rocks,
  79. (
  80. config.gateway_protocol_url.parse()?,
  81. config.gateway_publisher_url.parse()?,
  82. ),
  83. (
  84. expand_path(&config.mint_params_path.clone())?,
  85. expand_path(&config.spend_params_path.clone())?,
  86. ),
  87. wallet.clone(),
  88. cashier_public_keys,
  89. )
  90. .await?;
  91. let client = Arc::new(Mutex::new(client));
  92. let sol_tokenlist = SolTokenList::new()?;
  93. let drk_tokenlist = DrkTokenList::new(sol_tokenlist.clone())?;
  94. Ok(Self {
  95. config,
  96. client,
  97. sol_tokenlist,
  98. drk_tokenlist,
  99. cashiers,
  100. })
  101. }
  102. async fn start(&mut self) -> Result<()> {
  103. self.client.lock().await.start().await?;
  104. self.client.lock().await.connect_to_subscriber().await?;
  105. Ok(())
  106. }
  107. // --> {"method": "say_hello", "params": []}
  108. // <-- {"result": "hello world"}
  109. async fn say_hello(&self, id: Value, _params: Value) -> JsonResult {
  110. JsonResult::Resp(jsonresp(json!("hello world"), id))
  111. }
  112. // --> {"method": "create_wallet", "params": []}
  113. // <-- {"result": true}
  114. async fn create_wallet(&self, id: Value, _params: Value) -> JsonResult {
  115. match self.client.lock().await.init_db().await {
  116. Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
  117. Err(e) => {
  118. return JsonResult::Err(jsonerr(ServerError(-32001), Some(e.to_string()), id))
  119. }
  120. }
  121. }
  122. // --> {"method": "key_gen", "params": []}
  123. // <-- {"result": true}
  124. async fn key_gen(&self, id: Value, _params: Value) -> JsonResult {
  125. match self.client.lock().await.key_gen().await {
  126. Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
  127. Err(e) => {
  128. return JsonResult::Err(jsonerr(ServerError(-32002), Some(e.to_string()), id))
  129. }
  130. }
  131. }
  132. // --> {"method": "get_key", "params": []}
  133. // <-- {"result": "vdNS7oBj7KvsMWWmo9r96SV4SqATLrGsH2a3PGpCfJC"}
  134. async fn get_key(&self, id: Value, _params: Value) -> JsonResult {
  135. let pk = self.client.lock().await.main_keypair.public;
  136. let b58 = bs58::encode(serialize(&pk)).into_string();
  137. return JsonResult::Resp(jsonresp(json!(b58), id));
  138. }
  139. // --> {"method": "get_balances", "params": []}
  140. // <-- {"result": "get_balances": "[token: btc, value: 0]"}
  141. async fn get_balances(&self, id: Value, _params: Value) -> JsonResult {
  142. let result: Result<HashMap<String, Vec<String>>> = async {
  143. let balances = self.client.lock().await.get_balances().await?;
  144. let mut symbols: Vec<String> = Vec::new();
  145. let mut data: Vec<Vec<String>> = Vec::new();
  146. for id in balances.keys() {
  147. let id: jubjub::Fr = deserialize(&id)?;
  148. // this is hardcoded for SOL
  149. // TODO: if id == btc_id:
  150. // network = bitcoin
  151. // else
  152. // network = solana
  153. let network = "solana";
  154. let mut data_vec: Vec<String> = Vec::new();
  155. if let Some(symbol) = self.drk_tokenlist.clone().symbol_from_id(id)? {
  156. let decimals = decimals(network, &symbol, &self.sol_tokenlist)?;
  157. for amount in balances.values() {
  158. let amount = encode_base10(amount.clone(), decimals);
  159. data_vec.push(amount);
  160. }
  161. data_vec.push(network.to_string());
  162. data.push(data_vec);
  163. symbols.push(symbol);
  164. }
  165. }
  166. let new_balances: HashMap<String, Vec<String>> = symbols
  167. .into_iter()
  168. .zip(data.into_iter())
  169. .map(|(key, value)| return (key.clone(), value.clone()))
  170. .collect::<HashMap<String, Vec<String>>>();
  171. Ok(new_balances)
  172. }
  173. .await;
  174. match result {
  175. Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(res))),
  176. Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
  177. }
  178. }
  179. // --> {"method": "get_token_id", "params": [network, token]}
  180. // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
  181. async fn get_token_id(&self, id: Value, params: Value) -> JsonResult {
  182. let args = params.as_array();
  183. if args.is_none() {
  184. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  185. }
  186. let args = args.unwrap();
  187. let network = args[0].as_str();
  188. let symbol = args[1].as_str();
  189. if network.is_none() {
  190. return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id));
  191. }
  192. if symbol.is_none() {
  193. return JsonResult::Err(jsonerr(InvalidSymbolParam, None, id));
  194. }
  195. let symbol = symbol.unwrap();
  196. let result: Result<Value> = async {
  197. let network = NetworkName::from_str(&network.unwrap())?;
  198. match network {
  199. #[cfg(feature = "sol")]
  200. NetworkName::Solana => {
  201. let token_id = self.sol_tokenlist.search_id(symbol)?;
  202. Ok(json!(token_id))
  203. }
  204. #[cfg(feature = "btc")]
  205. NetworkName::Bitcoin => {
  206. return Err(Error::NotSupportedToken);
  207. }
  208. _ => Err(Error::NotSupportedNetwork),
  209. }
  210. }
  211. .await;
  212. match result {
  213. Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(res))),
  214. Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
  215. }
  216. }
  217. // --> {""method": "features", "params": []}
  218. // <-- {"result": { "network": ["btc", "sol"] } }
  219. async fn features(&self, id: Value, _params: Value) -> JsonResult {
  220. let req = jsonreq(json!("features"), json!([]));
  221. let rep: JsonResult;
  222. // TODO which cashier to send the request to?
  223. match send_request(&self.cashiers[0].cashier_rpc_url, json!(req)).await {
  224. Ok(v) => rep = v,
  225. Err(e) => {
  226. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
  227. }
  228. }
  229. match rep {
  230. JsonResult::Resp(r) => return JsonResult::Resp(r),
  231. JsonResult::Err(e) => return JsonResult::Err(e),
  232. JsonResult::Notif(_) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  233. }
  234. }
  235. // --> {"method": "deposit", "params": [network, token, publickey]}
  236. // The publickey sent here is used so the cashier can know where to send
  237. // tokens once the deposit is received.
  238. // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
  239. async fn deposit(&self, id: Value, params: Value) -> JsonResult {
  240. let args = params.as_array();
  241. if args.is_none() {
  242. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  243. }
  244. let args = args.unwrap();
  245. if args.len() != 2 {
  246. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  247. }
  248. let network = &args[0];
  249. let token = &args[1];
  250. if token.as_str().is_none() {
  251. return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id));
  252. }
  253. let token = token.as_str().unwrap();
  254. if network.as_str().is_none() {
  255. return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id));
  256. }
  257. let network = network.as_str().unwrap();
  258. let token_id = match assign_id(&network, &token, &self.sol_tokenlist) {
  259. Ok(t) => t,
  260. Err(e) => {
  261. return JsonResult::Err(jsonerr(InternalError, Some(e.to_string()), id));
  262. }
  263. };
  264. // TODO: Optional sanity checking here, but cashier *must* do so too.
  265. let pk = self.client.lock().await.main_keypair.public;
  266. let pubkey = bs58::encode(serialize(&pk)).into_string();
  267. // Send request to cashier. If the cashier supports the requested network
  268. // (and token), it shall return a valid address where tokens can be deposited.
  269. // If not, an error is returned, and forwarded to the method caller.
  270. let req = jsonreq(json!("deposit"), json!([network, token_id, pubkey]));
  271. let rep: JsonResult;
  272. match send_request(&self.cashiers[0].cashier_rpc_url, json!(req)).await {
  273. Ok(v) => rep = v,
  274. Err(e) => {
  275. debug!(target: "DARKFID", "REQUEST IS ERR");
  276. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id));
  277. }
  278. }
  279. match rep {
  280. JsonResult::Resp(r) => return JsonResult::Resp(r),
  281. JsonResult::Err(e) => return JsonResult::Err(e),
  282. JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  283. }
  284. }
  285. // --> {"method": "withdraw", "params": [network, token, publickey, amount]}
  286. // The publickey sent here is the address where the caller wants to receive
  287. // the tokens they plan to withdraw.
  288. // On request, send request to cashier to get deposit address, and then transfer
  289. // dark tokens to the cashier's wallet. Following that, the cashier should return
  290. // a transaction ID of them sending the funds that are requested for withdrawal.
  291. // <-- {"result": "txID"}
  292. async fn withdraw(&self, id: Value, params: Value) -> JsonResult {
  293. let args = params.as_array();
  294. if args.is_none() {
  295. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  296. }
  297. let args = args.unwrap();
  298. if args.len() != 4 {
  299. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  300. }
  301. let network = &args[0];
  302. let token = &args[1];
  303. let address = &args[2];
  304. let amount = &args[3];
  305. if token.as_str().is_none() {
  306. return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id));
  307. }
  308. let token = token.as_str().unwrap();
  309. if network.as_str().is_none() {
  310. return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id));
  311. }
  312. let network = network.as_str().unwrap();
  313. if amount.as_str().is_none() {
  314. return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id));
  315. }
  316. let amount = amount.as_str().unwrap();
  317. let decimals = match decimals(network, token, &self.sol_tokenlist) {
  318. Ok(d) => d,
  319. Err(e) => {
  320. return JsonResult::Err(jsonerr(InternalError, Some(e.to_string()), id));
  321. }
  322. };
  323. let amount_in_apo = match decode_base10(&amount, decimals, true) {
  324. Ok(a) => a,
  325. Err(e) => {
  326. return JsonResult::Err(jsonerr(InternalError, Some(e.to_string()), id));
  327. }
  328. };
  329. let token_id = match assign_id(&network, &token, &self.sol_tokenlist) {
  330. Ok(t) => t,
  331. Err(e) => {
  332. return JsonResult::Err(jsonerr(InternalError, Some(e.to_string()), id));
  333. }
  334. };
  335. let req = jsonreq(
  336. json!("withdraw"),
  337. json!([network, token_id, address, amount_in_apo]),
  338. );
  339. let mut rep: JsonResult;
  340. match send_request(&self.cashiers[0].cashier_rpc_url, json!(req)).await {
  341. Ok(v) => rep = v,
  342. Err(e) => {
  343. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id));
  344. }
  345. }
  346. let token_id: &jubjub::Fr;
  347. // get the id for the token
  348. if let Some(tk_id) = self.drk_tokenlist.tokens.get(&token.to_uppercase()) {
  349. token_id = tk_id;
  350. } else {
  351. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  352. }
  353. // send drk to cashier_public
  354. if let JsonResult::Resp(cashier_public) = &rep {
  355. let result: Result<()> = async {
  356. let cashier_public = cashier_public.result.as_str().unwrap();
  357. let cashier_public: jubjub::SubgroupPoint =
  358. deserialize(&bs58::decode(cashier_public).into_vec()?)?;
  359. let decimals: usize = 8;
  360. let amount = decode_base10(&amount.to_string(), decimals, true)?;
  361. self.client
  362. .lock()
  363. .await
  364. .transfer(token_id.clone(), cashier_public, amount)
  365. .await?;
  366. Ok(())
  367. }
  368. .await;
  369. match result {
  370. Err(e) => {
  371. rep = JsonResult::Err(jsonerr(InternalError, Some(e.to_string()), id.clone()))
  372. }
  373. Ok(_) => {
  374. rep = JsonResult::Resp(jsonresp(
  375. json!(format!(
  376. "Sent request to withdraw {} amount of {}",
  377. amount, token_id
  378. )),
  379. json!(id.clone()),
  380. ))
  381. }
  382. }
  383. };
  384. match rep {
  385. JsonResult::Resp(r) => return JsonResult::Resp(r),
  386. JsonResult::Err(e) => return JsonResult::Err(e),
  387. JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  388. }
  389. }
  390. // --> {"method": "transfer", [dToken, address, amount]}
  391. // <-- {"result": "txID"}
  392. async fn transfer(&self, id: Value, params: Value) -> JsonResult {
  393. let args = params.as_array();
  394. if args.is_none() {
  395. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  396. }
  397. let args = args.unwrap();
  398. if args.len() != 3 {
  399. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  400. }
  401. let token = &args[0].as_str();
  402. let address = &args[1].as_str();
  403. let amount = &args[2].as_str();
  404. if token.is_none() {
  405. return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id));
  406. }
  407. let token = token.unwrap();
  408. if address.is_none() {
  409. return JsonResult::Err(jsonerr(InvalidAddressParam, None, id));
  410. }
  411. let address = address.unwrap();
  412. if amount.is_none() {
  413. return JsonResult::Err(jsonerr(InvalidAmountParam, None, id));
  414. }
  415. let amount = amount.unwrap();
  416. let token_id: &jubjub::Fr;
  417. // get the id for the token
  418. if let Some(tk_id) = self.drk_tokenlist.tokens.get(&token.to_uppercase()) {
  419. token_id = tk_id;
  420. } else {
  421. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  422. }
  423. let result: Result<()> = async {
  424. let drk_address = bs58::decode(&address).into_vec()?;
  425. let drk_address: jubjub::SubgroupPoint = deserialize(&drk_address)?;
  426. let decimals: usize = 8;
  427. let amount = decode_base10(&amount, decimals, true)?;
  428. self.client
  429. .lock()
  430. .await
  431. .transfer(token_id.clone(), drk_address, amount)
  432. .await?;
  433. Ok(())
  434. }
  435. .await;
  436. match result {
  437. Ok(msg) => JsonResult::Resp(jsonresp(json!(msg), json!(id))),
  438. Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
  439. }
  440. }
  441. }
  442. #[async_std::main]
  443. async fn main() -> Result<()> {
  444. let args = clap_app!(darkfid =>
  445. (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
  446. (@arg verbose: -v --verbose "Increase verbosity")
  447. //(@subcommand cashier =>
  448. // (about: "Manage cashier public key")
  449. // (@arg GETCASHIERKEY: --get "Get cashier public key")
  450. // (@arg SETCASHIERKEY: --set +takes_value "Sets cashier public key")
  451. //)
  452. )
  453. .get_matches();
  454. let config_path = if args.is_present("CONFIG") {
  455. PathBuf::from(args.value_of("CONFIG").unwrap())
  456. } else {
  457. join_config_path(&PathBuf::from("darkfid.toml"))?
  458. };
  459. let loglevel = if args.is_present("verbose") {
  460. log::Level::Debug
  461. } else {
  462. log::Level::Info
  463. };
  464. simple_logger::init_with_level(loglevel)?;
  465. let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
  466. let wallet = WalletDb::new(
  467. expand_path(&config.wallet_path)?.as_path(),
  468. config.wallet_password.clone(),
  469. )?;
  470. //if let Some(matches) = args.subcommand_matches("cashier") {
  471. // if matches.is_present("GETCASHIERKEY") {
  472. // let cashier_public = wallet.get_cashier_public_keys()?[0];
  473. // let cashier_public = bs58::encode(&serialize(&cashier_public)).into_string();
  474. // println!("Cashier Public Key: {}", cashier_public);
  475. // return Ok(());
  476. // }
  477. // if matches.is_present("SETCASHIERKEY") {
  478. // let cashier_public = matches.value_of("SETCASHIERKEY").unwrap();
  479. // let cashier_public: jubjub::SubgroupPoint =
  480. // deserialize(&bs58::decode(cashier_public).into_vec()?)?;
  481. // wallet.put_cashier_pub(&cashier_public)?;
  482. // println!("Cashier public key set successfully");
  483. // return Ok(());
  484. // }
  485. //}
  486. let mut darkfid = Darkfid::new(config.clone(), wallet.clone()).await?;
  487. let server_config = RpcServerConfig {
  488. socket_addr: config.rpc_listen_address.clone(),
  489. use_tls: config.serve_tls,
  490. identity_path: expand_path(&config.tls_identity_path.clone())?,
  491. identity_pass: config.tls_identity_password.clone(),
  492. };
  493. darkfid.start().await?;
  494. listen_and_serve(server_config, Arc::new(darkfid)).await
  495. }