darkfid.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  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 std::sync::Arc;
  8. use drk::{
  9. blockchain::Rocks,
  10. cli::{Config, DarkfidConfig},
  11. client::Client,
  12. rpc::{
  13. jsonrpc::{error as jsonerr, request as jsonreq, response as jsonresp, send_request},
  14. jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
  15. rpcserver::{listen_and_serve, RequestHandler, RpcServerConfig},
  16. },
  17. serial::{deserialize, serialize},
  18. util::{assign_id, decimals, expand_path, join_config_path, to_apo, TokenList},
  19. wallet::WalletDb,
  20. Result,
  21. };
  22. #[derive(Clone)]
  23. struct Darkfid {
  24. config: DarkfidConfig,
  25. wallet: Arc<WalletDb>,
  26. client: Arc<Mutex<Client>>,
  27. tokenlist: TokenList,
  28. }
  29. #[async_trait]
  30. impl RequestHandler for Darkfid {
  31. // TODO: ServerError codes should be part of the lib.
  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_path: PathBuf) -> Result<Self> {
  53. let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
  54. let wallet = WalletDb::new(
  55. expand_path(&config.wallet_path)?.as_path(),
  56. config.wallet_password.clone(),
  57. )?;
  58. debug!(target: "DARKFID", "INIT WALLET WITH PATH {}", config.wallet_path);
  59. let rocks = Rocks::new(expand_path(&config.database_path.clone())?.as_path())?;
  60. let client = Client::new(
  61. rocks,
  62. (
  63. config.gateway_protocol_url.parse()?,
  64. config.gateway_publisher_url.parse()?,
  65. ),
  66. (
  67. expand_path(&config.mint_params_path.clone())?,
  68. expand_path(&config.spend_params_path.clone())?,
  69. ),
  70. wallet.clone(),
  71. )
  72. .await?;
  73. let client = Arc::new(Mutex::new(client));
  74. let tokenlist = TokenList::new()?;
  75. Ok(Self {
  76. config,
  77. wallet,
  78. client,
  79. tokenlist,
  80. })
  81. }
  82. // --> {"method": "say_hello", "params": []}
  83. // <-- {"result": "hello world"}
  84. async fn say_hello(&self, id: Value, _params: Value) -> JsonResult {
  85. JsonResult::Resp(jsonresp(json!("hello world"), id))
  86. }
  87. // --> {"method": "create_wallet", "params": []}
  88. // <-- {"result": true}
  89. async fn create_wallet(&self, id: Value, _params: Value) -> JsonResult {
  90. match self.wallet.init_db().await {
  91. Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
  92. Err(e) => {
  93. return JsonResult::Err(jsonerr(ServerError(-32001), Some(e.to_string()), id))
  94. }
  95. }
  96. }
  97. // --> {"method": "key_gen", "params": []}
  98. // <-- {"result": true}
  99. async fn key_gen(&self, id: Value, _params: Value) -> JsonResult {
  100. match self.wallet.key_gen() {
  101. Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
  102. Err(e) => {
  103. return JsonResult::Err(jsonerr(ServerError(-32002), Some(e.to_string()), id))
  104. }
  105. }
  106. }
  107. // --> {"method": "get_key", "params": []}
  108. // <-- {"result": "vdNS7oBj7KvsMWWmo9r96SV4SqATLrGsH2a3PGpCfJC"}
  109. async fn get_key(&self, id: Value, _params: Value) -> JsonResult {
  110. match self.wallet.get_keypairs() {
  111. Ok(v) => {
  112. let pk = v[0].public;
  113. let b58 = bs58::encode(serialize(&pk)).into_string();
  114. return JsonResult::Resp(jsonresp(json!(b58), id));
  115. }
  116. Err(e) => {
  117. return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
  118. }
  119. }
  120. }
  121. // --> {"method": "get_token_id", "params": [token]}
  122. // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
  123. async fn get_token_id(&self, id: Value, params: Value) -> JsonResult {
  124. let args = params.as_array();
  125. if args.is_none() {
  126. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  127. }
  128. let args = args.unwrap();
  129. let symbol = args[0].as_str();
  130. if symbol.is_none() {
  131. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  132. }
  133. let symbol = symbol.unwrap();
  134. let result: Result<Value> = async {
  135. let token_id = self.tokenlist.clone().search_id(symbol)?;
  136. Ok(json!(token_id))
  137. }
  138. .await;
  139. match result {
  140. Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(res))),
  141. Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
  142. }
  143. }
  144. // --> {""method": "features", "params": []}
  145. // <-- {"result": { "network": ["btc", "sol"] } }
  146. async fn features(&self, id: Value, _params: Value) -> JsonResult {
  147. // TODO: return a dictionary of features
  148. let req = jsonreq(json!("features"), json!([]));
  149. let rep: JsonResult;
  150. match send_request(&self.config.cashier_rpc_url, json!(req)).await {
  151. Ok(v) => rep = v,
  152. Err(e) => {
  153. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
  154. }
  155. }
  156. match rep {
  157. JsonResult::Resp(r) => return JsonResult::Resp(r),
  158. JsonResult::Err(e) => return JsonResult::Err(e),
  159. JsonResult::Notif(_) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  160. }
  161. }
  162. // --> {"method": "deposit", "params": [network, token, publickey]}
  163. // The publickey sent here is used so the cashier can know where to send
  164. // assets once the deposit is received.
  165. // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
  166. async fn deposit(&self, id: Value, params: Value) -> JsonResult {
  167. let args = params.as_array();
  168. if args.is_none() {
  169. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  170. }
  171. let args = args.unwrap();
  172. if args.len() != 2 {
  173. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  174. }
  175. let network = &args[0];
  176. let token = &args[1];
  177. if token.as_str().is_none() {
  178. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  179. }
  180. let token = token.as_str().unwrap();
  181. if network.as_str().is_none() {
  182. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  183. }
  184. let network = network.as_str().unwrap();
  185. let token_id = match assign_id(&network, &token, self.tokenlist.clone()) {
  186. Ok(t) => t,
  187. Err(_e) => {
  188. debug!(target: "DARKFID", "TOKEN ID IS ERR");
  189. // TODO: this should return the relevant drk error
  190. // right now it just flattens it into ParseError
  191. return JsonResult::Err(jsonerr(ParseError, None, id));
  192. }
  193. };
  194. // TODO: Optional sanity checking here, but cashier *must* do so too.
  195. let pubkey: String;
  196. match self.wallet.get_keypairs() {
  197. Ok(v) => {
  198. let pk = v[0].public;
  199. let pk = serialize(&pk);
  200. pubkey = bs58::encode(pk).into_string();
  201. }
  202. Err(e) => {
  203. return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
  204. }
  205. }
  206. // Send request to cashier. If the cashier supports the requested network
  207. // (and token), it shall return a valid address where assets can be deposited.
  208. // If not, an error is returned, and forwarded to the method caller.
  209. let req = jsonreq(json!("deposit"), json!([network, token_id, pubkey]));
  210. let rep: JsonResult;
  211. match send_request(&self.config.cashier_rpc_url, json!(req)).await {
  212. Ok(v) => rep = v,
  213. Err(e) => {
  214. debug!(target: "DARKFID", "REQUEST IS ERR");
  215. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id));
  216. }
  217. }
  218. match rep {
  219. JsonResult::Resp(r) => return JsonResult::Resp(r),
  220. JsonResult::Err(e) => return JsonResult::Err(e),
  221. JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  222. }
  223. }
  224. // --> {"method": "withdraw", "params": [network, token, publickey, amount]}
  225. // The publickey sent here is the address where the caller wants to receive
  226. // the tokens they plan to withdraw.
  227. // On request, send request to cashier to get deposit address, and then transfer
  228. // dark assets to the cashier's wallet. Following that, the cashier should return
  229. // a transaction ID of them sending the funds that are requested for withdrawal.
  230. // <-- {"result": "txID"}
  231. async fn withdraw(&self, id: Value, params: Value) -> JsonResult {
  232. let args = params.as_array();
  233. if args.is_none() {
  234. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  235. }
  236. let args = args.unwrap();
  237. if args.len() != 4 {
  238. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  239. }
  240. let network = &args[0];
  241. let token = &args[1];
  242. let address = &args[2];
  243. let amount = &args[3];
  244. if token.as_str().is_none() {
  245. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  246. }
  247. let token = token.as_str().unwrap();
  248. if network.as_str().is_none() {
  249. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  250. }
  251. let network = network.as_str().unwrap();
  252. if amount.as_f64().is_none() {
  253. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  254. }
  255. let amount = amount.as_f64().unwrap();
  256. // TODO: get rid of these unwraps
  257. let decimals = decimals(network, token, self.tokenlist.clone()).unwrap();
  258. let amount_in_apo = to_apo(amount, decimals as u32).unwrap();
  259. let token_id = match assign_id(&network, &token, self.tokenlist.clone()) {
  260. Ok(t) => t,
  261. Err(_e) => {
  262. debug!(target: "DARKFID", "TOKEN ID IS ERR");
  263. // TODO: this should return the relevant drk error
  264. // right now it just flattens it into ParseError
  265. return JsonResult::Err(jsonerr(ParseError, None, id));
  266. }
  267. };
  268. let req = jsonreq(
  269. json!("withdraw"),
  270. json!([network, token_id, address, amount_in_apo]),
  271. );
  272. let rep: JsonResult;
  273. match send_request(&self.config.cashier_rpc_url, json!(req)).await {
  274. Ok(v) => rep = v,
  275. Err(e) => {
  276. debug!(target: "DARKFID", "REQUEST IS ERR");
  277. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id));
  278. }
  279. }
  280. match rep {
  281. JsonResult::Resp(r) => return JsonResult::Resp(r),
  282. JsonResult::Err(e) => return JsonResult::Err(e),
  283. JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  284. }
  285. }
  286. // --> {"method": "transfer", [dToken, address, amount]}
  287. // <-- {"result": "txID"}
  288. async fn transfer(&self, id: Value, params: Value) -> JsonResult {
  289. let args = params.as_array();
  290. if args.is_none() {
  291. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  292. }
  293. let args = args.unwrap();
  294. if args.len() != 3 {
  295. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  296. }
  297. let token = &args[0];
  298. let address = &args[1];
  299. let amount = &args[2];
  300. if token.as_str().is_none() {
  301. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  302. }
  303. let token = address.as_str().unwrap();
  304. if address.as_str().is_none() {
  305. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  306. }
  307. let address = address.as_str().unwrap();
  308. if amount.as_f64().is_none() {
  309. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  310. }
  311. let amount = amount.as_f64().unwrap();
  312. // TODO: get tokenID from walletdb
  313. //let result: Result<()> = async {
  314. // let token_id = parse_wrapped_token(token, self.tokenlist.clone())?;
  315. // let address = bs58::decode(&address).into_vec()?;
  316. // let address: jubjub::SubgroupPoint = deserialize(&address)?;
  317. // self.client
  318. // .lock()
  319. // .await
  320. // .transfer(token_id, address, amount)
  321. // .await?;
  322. // Ok(())
  323. //}
  324. //.await;
  325. //match result {
  326. // Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(id))),
  327. // Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
  328. //}
  329. return JsonResult::Err(jsonerr(
  330. ServerError(-32005),
  331. Some("failed to withdraw".to_string()),
  332. id,
  333. ));
  334. }
  335. }
  336. #[async_std::main]
  337. async fn main() -> Result<()> {
  338. let args = clap_app!(darkfid =>
  339. (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
  340. (@arg verbose: -v --verbose "Increase verbosity")
  341. )
  342. .get_matches();
  343. let config_path = if args.is_present("CONFIG") {
  344. PathBuf::from(args.value_of("CONFIG").unwrap())
  345. } else {
  346. join_config_path(&PathBuf::from("darkfid.toml"))?
  347. };
  348. let loglevel = if args.is_present("verbose") {
  349. log::Level::Debug
  350. } else {
  351. log::Level::Info
  352. };
  353. simple_logger::init_with_level(loglevel)?;
  354. let darkfid = Darkfid::new(config_path).await?;
  355. let server_config = RpcServerConfig {
  356. socket_addr: darkfid.config.rpc_listen_address.clone(),
  357. use_tls: darkfid.config.serve_tls,
  358. identity_path: expand_path(&darkfid.config.tls_identity_path.clone())?,
  359. identity_pass: darkfid.config.tls_identity_password.clone(),
  360. };
  361. listen_and_serve(server_config, darkfid).await
  362. }
  363. mod tests {
  364. //#[test]
  365. //fn test_token_parsing() {
  366. // let token = "usdc";
  367. // let vec: Vec<char> = token.chars().collect();
  368. // let mut counter = 0;
  369. // for c in vec {
  370. // if c.is_alphabetic() {
  371. // counter += 1;
  372. // println!("Found letter: {}", c)
  373. // }
  374. // }
  375. // if counter == token.len() {
  376. // println!("Every character is a letter");
  377. // }
  378. //}
  379. }