sync.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::collections::HashMap;
  19. use darkfi::{
  20. blockchain::HeaderHash, net::ChannelPtr, rpc::jsonrpc::JsonSubscriber, system::sleep,
  21. util::encoding::base64, validator::consensus::Proposal, Error, Result,
  22. };
  23. use darkfi_serial::serialize_async;
  24. use log::{debug, info, warn};
  25. use rand::{prelude::SliceRandom, rngs::OsRng};
  26. use tinyjson::JsonValue;
  27. use crate::{
  28. proto::{
  29. ForkSyncRequest, ForkSyncResponse, HeaderSyncRequest, HeaderSyncResponse, SyncRequest,
  30. SyncResponse, TipRequest, TipResponse, BATCH,
  31. },
  32. Darkfid,
  33. };
  34. // TODO: Parallelize independent requests.
  35. // We can also make them be like torrents, where we retrieve chunks not in order.
  36. /// async task used for block syncing.
  37. /// A checkpoint can be provided to ensure node syncs the correct sequence.
  38. pub async fn sync_task(node: &Darkfid, checkpoint: Option<(u32, HeaderHash)>) -> Result<()> {
  39. info!(target: "darkfid::task::sync_task", "Starting blockchain sync...");
  40. // Grab blocks subscriber
  41. let block_sub = node.subscribers.get("blocks").unwrap();
  42. // Grab last known block header, including existing pending sync ones
  43. let mut last = node.validator.blockchain.last()?;
  44. // If checkpoint is not reached, purge headers and start syncing from scratch
  45. if let Some(checkpoint) = checkpoint {
  46. if checkpoint.0 > last.0 {
  47. node.validator.blockchain.headers.remove_all_sync()?;
  48. }
  49. }
  50. // Check sync headers first record is the next one
  51. if let Some(next) = node.validator.blockchain.headers.get_first_sync()? {
  52. if next.height == last.0 + 1 {
  53. // Grab last sync header to continue syncing from
  54. if let Some(last_sync) = node.validator.blockchain.headers.get_last_sync()? {
  55. last = (last_sync.height, last_sync.hash());
  56. }
  57. } else {
  58. // Purge headers and start syncing from scratch
  59. node.validator.blockchain.headers.remove_all_sync()?;
  60. }
  61. }
  62. info!(target: "darkfid::task::sync_task", "Last known block: {} - {}", last.0, last.1);
  63. // Grab the most common tip and the corresponding peers
  64. let (mut common_tip_height, mut common_tip_peers) =
  65. most_common_tip(node, &last.1, checkpoint).await?;
  66. // If last known block header is before the checkpoint, we sync until that first.
  67. if let Some(checkpoint) = checkpoint {
  68. if checkpoint.0 > last.0 {
  69. info!(target: "darkfid::task::sync_task", "Syncing until configured checkpoint: {} - {}", checkpoint.0, checkpoint.1);
  70. // Retrieve all the headers backwards until our last known one and verify them.
  71. // We use the next height, in order to also retrieve the checkpoint header.
  72. retrieve_headers(node, &common_tip_peers, last.0, checkpoint.0 + 1).await?;
  73. // Retrieve all the blocks for those headers and apply them to canonical
  74. last = retrieve_blocks(node, &common_tip_peers, last, block_sub, true).await?;
  75. info!(target: "darkfid::task::sync_task", "Last received block: {} - {}", last.0, last.1);
  76. // Grab synced peers most common tip again
  77. (common_tip_height, common_tip_peers) = most_common_tip(node, &last.1, None).await?;
  78. }
  79. }
  80. // Sync headers and blocks
  81. loop {
  82. // Retrieve all the headers backwards until our last known one and verify them.
  83. // We use the next height, in order to also retrieve the peers tip header.
  84. retrieve_headers(node, &common_tip_peers, last.0, common_tip_height + 1).await?;
  85. // Retrieve all the blocks for those headers and apply them to canonical
  86. let last_received =
  87. retrieve_blocks(node, &common_tip_peers, last, block_sub, false).await?;
  88. info!(target: "darkfid::task::sync_task", "Last received block: {} - {}", last_received.0, last_received.1);
  89. if last == last_received {
  90. break
  91. }
  92. last = last_received;
  93. // Grab synced peers most common tip again
  94. (common_tip_height, common_tip_peers) = most_common_tip(node, &last.1, None).await?;
  95. }
  96. // Sync best fork
  97. sync_best_fork(node, &common_tip_peers, &last.1).await?;
  98. // Perform finalization
  99. let finalized = node.validator.finalization().await?;
  100. if !finalized.is_empty() {
  101. // Notify subscriber
  102. let mut notif_blocks = Vec::with_capacity(finalized.len());
  103. for block in finalized {
  104. notif_blocks.push(JsonValue::String(base64::encode(&serialize_async(&block).await)));
  105. }
  106. block_sub.notify(JsonValue::Array(notif_blocks)).await;
  107. }
  108. *node.validator.synced.write().await = true;
  109. info!(target: "darkfid::task::sync_task", "Blockchain synced!");
  110. Ok(())
  111. }
  112. /// Auxiliary function to block until node is connected to at least one synced peer,
  113. /// and retrieve the synced peers tips.
  114. async fn synced_peers(
  115. node: &Darkfid,
  116. last_tip: &HeaderHash,
  117. checkpoint: Option<(u32, HeaderHash)>,
  118. ) -> Result<HashMap<(u32, [u8; 32]), Vec<ChannelPtr>>> {
  119. info!(target: "darkfid::task::sync::synced_peers", "Receiving tip from peers...");
  120. let comms_timeout = node.p2p.settings().outbound_connect_timeout;
  121. let mut tips = HashMap::new();
  122. loop {
  123. // Grab channels
  124. let peers = node.p2p.hosts().channels().await;
  125. // Check anyone is connected
  126. if !peers.is_empty() {
  127. // Ask each peer if they are synced
  128. for peer in peers {
  129. // If a checkpoint was provider, we check that the peer follows that sequence
  130. if let Some(c) = checkpoint {
  131. // Communication setup
  132. let response_sub = peer.subscribe_msg::<HeaderSyncResponse>().await?;
  133. // Node creates a `HeaderSyncRequest` and sends it
  134. let request = HeaderSyncRequest { height: c.0 + 1 };
  135. peer.send(&request).await?;
  136. // Node waits for response
  137. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await
  138. else {
  139. continue
  140. };
  141. // Handle response
  142. if response.headers.is_empty() || response.headers.last().unwrap().hash() != c.1
  143. {
  144. continue
  145. }
  146. }
  147. // Communication setup
  148. let response_sub = peer.subscribe_msg::<TipResponse>().await?;
  149. // Node creates a `TipRequest` and sends it
  150. let request = TipRequest { tip: *last_tip };
  151. peer.send(&request).await?;
  152. // Node waits for response
  153. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await else {
  154. continue
  155. };
  156. // Handle response
  157. if response.synced && response.height.is_some() && response.hash.is_some() {
  158. let tip = (response.height.unwrap(), *response.hash.unwrap().inner());
  159. let Some(tip_peers) = tips.get_mut(&tip) else {
  160. tips.insert(tip, vec![peer.clone()]);
  161. continue
  162. };
  163. tip_peers.push(peer.clone());
  164. }
  165. }
  166. }
  167. // Check if we got any tips
  168. if !tips.is_empty() {
  169. break
  170. }
  171. warn!(target: "darkfid::task::sync::synced_peers", "Node is not connected to other nodes, waiting to retry...");
  172. let subscription = node.p2p.hosts().subscribe_channel().await;
  173. let _ = subscription.receive().await;
  174. subscription.unsubscribe().await;
  175. info!(target: "darkfid::task::sync::synced_peers", "Sleeping for {comms_timeout} to allow for more nodes to connect...");
  176. sleep(comms_timeout).await;
  177. }
  178. Ok(tips)
  179. }
  180. /// Auxiliary function to ask all peers for their current tip and find the most common one.
  181. async fn most_common_tip(
  182. node: &Darkfid,
  183. last_tip: &HeaderHash,
  184. checkpoint: Option<(u32, HeaderHash)>,
  185. ) -> Result<(u32, Vec<ChannelPtr>)> {
  186. // Grab synced peers tips
  187. let tips = synced_peers(node, last_tip, checkpoint).await?;
  188. // Grab the most common highest tip peers
  189. info!(target: "darkfid::task::sync::most_common_tip", "Finding most common tip...");
  190. let mut common_tip = (0, [0u8; 32], vec![]);
  191. for (tip, peers) in tips {
  192. // Check if tip peers is less than the most common tip peers
  193. if peers.len() < common_tip.2.len() {
  194. continue;
  195. }
  196. // If peers are the same length, skip if tip height is less than
  197. // the most common tip height.
  198. if peers.len() == common_tip.2.len() || tip.0 < common_tip.0 {
  199. continue;
  200. }
  201. // Keep the heighest tip with the most peers
  202. common_tip = (tip.0, tip.1, peers);
  203. }
  204. info!(target: "darkfid::task::sync::most_common_tip", "Most common tip: {} - {}", common_tip.0, HeaderHash::new(common_tip.1));
  205. Ok((common_tip.0, common_tip.2))
  206. }
  207. /// Auxiliary function to retrieve headers backwards until our last known one and verify them.
  208. async fn retrieve_headers(
  209. node: &Darkfid,
  210. peers: &[ChannelPtr],
  211. last_known: u32,
  212. tip_height: u32,
  213. ) -> Result<()> {
  214. info!(target: "darkfid::task::sync::retrieve_headers", "Retrieving missing headers from peers...");
  215. // Communication setup
  216. let mut peer_subs = vec![];
  217. for peer in peers {
  218. peer_subs.push(peer.subscribe_msg::<HeaderSyncResponse>().await?);
  219. }
  220. let comms_timeout = node.p2p.settings().outbound_connect_timeout;
  221. // We subtract 1 since tip_height is increased by one
  222. let total = tip_height - last_known - 1;
  223. let mut last_tip_height = tip_height;
  224. 'headers_loop: loop {
  225. for (index, peer) in peers.iter().enumerate() {
  226. // Node creates a `HeaderSyncRequest` and sends it
  227. let request = HeaderSyncRequest { height: last_tip_height };
  228. peer.send(&request).await?;
  229. // Node waits for response
  230. let Ok(response) = peer_subs[index].receive_with_timeout(comms_timeout).await else {
  231. continue
  232. };
  233. // Retain only the headers after our last known
  234. let mut response_headers = response.headers.to_vec();
  235. response_headers.retain(|h| h.height > last_known);
  236. if response_headers.is_empty() {
  237. break 'headers_loop
  238. }
  239. // Store the headers
  240. node.validator.blockchain.headers.insert_sync(&response_headers)?;
  241. last_tip_height = response_headers[0].height;
  242. info!(target: "darkfid::task::sync::retrieve_headers", "Headers received: {}/{}", node.validator.blockchain.headers.len_sync(), total);
  243. }
  244. }
  245. // Check if we retrieved any new headers
  246. if node.validator.blockchain.headers.is_empty_sync() {
  247. return Ok(());
  248. }
  249. // Verify headers sequence. Here we do a quick and dirty verification
  250. // of just the hashes and heights sequence. We will formaly verify
  251. // the blocks when we retrieve them. We verify them in batches,
  252. // to not load them all in memory.
  253. info!(target: "darkfid::task::sync::retrieve_headers", "Verifying headers sequence...");
  254. let mut verified_headers = 0;
  255. let total = node.validator.blockchain.headers.len_sync();
  256. // First we verify the first `BATCH` sequence, using the last known header
  257. // as the first sync header previous.
  258. let last_known = node.validator.consensus.best_fork_last_header().await?;
  259. let mut headers = node.validator.blockchain.headers.get_after_sync(0, BATCH)?;
  260. if headers[0].previous != last_known.1 || headers[0].height != last_known.0 + 1 {
  261. node.validator.blockchain.headers.remove_all_sync()?;
  262. return Err(Error::BlockIsInvalid(headers[0].hash().as_string()))
  263. }
  264. verified_headers += 1;
  265. for (index, header) in headers[1..].iter().enumerate() {
  266. if header.previous != headers[index].hash() || header.height != headers[index].height + 1 {
  267. node.validator.blockchain.headers.remove_all_sync()?;
  268. return Err(Error::BlockIsInvalid(header.hash().as_string()))
  269. }
  270. verified_headers += 1;
  271. }
  272. info!(target: "darkfid::task::sync::retrieve_headers", "Headers verified: {}/{}", verified_headers, total);
  273. // Now we verify the rest sequences
  274. let mut last_checked = headers.last().unwrap().clone();
  275. headers = node.validator.blockchain.headers.get_after_sync(last_checked.height, BATCH)?;
  276. while !headers.is_empty() {
  277. if headers[0].previous != last_checked.hash() ||
  278. headers[0].height != last_checked.height + 1
  279. {
  280. node.validator.blockchain.headers.remove_all_sync()?;
  281. return Err(Error::BlockIsInvalid(headers[0].hash().as_string()))
  282. }
  283. verified_headers += 1;
  284. for (index, header) in headers[1..].iter().enumerate() {
  285. if header.previous != headers[index].hash() ||
  286. header.height != headers[index].height + 1
  287. {
  288. node.validator.blockchain.headers.remove_all_sync()?;
  289. return Err(Error::BlockIsInvalid(header.hash().as_string()))
  290. }
  291. verified_headers += 1;
  292. }
  293. last_checked = headers.last().unwrap().clone();
  294. headers = node.validator.blockchain.headers.get_after_sync(last_checked.height, BATCH)?;
  295. info!(target: "darkfid::task::sync::retrieve_headers", "Headers verified: {}/{}", verified_headers, total);
  296. }
  297. info!(target: "darkfid::task::sync::retrieve_headers", "Headers sequence verified!");
  298. Ok(())
  299. }
  300. /// Auxiliary function to retrieve blocks of provided headers and apply them to canonical.
  301. async fn retrieve_blocks(
  302. node: &Darkfid,
  303. peers: &[ChannelPtr],
  304. last_known: (u32, HeaderHash),
  305. block_sub: &JsonSubscriber,
  306. checkpoint_blocks: bool,
  307. ) -> Result<(u32, HeaderHash)> {
  308. info!(target: "darkfid::task::sync::retrieve_blocks", "Retrieving missing blocks from peers...");
  309. let mut last_received = last_known;
  310. // Communication setup
  311. let mut peer_subs = vec![];
  312. for peer in peers {
  313. peer_subs.push(peer.subscribe_msg::<SyncResponse>().await?);
  314. }
  315. let comms_timeout = node.p2p.settings().outbound_connect_timeout;
  316. let mut received_blocks = 0;
  317. let total = node.validator.blockchain.headers.len_sync();
  318. 'blocks_loop: loop {
  319. for (index, peer) in peers.iter().enumerate() {
  320. // Grab first `BATCH` headers
  321. let headers = node.validator.blockchain.headers.get_after_sync(0, BATCH)?;
  322. if headers.is_empty() {
  323. break 'blocks_loop
  324. }
  325. let mut headers_hashes = Vec::with_capacity(headers.len());
  326. let mut synced_headers = Vec::with_capacity(headers.len());
  327. for header in &headers {
  328. headers_hashes.push(header.hash());
  329. synced_headers.push(header.height);
  330. }
  331. // Node creates a `SyncRequest` and sends it
  332. let request = SyncRequest { headers: headers_hashes.clone() };
  333. peer.send(&request).await?;
  334. // Node waits for response
  335. let Ok(response) = peer_subs[index].receive_with_timeout(comms_timeout).await else {
  336. continue
  337. };
  338. // Verify and store retrieved blocks
  339. debug!(target: "darkfid::task::sync::retrieve_blocks", "Processing received blocks");
  340. received_blocks += response.blocks.len();
  341. if checkpoint_blocks {
  342. node.validator.add_checkpoint_blocks(&response.blocks, &headers_hashes).await?;
  343. } else {
  344. for block in &response.blocks {
  345. node.validator.append_proposal(&Proposal::new(block.clone())).await?;
  346. }
  347. }
  348. last_received = (*synced_headers.last().unwrap(), *headers_hashes.last().unwrap());
  349. // Remove synced headers
  350. node.validator.blockchain.headers.remove_sync(&synced_headers)?;
  351. if checkpoint_blocks {
  352. // Notify subscriber
  353. let mut notif_blocks = Vec::with_capacity(response.blocks.len());
  354. info!(target: "darkfid::task::sync::retrieve_blocks", "Blocks added:");
  355. for (index, block) in response.blocks.iter().enumerate() {
  356. info!(target: "darkfid::task::sync::retrieve_blocks", "\t{} - {}", headers_hashes[index], headers[index].height);
  357. notif_blocks
  358. .push(JsonValue::String(base64::encode(&serialize_async(block).await)));
  359. }
  360. block_sub.notify(JsonValue::Array(notif_blocks)).await;
  361. } else {
  362. // Perform finalization for received blocks
  363. let finalized = node.validator.finalization().await?;
  364. if !finalized.is_empty() {
  365. // Notify subscriber
  366. let mut notif_blocks = Vec::with_capacity(finalized.len());
  367. for block in finalized {
  368. notif_blocks.push(JsonValue::String(base64::encode(
  369. &serialize_async(&block).await,
  370. )));
  371. }
  372. block_sub.notify(JsonValue::Array(notif_blocks)).await;
  373. }
  374. }
  375. info!(target: "darkfid::task::sync::retrieve_blocks", "Blocks received: {}/{}", received_blocks, total);
  376. }
  377. }
  378. Ok(last_received)
  379. }
  380. /// Auxiliary function to retrieve best fork state from a random peer.
  381. async fn sync_best_fork(node: &Darkfid, peers: &[ChannelPtr], last_tip: &HeaderHash) -> Result<()> {
  382. info!(target: "darkfid::task::sync::sync_best_fork", "Syncing fork states from peers...");
  383. // Getting a random peer to ask for blocks
  384. let channel = &peers.choose(&mut OsRng).unwrap();
  385. // Communication setup
  386. let response_sub = channel.subscribe_msg::<ForkSyncResponse>().await?;
  387. let notif_sub = node.subscribers.get("proposals").unwrap();
  388. // Node creates a `ForkSyncRequest` and sends it
  389. let request = ForkSyncRequest { tip: *last_tip, fork_tip: None };
  390. channel.send(&request).await?;
  391. // Node waits for response
  392. let response =
  393. response_sub.receive_with_timeout(node.p2p.settings().outbound_connect_timeout).await?;
  394. // Verify and store retrieved proposals
  395. debug!(target: "darkfid::task::sync_task", "Processing received proposals");
  396. for proposal in &response.proposals {
  397. node.validator.append_proposal(proposal).await?;
  398. // Notify subscriber
  399. let enc_prop = JsonValue::String(base64::encode(&serialize_async(proposal).await));
  400. notif_sub.notify(vec![enc_prop].into()).await;
  401. }
  402. Ok(())
  403. }