main.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. use std::{net::SocketAddr, path::PathBuf, sync::Arc, thread};
  2. use async_executor::Executor;
  3. use async_trait::async_trait;
  4. use easy_parallel::Parallel;
  5. use log::{debug, error, info};
  6. use serde::{Deserialize, Serialize};
  7. use serde_json::{json, Value};
  8. use simplelog::{ColorChoice, TermLogger, TerminalMode};
  9. use structopt::StructOpt;
  10. use structopt_toml::StructOptToml;
  11. use darkfi::{
  12. consensus::{
  13. participant::Participant,
  14. state::{ValidatorState, ValidatorStatePtr},
  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_participant::ProtocolParticipant, protocol_proposal::ProtocolProposal,
  36. protocol_tx::ProtocolTx, protocol_vote::ProtocolVote,
  37. };
  38. const CONFIG_FILE: &str = r"validatord_config.toml";
  39. const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../validatord_config.toml");
  40. #[derive(Debug, Deserialize, Serialize, StructOpt, StructOptToml)]
  41. #[serde(default)]
  42. struct Opt {
  43. #[structopt(short, long, default_value = CONFIG_FILE)]
  44. /// Configuration file to use
  45. config: String,
  46. #[structopt(long, default_value = "0.0.0.0:11000")]
  47. /// Accept address
  48. accept: SocketAddr,
  49. #[structopt(long, default_value = "0.0.0.0:12000")]
  50. /// Consensus accept address
  51. caccept: SocketAddr,
  52. #[structopt(long)]
  53. /// Seed nodes
  54. seeds: Vec<SocketAddr>,
  55. #[structopt(long)]
  56. /// Consensus seed nodes
  57. cseeds: Vec<SocketAddr>,
  58. #[structopt(long)]
  59. /// Manual connections
  60. connect: Vec<SocketAddr>,
  61. #[structopt(long, default_value = "5")]
  62. /// Connection slots
  63. slots: u32,
  64. #[structopt(long, default_value = "127.0.0.1:11000")]
  65. /// External address
  66. external: SocketAddr,
  67. #[structopt(long, default_value = "127.0.0.1:12000")]
  68. /// Consensus accept address
  69. cexternal: SocketAddr,
  70. #[structopt(long, default_value = "/tmp/darkfid.log")]
  71. /// Logfile path
  72. log: String,
  73. #[structopt(long, default_value = "127.0.0.1:6660")]
  74. /// The endpoint where validatord will bind its RPC socket
  75. rpc: SocketAddr,
  76. #[structopt(long)]
  77. /// Whether to listen with TLS or plain TCP
  78. tls: bool,
  79. #[structopt(long, default_value = "~/.config/darkfi/validatord_identity.pfx")]
  80. /// TLS certificate to use
  81. identity: PathBuf,
  82. #[structopt(long, default_value = "FOOBAR")]
  83. /// Password for the created TLS identity
  84. password: String,
  85. #[structopt(long, default_value = "1648383795")]
  86. /// Timestamp of the genesis block creation
  87. genesis: i64,
  88. #[structopt(long, default_value = "~/.config/darkfi/validatord_db_0")]
  89. /// Path to the sled database folder
  90. database: String,
  91. #[structopt(long, default_value = "0")]
  92. /// Node ID, used only for testing
  93. id: u64,
  94. #[structopt(short, long, default_value = "0")]
  95. /// How many threads to utilize
  96. threads: usize,
  97. #[structopt(short, long, parse(from_occurrences))]
  98. /// Multiple levels can be used (-vv)
  99. verbose: u8,
  100. }
  101. async fn proposal_task(p2p: net::P2pPtr, state: ValidatorStatePtr) {
  102. // Node signals the network that it starts participating
  103. let participant =
  104. Participant::new(state.read().unwrap().id, state.read().unwrap().current_epoch());
  105. state.write().unwrap().append_participant(participant.clone());
  106. let result = p2p.broadcast(participant).await;
  107. match result {
  108. Ok(()) => info!("Participation message broadcasted successfuly."),
  109. Err(e) => error!("Broadcast failed. Error: {:?}", e),
  110. }
  111. // After initialization node should wait for next epoch
  112. let seconds_until_next_epoch = state.read().unwrap().next_epoch_start();
  113. info!("Waiting for next epoch({:?} sec)...", seconds_until_next_epoch);
  114. thread::sleep(seconds_until_next_epoch);
  115. loop {
  116. // Node refreshes participants records
  117. state.write().unwrap().refresh_participants();
  118. // Node checks if its the epoch leader to generate a new proposal for that epoch
  119. let result = if state.write().unwrap().is_epoch_leader() {
  120. state.read().unwrap().propose()
  121. } else {
  122. Ok(None)
  123. };
  124. match result {
  125. Ok(proposal) => {
  126. if proposal.is_none() {
  127. info!("Node is not the epoch leader. Sleeping till next epoch...");
  128. } else {
  129. // Leader creates a vote for the proposal and broadcasts them both
  130. let unwrapped = proposal.unwrap();
  131. info!("Node is the epoch leader. Proposed block: {:?}", unwrapped);
  132. let vote = state.write().unwrap().receive_proposal(&unwrapped);
  133. match vote {
  134. Ok(x) => {
  135. if x.is_none() {
  136. debug!("Node did not vote for the proposed block.");
  137. } else {
  138. let vote = x.unwrap();
  139. let result = state.write().unwrap().receive_vote(&vote);
  140. match result {
  141. Ok(_) => info!("Vote saved successfuly."),
  142. Err(e) => error!("Vote save failed. Error: {:?}", e),
  143. }
  144. // Broadcasting block
  145. let result = p2p.broadcast(unwrapped).await;
  146. match result {
  147. Ok(()) => info!("Proposal broadcasted successfuly."),
  148. Err(e) => error!("Broadcast failed. Error: {:?}", e),
  149. }
  150. // Broadcasting leader vote
  151. let result = p2p.broadcast(vote).await;
  152. match result {
  153. Ok(()) => info!("Leader vote broadcasted successfuly."),
  154. Err(e) => error!("Broadcast failed. Error: {:?}", e),
  155. }
  156. }
  157. }
  158. Err(e) => {
  159. error!("Error prosessing proposal: {:?}", e)
  160. }
  161. }
  162. }
  163. }
  164. Err(e) => error!("Block proposal failed. Error: {:?}", e),
  165. }
  166. // Current node state is flushed to sled database
  167. let result = state.read().unwrap().save_consensus_state();
  168. match result {
  169. Ok(()) => (),
  170. Err(e) => {
  171. error!("State could not be flushed: {:?}", e)
  172. }
  173. };
  174. // Node waits untile next epoch
  175. let seconds_until_next_epoch = state.read().unwrap().next_epoch_start();
  176. info!("Waiting for next epoch({:?} sec)...", seconds_until_next_epoch);
  177. thread::sleep(seconds_until_next_epoch);
  178. }
  179. }
  180. async fn start(executor: Arc<Executor<'_>>, opts: &Opt) -> Result<()> {
  181. let rpc_server_config = RpcServerConfig {
  182. socket_addr: opts.rpc,
  183. use_tls: opts.tls,
  184. identity_path: opts.identity.clone(),
  185. identity_pass: opts.password.clone(),
  186. };
  187. // Main subnet settings
  188. let subnet_settings = net::Settings {
  189. inbound: Some(opts.accept),
  190. outbound_connections: opts.slots,
  191. external_addr: Some(opts.external),
  192. peers: opts.connect.clone(),
  193. seeds: opts.seeds.clone(),
  194. ..Default::default()
  195. };
  196. // Consensus subnet settings
  197. let consensus_subnet_settings = net::Settings {
  198. inbound: Some(opts.caccept),
  199. outbound_connections: opts.slots,
  200. external_addr: Some(opts.cexternal),
  201. peers: opts.connect.clone(),
  202. seeds: opts.cseeds.clone(),
  203. ..Default::default()
  204. };
  205. // State setup
  206. let genesis = opts.genesis;
  207. let database_path = expand_path(&opts.database).unwrap();
  208. let id = opts.id.clone();
  209. let state = ValidatorState::new(database_path, id, genesis).unwrap();
  210. // Main P2P registry setup
  211. let p2p = net::P2p::new(subnet_settings).await;
  212. let _registry = p2p.protocol_registry();
  213. // Consensus P2P registry setup
  214. let p2p = net::P2p::new(consensus_subnet_settings).await;
  215. let registry = p2p.protocol_registry();
  216. // Adding ProtocolTx to the registry
  217. let state2 = state.clone();
  218. registry
  219. .register(net::SESSION_ALL, move |channel, p2p| {
  220. let state = state2.clone();
  221. async move { ProtocolTx::init(channel, state, p2p).await }
  222. })
  223. .await;
  224. // Adding PropotolVote to the registry
  225. let state2 = state.clone();
  226. registry
  227. .register(net::SESSION_ALL, move |channel, p2p| {
  228. let state = state2.clone();
  229. async move { ProtocolVote::init(channel, state, p2p).await }
  230. })
  231. .await;
  232. // Adding ProtocolProposal to the registry
  233. let state2 = state.clone();
  234. registry
  235. .register(net::SESSION_ALL, move |channel, p2p| {
  236. let state = state2.clone();
  237. async move { ProtocolProposal::init(channel, state, p2p).await }
  238. })
  239. .await;
  240. // Adding ProtocolParticipant to the registry
  241. let state2 = state.clone();
  242. registry
  243. .register(net::SESSION_ALL, move |channel, p2p| {
  244. let state = state2.clone();
  245. async move { ProtocolParticipant::init(channel, state, p2p).await }
  246. })
  247. .await;
  248. // Performs seed session
  249. p2p.clone().start(executor.clone()).await?;
  250. // Actual consensus p2p session
  251. let ex2 = executor.clone();
  252. let p2p2 = p2p.clone();
  253. executor
  254. .spawn(async move {
  255. if let Err(err) = p2p2.run(ex2).await {
  256. error!("Error: p2p run failed {}", err);
  257. }
  258. })
  259. .detach();
  260. // RPC interface
  261. let ex2 = executor.clone();
  262. let ex3 = ex2.clone();
  263. let rpc_interface = Arc::new(JsonRpcInterface {
  264. state: state.clone(),
  265. p2p: p2p.clone(),
  266. _rpc_listen_addr: opts.rpc,
  267. });
  268. executor
  269. .spawn(async move { listen_and_serve(rpc_server_config, rpc_interface, ex3).await })
  270. .detach();
  271. proposal_task(p2p, state).await;
  272. Ok(())
  273. }
  274. struct JsonRpcInterface {
  275. state: ValidatorStatePtr,
  276. p2p: net::P2pPtr,
  277. _rpc_listen_addr: SocketAddr,
  278. }
  279. #[async_trait]
  280. impl RequestHandler for JsonRpcInterface {
  281. async fn handle_request(&self, req: JsonRequest, _executor: Arc<Executor<'_>>) -> JsonResult {
  282. if req.params.as_array().is_none() {
  283. return jsonrpc::error(InvalidRequest, None, req.id).into()
  284. }
  285. debug!(target: "RPC", "--> {}", serde_json::to_string(&req).unwrap());
  286. from_result(
  287. match req.method.as_str() {
  288. Some("ping") => self.pong().await,
  289. Some("get_info") => self.get_info().await,
  290. Some("receive_tx") => self.receive_tx(req.params).await,
  291. Some(_) | None => Err(MethodNotFound),
  292. },
  293. req.id,
  294. )
  295. }
  296. }
  297. impl JsonRpcInterface {
  298. // --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 42}
  299. // <-- {"jsonrpc": "2.0", "result": "pong", "id": 42}
  300. async fn pong(&self) -> ValueResult<Value> {
  301. Ok(json!("pong"))
  302. }
  303. // --> {"jsonrpc": "2.0", "method": "get_info", "params": [], "id": 42}
  304. // <-- {"jsonrpc": "2.0", "result": {"nodeID": [], "nodeinfo" [], "id": 42}
  305. async fn get_info(&self) -> ValueResult<Value> {
  306. Ok(self.p2p.get_info().await)
  307. }
  308. // --> {"jsonrpc": "2.0", "method": "receive_tx", "params": ["tx"], "id": 42}
  309. // <-- {"jsonrpc": "2.0", "result": true, "id": 0}
  310. async fn receive_tx(&self, params: Value) -> ValueResult<Value> {
  311. let args = params.as_array().unwrap();
  312. if args.len() != 1 {
  313. return Err(InvalidParams)
  314. }
  315. let payload = String::from(args[0].as_str().unwrap());
  316. let tx = Tx { payload };
  317. self.state.write().unwrap().append_tx(tx.clone());
  318. let result = self.p2p.broadcast(tx).await;
  319. match result {
  320. Ok(()) => Ok(json!(true)),
  321. Err(_) => Err(InternalError),
  322. }
  323. }
  324. }
  325. #[async_std::main]
  326. async fn main() -> Result<()> {
  327. let opts = Opt::from_args_with_toml(&String::from_utf8(CONFIG_FILE_CONTENTS.to_vec()).unwrap())
  328. .unwrap();
  329. let config_path = get_config_path(Some(opts.config.clone()), CONFIG_FILE)?;
  330. spawn_config(&config_path, CONFIG_FILE_CONTENTS)?;
  331. let opts = Opt::from_args_with_toml(&String::from_utf8(CONFIG_FILE_CONTENTS.to_vec()).unwrap())
  332. .unwrap();
  333. let (lvl, conf) = log_config(opts.verbose.into())?;
  334. TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
  335. let ex = Arc::new(Executor::new());
  336. let (signal, shutdown) = async_channel::unbounded::<()>();
  337. let ex2 = ex.clone();
  338. let nthreads = if opts.threads == 0 { num_cpus::get() } else { opts.threads };
  339. debug!(target: "VALIDATOR DAEMON", "Executing with opts: {:?}", opts);
  340. debug!(target: "VALIDATOR DAEMON", "Run {} executor threads", nthreads);
  341. let (_, result) = Parallel::new()
  342. .each(0..nthreads, |_| smol::future::block_on(ex.run(shutdown.recv())))
  343. // Run the main future on the current thread.
  344. .finish(|| {
  345. smol::future::block_on(async move {
  346. start(ex2.clone(), &opts).await?;
  347. drop(signal);
  348. Ok::<(), darkfi::Error>(())
  349. })
  350. });
  351. result
  352. }