darkfid.rs 20 KB

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