main.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. use std::{net::SocketAddr, path::PathBuf, sync::Arc, thread, time};
  2. use async_executor::Executor;
  3. use async_trait::async_trait;
  4. use easy_parallel::Parallel;
  5. use log::{debug, error, info};
  6. use rand::{rngs::OsRng, RngCore};
  7. use serde::{Deserialize, Serialize};
  8. use serde_json::{json, Value};
  9. use simplelog::{ColorChoice, TermLogger, TerminalMode};
  10. use structopt::StructOpt;
  11. use structopt_toml::StructOptToml;
  12. use darkfi::{
  13. consensus::{
  14. state::{State, StatePtr},
  15. tx::Tx,
  16. },
  17. net,
  18. rpc::{
  19. jsonrpc,
  20. jsonrpc::{
  21. from_result,
  22. ErrorCode::{InternalError, InvalidParams, InvalidRequest, MethodNotFound},
  23. JsonRequest, JsonResult, ValueResult,
  24. },
  25. rpcserver::{listen_and_serve, RequestHandler, RpcServerConfig},
  26. },
  27. util::{
  28. cli::{log_config, spawn_config},
  29. expand_path,
  30. path::get_config_path,
  31. },
  32. Result,
  33. };
  34. use validatord::protocols::{
  35. protocol_proposal::ProtocolProposal, protocol_tx::ProtocolTx, protocol_vote::ProtocolVote,
  36. };
  37. const CONFIG_FILE: &str = r"validatord_config.toml";
  38. const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../validatord_config.toml");
  39. #[derive(Debug, Deserialize, Serialize, StructOpt, StructOptToml)]
  40. #[serde(default)]
  41. struct Opt {
  42. #[structopt(short, long, default_value = CONFIG_FILE)]
  43. /// Configuration file to use
  44. config: String,
  45. #[structopt(long, default_value = "0.0.0.0:11000")]
  46. /// Accept address
  47. accept: SocketAddr,
  48. #[structopt(long)]
  49. /// Seed nodes
  50. seeds: Vec<SocketAddr>,
  51. #[structopt(long)]
  52. /// Manual connections
  53. connect: Vec<SocketAddr>,
  54. #[structopt(long, default_value = "5")]
  55. /// Connection slots
  56. slots: u32,
  57. #[structopt(long, default_value = "127.0.0.1:11000")]
  58. /// External address
  59. external: SocketAddr,
  60. #[structopt(long, default_value = "/tmp/darkfid.log")]
  61. /// Logfile path
  62. log: String,
  63. #[structopt(long, default_value = "127.0.0.1:6660")]
  64. /// The endpoint where validatord will bind its RPC socket
  65. rpc: SocketAddr,
  66. #[structopt(long)]
  67. /// Whether to listen with TLS or plain TCP
  68. tls: bool,
  69. #[structopt(long, default_value = "~/.config/darkfi/validatord_identity.pfx")]
  70. /// TLS certificate to use
  71. identity: PathBuf,
  72. #[structopt(long, default_value = "FOOBAR")]
  73. /// Password for the created TLS identity
  74. password: String,
  75. #[structopt(long, default_value = "~/.config/darkfi/validatord_state_0")]
  76. /// Path to the state file
  77. state: String,
  78. #[structopt(long, default_value = "0")]
  79. /// How many threads to utilize
  80. id: u64,
  81. #[structopt(short, long, default_value = "0")]
  82. /// How many threads to utilize
  83. threads: usize,
  84. #[structopt(short, long, parse(from_occurrences))]
  85. /// Multiple levels can be used (-vv)
  86. verbose: u8,
  87. }
  88. // TODO:
  89. // 1. Nodes count not hardcoded.
  90. // 2. Remove dummy delay.
  91. async fn proposal_task(p2p: net::P2pPtr, state: StatePtr, state_path: &PathBuf) {
  92. let nodes_count = 4;
  93. // After initialization node should wait for next epoch
  94. let seconds_until_next_epoch = state.read().unwrap().get_seconds_until_next_epoch_start();
  95. info!("Waiting for next epoch({:?} sec)...", seconds_until_next_epoch);
  96. thread::sleep(seconds_until_next_epoch);
  97. loop {
  98. let result = if state.read().unwrap().check_if_epoch_leader(nodes_count) {
  99. state.read().unwrap().propose_block()
  100. } else {
  101. Ok(None)
  102. };
  103. match result {
  104. Ok(proposal) => {
  105. if proposal.is_none() {
  106. info!("Node is not the epoch leader. Sleeping till next epoch...");
  107. } else {
  108. let unwrapped = proposal.unwrap();
  109. info!("Node is the epoch leader. Proposed block: {:?}", unwrapped);
  110. let vote = state.write().unwrap().receive_proposed_block(
  111. &unwrapped,
  112. nodes_count,
  113. true,
  114. );
  115. match vote {
  116. Ok(x) => {
  117. if x.is_none() {
  118. debug!("Node did not vote for the proposed block.");
  119. } else {
  120. let vote = x.unwrap();
  121. state.write().unwrap().receive_vote(&vote, nodes_count as usize);
  122. // Broadcasting block
  123. let result = p2p.broadcast(unwrapped).await;
  124. match result {
  125. Ok(()) => info!("Proposal broadcasted successfuly."),
  126. Err(e) => error!("Broadcast failed. Error: {:?}", e),
  127. }
  128. // Broadcasting leader vote
  129. thread::sleep(time::Duration::from_secs(10)); // communication delay simulation
  130. let result = p2p.broadcast(vote).await;
  131. match result {
  132. Ok(()) => info!("Leader vote broadcasted successfuly."),
  133. Err(e) => error!("Broadcast failed. Error: {:?}", e),
  134. }
  135. }
  136. }
  137. Err(e) => {
  138. debug!(target: "ircd", "ProtocolBlock::handle_receive_proposal() error prosessing proposal: {:?}", e)
  139. }
  140. }
  141. }
  142. }
  143. Err(e) => error!("Broadcast failed. Error: {:?}", e),
  144. }
  145. let seconds_until_next_epoch = state.read().unwrap().get_seconds_until_next_epoch_start();
  146. info!("Waiting for next epoch({:?} sec)...", seconds_until_next_epoch);
  147. thread::sleep(seconds_until_next_epoch);
  148. let result = state.read().unwrap().save(state_path);
  149. match result {
  150. Ok(()) => (),
  151. Err(e) => {
  152. debug!(target: "ircd", "ProtocolVote::handle_receive_proposal() error saving state: {:?}", e)
  153. }
  154. };
  155. }
  156. }
  157. async fn start(executor: Arc<Executor<'_>>, opts: &Opt) -> Result<()> {
  158. let rpc_server_config = RpcServerConfig {
  159. socket_addr: opts.rpc,
  160. use_tls: opts.tls,
  161. identity_path: opts.identity.clone(),
  162. identity_pass: opts.password.clone(),
  163. };
  164. let network_settings = net::Settings {
  165. inbound: Some(opts.accept),
  166. outbound_connections: opts.slots,
  167. external_addr: Some(opts.external),
  168. peers: opts.connect.clone(),
  169. seeds: opts.seeds.clone(),
  170. ..Default::default()
  171. };
  172. // State setup
  173. let state_path = expand_path(&opts.state).unwrap();
  174. let id = opts.id.clone();
  175. let state = State::load_current_state(id, &state_path).unwrap();
  176. // P2P registry setup
  177. let p2p = net::P2p::new(network_settings).await;
  178. let registry = p2p.protocol_registry();
  179. // Adding ProtocolTx to the registry
  180. let state2 = state.clone();
  181. registry
  182. .register(net::SESSION_ALL, move |channel, _p2p| {
  183. let state = state2.clone();
  184. async move { ProtocolTx::init(channel, state).await }
  185. })
  186. .await;
  187. // Adding PropotolVote to the registry
  188. let state2 = state.clone();
  189. registry
  190. .register(net::SESSION_ALL, move |channel, _p2p| {
  191. let state = state2.clone();
  192. async move { ProtocolVote::init(channel, state).await }
  193. })
  194. .await;
  195. // Adding ProtocolProposal to the registry
  196. let state2 = state.clone();
  197. registry
  198. .register(net::SESSION_ALL, move |channel, p2p| {
  199. let state = state2.clone();
  200. async move { ProtocolProposal::init(channel, state, p2p).await }
  201. })
  202. .await;
  203. // Performs seed session
  204. p2p.clone().start(executor.clone()).await?;
  205. // Actual main p2p session
  206. let ex2 = executor.clone();
  207. let p2p2 = p2p.clone();
  208. executor
  209. .spawn(async move {
  210. if let Err(err) = p2p2.run(ex2).await {
  211. error!("Error: p2p run failed {}", err);
  212. }
  213. })
  214. .detach();
  215. // RPC interface
  216. let ex2 = executor.clone();
  217. let ex3 = ex2.clone();
  218. let rpc_interface = Arc::new(JsonRpcInterface {
  219. state: state.clone(),
  220. p2p: p2p.clone(),
  221. _rpc_listen_addr: opts.rpc,
  222. });
  223. executor
  224. .spawn(async move { listen_and_serve(rpc_server_config, rpc_interface, ex3).await })
  225. .detach();
  226. proposal_task(p2p, state, &state_path).await;
  227. Ok(())
  228. }
  229. struct JsonRpcInterface {
  230. state: StatePtr,
  231. p2p: net::P2pPtr,
  232. _rpc_listen_addr: SocketAddr,
  233. }
  234. #[async_trait]
  235. impl RequestHandler for JsonRpcInterface {
  236. async fn handle_request(&self, req: JsonRequest, _executor: Arc<Executor<'_>>) -> JsonResult {
  237. if req.params.as_array().is_none() {
  238. return jsonrpc::error(InvalidRequest, None, req.id).into()
  239. }
  240. debug!(target: "RPC", "--> {}", serde_json::to_string(&req).unwrap());
  241. from_result(
  242. match req.method.as_str() {
  243. Some("ping") => self.pong().await,
  244. Some("get_info") => self.get_info().await,
  245. Some("receive_tx") => self.receive_tx(req.params).await,
  246. Some(_) | None => Err(MethodNotFound),
  247. },
  248. req.id,
  249. )
  250. }
  251. }
  252. impl JsonRpcInterface {
  253. // --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 42}
  254. // <-- {"jsonrpc": "2.0", "result": "pong", "id": 42}
  255. async fn pong(&self) -> ValueResult<Value> {
  256. Ok(json!("pong"))
  257. }
  258. // --> {"jsonrpc": "2.0", "method": "get_info", "params": [], "id": 42}
  259. // <-- {"jsonrpc": "2.0", "result": {"nodeID": [], "nodeinfo" [], "id": 42}
  260. async fn get_info(&self) -> ValueResult<Value> {
  261. Ok(self.p2p.get_info().await)
  262. }
  263. // --> {"jsonrpc": "2.0", "method": "receive_tx", "params": ["tx"], "id": 42}
  264. // <-- {"jsonrpc": "2.0", "result": true, "id": 0}
  265. async fn receive_tx(&self, params: Value) -> ValueResult<Value> {
  266. let args = params.as_array().unwrap();
  267. if args.len() != 1 {
  268. return Err(InvalidParams)
  269. }
  270. // TODO: add proper tx hash here
  271. let random_id = OsRng.next_u32();
  272. let payload = String::from(args[0].as_str().unwrap());
  273. let tx = Tx { hash: random_id, payload };
  274. self.state.write().unwrap().append_tx(tx.clone());
  275. let result = self.p2p.broadcast(tx).await;
  276. match result {
  277. Ok(()) => Ok(json!(true)),
  278. Err(_) => Err(InternalError),
  279. }
  280. }
  281. }
  282. #[async_std::main]
  283. async fn main() -> Result<()> {
  284. let opts = Opt::from_args_with_toml(&String::from_utf8(CONFIG_FILE_CONTENTS.to_vec()).unwrap())
  285. .unwrap();
  286. let config_path = get_config_path(Some(opts.config.clone()), CONFIG_FILE)?;
  287. spawn_config(&config_path, CONFIG_FILE_CONTENTS)?;
  288. let opts = Opt::from_args_with_toml(&String::from_utf8(CONFIG_FILE_CONTENTS.to_vec()).unwrap())
  289. .unwrap();
  290. let (lvl, conf) = log_config(opts.verbose.into())?;
  291. TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
  292. let ex = Arc::new(Executor::new());
  293. let (signal, shutdown) = async_channel::unbounded::<()>();
  294. let ex2 = ex.clone();
  295. let nthreads = if opts.threads == 0 { num_cpus::get() } else { opts.threads };
  296. debug!(target: "VALIDATOR DAEMON", "Executing with opts: {:?}", opts);
  297. debug!(target: "VALIDATOR DAEMON", "Run {} executor threads", nthreads);
  298. let (_, result) = Parallel::new()
  299. .each(0..nthreads, |_| smol::future::block_on(ex.run(shutdown.recv())))
  300. // Run the main future on the current thread.
  301. .finish(|| {
  302. smol::future::block_on(async move {
  303. start(ex2.clone(), &opts).await?;
  304. drop(signal);
  305. Ok::<(), darkfi::Error>(())
  306. })
  307. });
  308. result
  309. }