darkfid.rs 18 KB

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