darkfid.rs 19 KB

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