darkfid2.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. use log::*;
  2. use std::fs;
  3. use std::path::PathBuf;
  4. use clap::clap_app;
  5. use serde_json::{json, Value};
  6. use simplelog::{
  7. CombinedLogger, Config as SimLogConfig, ConfigBuilder, LevelFilter, TermLogger, TerminalMode,
  8. WriteLogger,
  9. };
  10. use async_std::sync::Arc;
  11. use tokio::io::{AsyncReadExt, AsyncWriteExt};
  12. use tokio::net::TcpListener;
  13. use drk::{
  14. cli::{Config, DarkfidConfig},
  15. rpc::{
  16. jsonrpc::{error as jsonerr, request as jsonreq, response as jsonresp, send_request},
  17. jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
  18. },
  19. serial::serialize,
  20. util::join_config_path,
  21. wallet::WalletDb,
  22. Error,
  23. };
  24. #[derive(Clone)]
  25. struct Darkfid {
  26. verbose: bool,
  27. config: DarkfidConfig,
  28. wallet: Arc<WalletDb>,
  29. // clientdb:
  30. // mint_params:
  31. // spend_params:
  32. }
  33. impl Darkfid {
  34. fn new(verbose: bool, config_path: PathBuf) -> Result<Self, Error> {
  35. let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
  36. let wallet = WalletDb::new(
  37. &PathBuf::from(config.walletdb_path.clone()),
  38. config.password.clone(),
  39. )?;
  40. Ok(Self {
  41. verbose,
  42. config,
  43. wallet,
  44. })
  45. }
  46. // TODO: ServerError codes should be part of the lib.
  47. async fn handle_request(self, req: JsonRequest) -> JsonResult {
  48. if req.params.as_array().is_none() {
  49. return JsonResult::Err(jsonerr(InvalidParams, None, req.id));
  50. }
  51. debug!(target: "RPC", "--> {:#?}", serde_json::to_string(&req).unwrap());
  52. match req.method.as_str() {
  53. Some("say_hello") => return self.say_hello(req.id, req.params).await,
  54. Some("create_wallet") => return self.create_wallet(req.id, req.params).await,
  55. Some("key_gen") => return self.key_gen(req.id, req.params).await,
  56. Some("get_key") => return self.get_key(req.id, req.params).await,
  57. Some("get_token_id") => return self.get_token_id(req.id, req.params).await,
  58. Some("deposit") => return self.deposit(req.id, req.params).await,
  59. Some("withdraw") => return self.withdraw(req.id, req.params).await,
  60. Some("transfer") => return self.transfer(req.id, req.params).await,
  61. Some(_) => {}
  62. None => {}
  63. };
  64. return JsonResult::Err(jsonerr(MethodNotFound, None, req.id));
  65. }
  66. // --> {"method": "say_hello", "params": []}
  67. // <-- {"result": "hello world"}
  68. async fn say_hello(self, id: Value, _params: Value) -> JsonResult {
  69. JsonResult::Resp(jsonresp(json!("hello world"), id))
  70. }
  71. // --> {"method": "create_wallet", "params": []}
  72. // <-- {"result": true}
  73. async fn create_wallet(self, id: Value, _params: Value) -> JsonResult {
  74. match self.wallet.init_db() {
  75. Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
  76. Err(e) => {
  77. return JsonResult::Err(jsonerr(ServerError(-32001), Some(e.to_string()), id))
  78. }
  79. }
  80. }
  81. // --> {"method": "key_gen", "params": []}
  82. // <-- {"result": true}
  83. async fn key_gen(self, id: Value, _params: Value) -> JsonResult {
  84. match self.wallet.key_gen() {
  85. Ok((_, _)) => return JsonResult::Resp(jsonresp(json!(true), id)),
  86. Err(e) => {
  87. return JsonResult::Err(jsonerr(ServerError(-32002), Some(e.to_string()), id))
  88. }
  89. }
  90. }
  91. // --> {"method": "get_key", "params": []}
  92. // <-- {"result": "vdNS7oBj7KvsMWWmo9r96SV4SqATLrGsH2a3PGpCfJC"}
  93. async fn get_key(self, id: Value, _params: Value) -> JsonResult {
  94. match self.wallet.get_keypairs() {
  95. Ok(v) => {
  96. let pk = v[0].public;
  97. let b58 = bs58::encode(serialize(&pk)).into_string();
  98. return JsonResult::Resp(jsonresp(json!(b58), id));
  99. }
  100. Err(e) => {
  101. return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
  102. }
  103. }
  104. }
  105. // --> {"jsonrpc": "2.0", "method": "get_token_id",
  106. // "params": [token],
  107. // "id": 42}
  108. // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
  109. async fn get_token_id(self, id: Value, params: Value) -> JsonResult {
  110. let args = params.as_array().unwrap();
  111. let symbol = &args[0];
  112. if symbol.as_str().is_none() {
  113. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  114. };
  115. let symbol = symbol.as_str().unwrap().to_uppercase();
  116. let file_contents =
  117. fs::read_to_string("token/solanatokenlist.json").expect("Can't find tokenlist file");
  118. let root: Value = serde_json::from_str(&file_contents).unwrap();
  119. let tokens = root["tokens"].as_array().unwrap();
  120. for item in tokens {
  121. if item["symbol"] == symbol {
  122. let address = &item["address"];
  123. return JsonResult::Resp(jsonresp(json!(address), id));
  124. }
  125. }
  126. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  127. }
  128. // --> {"jsonrpc": "2.0", "method": "deposit",
  129. // "params": [network, token, publickey],
  130. // "id": 42}
  131. // The publickey sent here is used so the cashier can know where to send
  132. // assets once the deposit is received.
  133. // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
  134. async fn deposit(self, id: Value, params: Value) -> JsonResult {
  135. let args = params.as_array().unwrap();
  136. if args.len() != 2 {
  137. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  138. }
  139. let network = &args[0];
  140. let token = &args[1];
  141. if token.as_str().is_none() {
  142. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  143. };
  144. // TODO: Optional sanity checking here, but cashier *must* do so too.
  145. let pubkey: String;
  146. match self.wallet.get_keypairs() {
  147. Ok(v) => {
  148. let pk = v[0].public;
  149. pubkey = bs58::encode(serialize(&pk)).into_string();
  150. }
  151. Err(e) => {
  152. return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
  153. }
  154. }
  155. // Send request to cashier. If the cashier supports the requested network
  156. // (and token), it shall return a valid address where assets can be deposited.
  157. // If not, an error is returned, and forwarded to the method caller.
  158. let req = jsonreq(json!("deposit"), json!([network, token, pubkey]));
  159. let rep: JsonResult;
  160. match send_request(self.config.cashier_url, json!(req)).await {
  161. Ok(v) => rep = v,
  162. Err(e) => {
  163. return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
  164. }
  165. }
  166. match rep {
  167. JsonResult::Resp(r) => return JsonResult::Resp(r),
  168. JsonResult::Err(e) => return JsonResult::Err(e),
  169. JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
  170. }
  171. }
  172. // --> {"method": "withdraw", "params": [network, token, publickey, amount]}
  173. // The publickey sent here is the address where the caller wants to receive
  174. // the tokens they plan to withdraw.
  175. // On request, send request to cashier to get deposit address, and then transfer
  176. // dark assets to the cashier's wallet. Following that, the cashier should return
  177. // a transaction ID of them sending the funds that are requested for withdrawal.
  178. // <-- {"result": "txID"}
  179. async fn withdraw(self, id: Value, params: Value) -> JsonResult {
  180. let args = params.as_array().unwrap();
  181. if args.len() != 4 {
  182. return JsonResult::Err(jsonerr(InvalidParams, None, id));
  183. }
  184. let network = &args[0];
  185. let token = &args[1];
  186. let address = &args[2];
  187. let amount = &args[3];
  188. // 1. Send request to cashier.
  189. // 2. Cashier checks if they support the network, and if so,
  190. // return adeposit address.
  191. // 3. We issue a transfer of $amount to the given address.
  192. return JsonResult::Err(jsonerr(
  193. ServerError(-32005),
  194. Some("failed to withdraw".to_string()),
  195. id,
  196. ));
  197. }
  198. // --> {"method": "transfer", [dToken, address, amount]}
  199. // <-- {"result": "txID"}
  200. async fn transfer(self, id: Value, _params: Value) -> JsonResult {
  201. return JsonResult::Err(jsonerr(
  202. ServerError(-32006),
  203. Some("failed to transfer".to_string()),
  204. id,
  205. ));
  206. }
  207. }
  208. #[tokio::main]
  209. async fn main() -> Result<(), Box<dyn std::error::Error>> {
  210. let args = clap_app!(darkfid =>
  211. (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
  212. (@arg verbose: -v --verbose "Increase verbosity")
  213. )
  214. .get_matches();
  215. let config_path: PathBuf;
  216. if args.is_present("CONFIG") {
  217. config_path = PathBuf::from(args.value_of("CONFIG").unwrap());
  218. } else {
  219. config_path = join_config_path(&PathBuf::from("darkfid.toml"))?;
  220. }
  221. let darkfid = Darkfid::new(args.clone().is_present("verbose"), config_path)?;
  222. // TODO: TLS
  223. let listener = TcpListener::bind(darkfid.clone().config.rpc_url).await?;
  224. debug!(target: "RPC SERVER", "Listening on {}", darkfid.clone().config.rpc_url);
  225. let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
  226. let debug_level = if args.is_present("verbose") {
  227. LevelFilter::Debug
  228. } else {
  229. LevelFilter::Off
  230. };
  231. let log_path = darkfid.clone().config.log_path;
  232. CombinedLogger::init(vec![
  233. TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
  234. WriteLogger::new(
  235. LevelFilter::Debug,
  236. SimLogConfig::default(),
  237. std::fs::File::create(log_path).unwrap(),
  238. ),
  239. ])
  240. .unwrap();
  241. loop {
  242. debug!(target: "RPC SERVER", "waiting for client");
  243. let (mut socket, _) = listener.accept().await?;
  244. let darkfid = darkfid.clone();
  245. debug!(target: "RPC SERVER", "accepted client");
  246. tokio::spawn(async move {
  247. let mut buf = [0; 2048];
  248. loop {
  249. let n = match socket.read(&mut buf).await {
  250. Ok(n) if n == 0 => {
  251. debug!(target: "RPC SERVER", "closed connection");
  252. return;
  253. }
  254. Ok(n) => n,
  255. Err(e) => {
  256. debug!(target: "RPC SERVER", "failed to read from socket; err = {:?}", e);
  257. return;
  258. }
  259. };
  260. let r: JsonRequest = match serde_json::from_slice(&buf[0..n]) {
  261. Ok(r) => r,
  262. Err(e) => {
  263. debug!(target: "RPC SERVER", "received invalid json; err = {:?}", e);
  264. return;
  265. }
  266. };
  267. let reply = darkfid.clone().handle_request(r).await;
  268. let j = serde_json::to_string(&reply).unwrap();
  269. debug!(target: "RPC", "<-- {:#?}", j);
  270. // Write the data back
  271. if let Err(e) = socket.write_all(j.as_bytes()).await {
  272. debug!(target: "RPC SERVER", "failed to write to socket; err = {:?}", e);
  273. return;
  274. }
  275. }
  276. });
  277. }
  278. }