sync.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 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 rand::{prelude::SliceRandom, rngs::OsRng};
  25. use tinyjson::JsonValue;
  26. use tracing::{debug, info, warn};
  27. use crate::{
  28. proto::{
  29. ForkSyncRequest, ForkSyncResponse, HeaderSyncRequest, HeaderSyncResponse, SyncRequest,
  30. SyncResponse, TipRequest, TipResponse, BATCH,
  31. },
  32. DarkfiNodePtr,
  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: &DarkfiNodePtr, 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, common_tip_hash, mut common_tip_peers) =
  65. most_common_tip(node, &last.1, checkpoint).await;
  66. // If the most common tip is the empty tip, we skip syncing
  67. // further and will reorg if needed when a new proposal arrives.
  68. if common_tip_hash == [0u8; 32] {
  69. *node.validator.synced.write().await = true;
  70. info!(target: "darkfid::task::sync_task", "Blockchain synced!");
  71. return Ok(())
  72. }
  73. // If last known block header is before the checkpoint, we sync until that first.
  74. if let Some(checkpoint) = checkpoint {
  75. if checkpoint.0 > last.0 {
  76. info!(target: "darkfid::task::sync_task", "Syncing until configured checkpoint: {} - {}", checkpoint.0, checkpoint.1);
  77. // Retrieve all the headers backwards until our last known one and verify them.
  78. // We use the next height, in order to also retrieve the checkpoint header.
  79. retrieve_headers(node, &common_tip_peers, last.0, checkpoint.0 + 1).await?;
  80. // Retrieve all the blocks for those headers and apply them to canonical
  81. last = retrieve_blocks(node, &common_tip_peers, last, block_sub, true).await?;
  82. info!(target: "darkfid::task::sync_task", "Last received block: {} - {}", last.0, last.1);
  83. // Grab synced peers most common tip again
  84. (common_tip_height, _, common_tip_peers) = most_common_tip(node, &last.1, None).await;
  85. }
  86. }
  87. // Sync headers and blocks
  88. loop {
  89. // Retrieve all the headers backwards until our last known one and verify them.
  90. // We use the next height, in order to also retrieve the peers tip header.
  91. retrieve_headers(node, &common_tip_peers, last.0, common_tip_height + 1).await?;
  92. // Retrieve all the blocks for those headers and apply them to canonical
  93. let last_received =
  94. retrieve_blocks(node, &common_tip_peers, last, block_sub, false).await?;
  95. info!(target: "darkfid::task::sync_task", "Last received block: {} - {}", last_received.0, last_received.1);
  96. if last == last_received {
  97. break
  98. }
  99. last = last_received;
  100. // Grab synced peers most common tip again
  101. (common_tip_height, _, common_tip_peers) = most_common_tip(node, &last.1, None).await;
  102. }
  103. // Sync best fork
  104. sync_best_fork(node, &common_tip_peers, &last.1).await;
  105. // Perform confirmation
  106. let confirmed = node.validator.confirmation().await?;
  107. if !confirmed.is_empty() {
  108. // Notify subscriber
  109. let mut notif_blocks = Vec::with_capacity(confirmed.len());
  110. for block in confirmed {
  111. notif_blocks.push(JsonValue::String(base64::encode(&serialize_async(&block).await)));
  112. }
  113. block_sub.notify(JsonValue::Array(notif_blocks)).await;
  114. }
  115. *node.validator.synced.write().await = true;
  116. info!(target: "darkfid::task::sync_task", "Blockchain synced!");
  117. Ok(())
  118. }
  119. /// Auxiliary function to block until node is connected to at least one synced peer,
  120. /// and retrieve the synced peers tips.
  121. async fn synced_peers(
  122. node: &DarkfiNodePtr,
  123. last_tip: &HeaderHash,
  124. checkpoint: Option<(u32, HeaderHash)>,
  125. ) -> HashMap<(u32, [u8; 32]), Vec<ChannelPtr>> {
  126. info!(target: "darkfid::task::sync::synced_peers", "Receiving tip from peers...");
  127. let mut tips = HashMap::new();
  128. loop {
  129. // Grab channels
  130. let peers = node.p2p_handler.p2p.hosts().channels();
  131. // Ask each peer(if we got any) if they are synced
  132. for peer in peers {
  133. let comms_timeout = node
  134. .p2p_handler
  135. .p2p
  136. .settings()
  137. .read_arc()
  138. .await
  139. .outbound_connect_timeout(peer.address().scheme());
  140. // If a checkpoint was provider, we check that the peer follows that sequence
  141. if let Some(c) = checkpoint {
  142. // Communication setup
  143. let Ok(response_sub) = peer.subscribe_msg::<HeaderSyncResponse>().await else {
  144. debug!(target: "darkfid::task::sync::synced_peers", "Failure during `HeaderSyncResponse` communication setup with peer: {peer:?}");
  145. continue
  146. };
  147. // Node creates a `HeaderSyncRequest` and sends it
  148. let request = HeaderSyncRequest { height: c.0 + 1 };
  149. if let Err(e) = peer.send(&request).await {
  150. debug!(target: "darkfid::task::sync::synced_peers", "Failure during `HeaderSyncRequest` send to peer {peer:?}: {e}");
  151. continue
  152. };
  153. // Node waits for response
  154. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await else {
  155. debug!(target: "darkfid::task::sync::synced_peers", "Timeout while waiting for `HeaderSyncResponse` from peer: {peer:?}");
  156. continue
  157. };
  158. // Handle response
  159. if response.headers.is_empty() || response.headers.last().unwrap().hash() != c.1 {
  160. debug!(target: "darkfid::task::sync::synced_peers", "Invalid `HeaderSyncResponse` from peer: {peer:?}");
  161. continue
  162. }
  163. }
  164. // Communication setup
  165. let Ok(response_sub) = peer.subscribe_msg::<TipResponse>().await else {
  166. debug!(target: "darkfid::task::sync::synced_peers", "Failure during `TipResponse` communication setup with peer: {peer:?}");
  167. continue
  168. };
  169. // Node creates a `TipRequest` and sends it
  170. let request = TipRequest { tip: *last_tip };
  171. if let Err(e) = peer.send(&request).await {
  172. debug!(target: "darkfid::task::sync::synced_peers", "Failure during `TipRequest` send to peer {peer:?}: {e}");
  173. continue
  174. };
  175. // Node waits for response
  176. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await else {
  177. debug!(target: "darkfid::task::sync::synced_peers", "Timeout while waiting for `TipResponse` from peer: {peer:?}");
  178. continue
  179. };
  180. // Handle response
  181. if response.synced {
  182. // Grab response tip
  183. let tip = if response.height.is_some() && response.hash.is_some() {
  184. (response.height.unwrap(), *response.hash.unwrap().inner())
  185. } else {
  186. // Empty response while synced means the peer is on an
  187. // entirely different chain/fork, so we keep track of
  188. // them in the empty tip reference.
  189. (0, [0u8; 32])
  190. };
  191. let Some(tip_peers) = tips.get_mut(&tip) else {
  192. tips.insert(tip, vec![peer.clone()]);
  193. continue
  194. };
  195. tip_peers.push(peer.clone());
  196. }
  197. }
  198. // Check if we got any tips
  199. if !tips.is_empty() {
  200. break
  201. }
  202. warn!(target: "darkfid::task::sync::synced_peers", "Node is not connected to other synced nodes, waiting to retry...");
  203. let subscription = node.p2p_handler.p2p.hosts().subscribe_channel().await;
  204. let _ = subscription.receive().await;
  205. subscription.unsubscribe().await;
  206. let comms_timeout =
  207. node.p2p_handler.p2p.settings().read_arc().await.outbound_connect_timeout_max();
  208. info!(target: "darkfid::task::sync::synced_peers", "Sleeping for {comms_timeout} to allow for more nodes to connect...");
  209. sleep(comms_timeout).await;
  210. }
  211. tips
  212. }
  213. /// Auxiliary function to ask all peers for their current tip and find the most common one.
  214. async fn most_common_tip(
  215. node: &DarkfiNodePtr,
  216. last_tip: &HeaderHash,
  217. checkpoint: Option<(u32, HeaderHash)>,
  218. ) -> (u32, [u8; 32], Vec<ChannelPtr>) {
  219. // Grab synced peers tips
  220. let tips = synced_peers(node, last_tip, checkpoint).await;
  221. // Grab the most common highest tip peers
  222. info!(target: "darkfid::task::sync::most_common_tip", "Finding most common tip...");
  223. let mut common_tip = (0, [0u8; 32], vec![]);
  224. for (tip, peers) in tips {
  225. // Check if tip peers is less than the most common tip peers
  226. if peers.len() < common_tip.2.len() {
  227. continue;
  228. }
  229. // If peers are the same length, skip if tip height is less than
  230. // the most common tip height.
  231. if peers.len() == common_tip.2.len() || tip.0 < common_tip.0 {
  232. continue;
  233. }
  234. // Keep the heighest tip with the most peers
  235. common_tip = (tip.0, tip.1, peers);
  236. }
  237. info!(target: "darkfid::task::sync::most_common_tip", "Most common tip: {} - {}", common_tip.0, HeaderHash::new(common_tip.1));
  238. common_tip
  239. }
  240. /// Auxiliary function to retrieve headers backwards until our last known one and verify them.
  241. async fn retrieve_headers(
  242. node: &DarkfiNodePtr,
  243. peers: &[ChannelPtr],
  244. last_known: u32,
  245. tip_height: u32,
  246. ) -> Result<()> {
  247. info!(target: "darkfid::task::sync::retrieve_headers", "Retrieving missing headers from peers...");
  248. // Communication setup
  249. let mut peer_subs = vec![];
  250. for peer in peers {
  251. match peer.subscribe_msg::<HeaderSyncResponse>().await {
  252. Ok(response_sub) => peer_subs.push((Some(response_sub), false)),
  253. Err(e) => {
  254. debug!(target: "darkfid::task::sync::retrieve_headers", "Failure during `HeaderSyncResponse` communication setup with peer {peer:?}: {e}");
  255. peer_subs.push((None, true))
  256. }
  257. }
  258. }
  259. // We subtract 1 since tip_height is increased by one
  260. let total = tip_height - last_known - 1;
  261. let mut last_tip_height = tip_height;
  262. 'headers_loop: loop {
  263. // Check if all our peers are failing
  264. let mut count = 0;
  265. for (peer_sub, failed) in &peer_subs {
  266. if peer_sub.is_none() || *failed {
  267. count += 1;
  268. }
  269. }
  270. if count == peer_subs.len() {
  271. debug!(target: "darkfid::task::sync::retrieve_headers", "All peer connections failed.");
  272. break
  273. }
  274. for (index, peer) in peers.iter().enumerate() {
  275. // Grab the response sub reference
  276. let (peer_sub, failed) = &mut peer_subs[index];
  277. if *failed {
  278. continue;
  279. }
  280. let Some(ref response_sub) = peer_sub else {
  281. continue;
  282. };
  283. // Node creates a `HeaderSyncRequest` and sends it
  284. let request = HeaderSyncRequest { height: last_tip_height };
  285. if let Err(e) = peer.send(&request).await {
  286. debug!(target: "darkfid::task::sync::retrieve_headers", "Failure during `HeaderSyncRequest` send to peer {peer:?}: {e}");
  287. *failed = true;
  288. continue
  289. };
  290. let comms_timeout = node
  291. .p2p_handler
  292. .p2p
  293. .settings()
  294. .read_arc()
  295. .await
  296. .outbound_connect_timeout(peer.address().scheme());
  297. // Node waits for response
  298. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await else {
  299. debug!(target: "darkfid::task::sync::retrieve_headers", "Timeout while waiting for `HeaderSyncResponse` from peer: {peer:?}");
  300. *failed = true;
  301. continue
  302. };
  303. // Retain only the headers after our last known
  304. let mut response_headers = response.headers.to_vec();
  305. response_headers.retain(|h| h.height > last_known);
  306. if response_headers.is_empty() {
  307. break 'headers_loop
  308. }
  309. // Store the headers
  310. node.validator.blockchain.headers.insert_sync(&response_headers)?;
  311. last_tip_height = response_headers[0].height;
  312. info!(target: "darkfid::task::sync::retrieve_headers", "Headers received: {}/{total}", node.validator.blockchain.headers.len_sync());
  313. }
  314. }
  315. // Check if we retrieved any new headers
  316. if node.validator.blockchain.headers.is_empty_sync() {
  317. return Ok(());
  318. }
  319. // Verify headers sequence. Here we do a quick and dirty verification
  320. // of just the hashes and heights sequence. We will formaly verify
  321. // the blocks when we retrieve them. We verify them in batches,
  322. // to not load them all in memory.
  323. info!(target: "darkfid::task::sync::retrieve_headers", "Verifying headers sequence...");
  324. let mut verified_headers = 0;
  325. let total = node.validator.blockchain.headers.len_sync();
  326. // First we verify the first `BATCH` sequence, using the last known header
  327. // as the first sync header previous.
  328. let last_known = node.validator.consensus.best_fork_last_header().await?;
  329. let mut headers = node.validator.blockchain.headers.get_after_sync(0, BATCH)?;
  330. if headers[0].previous != last_known.1 || headers[0].height != last_known.0 + 1 {
  331. node.validator.blockchain.headers.remove_all_sync()?;
  332. return Err(Error::BlockIsInvalid(headers[0].hash().as_string()))
  333. }
  334. verified_headers += 1;
  335. for (index, header) in headers[1..].iter().enumerate() {
  336. if header.previous != headers[index].hash() || header.height != headers[index].height + 1 {
  337. node.validator.blockchain.headers.remove_all_sync()?;
  338. return Err(Error::BlockIsInvalid(header.hash().as_string()))
  339. }
  340. verified_headers += 1;
  341. }
  342. info!(target: "darkfid::task::sync::retrieve_headers", "Headers verified: {verified_headers}/{total}");
  343. // Now we verify the rest sequences
  344. let mut last_checked = headers.last().unwrap().clone();
  345. headers = node.validator.blockchain.headers.get_after_sync(last_checked.height, BATCH)?;
  346. while !headers.is_empty() {
  347. if headers[0].previous != last_checked.hash() ||
  348. headers[0].height != last_checked.height + 1
  349. {
  350. node.validator.blockchain.headers.remove_all_sync()?;
  351. return Err(Error::BlockIsInvalid(headers[0].hash().as_string()))
  352. }
  353. verified_headers += 1;
  354. for (index, header) in headers[1..].iter().enumerate() {
  355. if header.previous != headers[index].hash() ||
  356. header.height != headers[index].height + 1
  357. {
  358. node.validator.blockchain.headers.remove_all_sync()?;
  359. return Err(Error::BlockIsInvalid(header.hash().as_string()))
  360. }
  361. verified_headers += 1;
  362. }
  363. last_checked = headers.last().unwrap().clone();
  364. headers = node.validator.blockchain.headers.get_after_sync(last_checked.height, BATCH)?;
  365. info!(target: "darkfid::task::sync::retrieve_headers", "Headers verified: {verified_headers}/{total}");
  366. }
  367. info!(target: "darkfid::task::sync::retrieve_headers", "Headers sequence verified!");
  368. Ok(())
  369. }
  370. /// Auxiliary function to retrieve blocks of provided headers and apply them to canonical.
  371. async fn retrieve_blocks(
  372. node: &DarkfiNodePtr,
  373. peers: &[ChannelPtr],
  374. last_known: (u32, HeaderHash),
  375. block_sub: &JsonSubscriber,
  376. checkpoint_blocks: bool,
  377. ) -> Result<(u32, HeaderHash)> {
  378. info!(target: "darkfid::task::sync::retrieve_blocks", "Retrieving missing blocks from peers...");
  379. let mut last_received = last_known;
  380. // Communication setup
  381. let mut peer_subs = vec![];
  382. for peer in peers {
  383. match peer.subscribe_msg::<SyncResponse>().await {
  384. Ok(response_sub) => peer_subs.push((Some(response_sub), false)),
  385. Err(e) => {
  386. debug!(target: "darkfid::task::sync::retrieve_blocks", "Failure during `SyncResponse` communication setup with peer {peer:?}: {e}");
  387. peer_subs.push((None, true))
  388. }
  389. }
  390. }
  391. let mut received_blocks = 0;
  392. let total = node.validator.blockchain.headers.len_sync();
  393. 'blocks_loop: loop {
  394. // Check if all our peers are failing
  395. let mut count = 0;
  396. for (peer_sub, failed) in &peer_subs {
  397. if peer_sub.is_none() || *failed {
  398. count += 1;
  399. }
  400. }
  401. if count == peer_subs.len() {
  402. debug!(target: "darkfid::task::sync::retrieve_blocks", "All peer connections failed.");
  403. break
  404. }
  405. 'peers_loop: for (index, peer) in peers.iter().enumerate() {
  406. // Grab the response sub reference
  407. let (peer_sub, failed) = &mut peer_subs[index];
  408. if *failed {
  409. continue;
  410. }
  411. let Some(ref response_sub) = peer_sub else {
  412. continue;
  413. };
  414. // Grab first `BATCH` headers
  415. let headers = node.validator.blockchain.headers.get_after_sync(0, BATCH)?;
  416. if headers.is_empty() {
  417. break 'blocks_loop
  418. }
  419. let mut headers_hashes = Vec::with_capacity(headers.len());
  420. let mut synced_headers = Vec::with_capacity(headers.len());
  421. for header in &headers {
  422. headers_hashes.push(header.hash());
  423. synced_headers.push(header.height);
  424. }
  425. // Node creates a `SyncRequest` and sends it
  426. let request = SyncRequest { headers: headers_hashes.clone() };
  427. if let Err(e) = peer.send(&request).await {
  428. debug!(target: "darkfid::task::sync::retrieve_blocks", "Failure during `SyncRequest` send to peer {peer:?}: {e}");
  429. *failed = true;
  430. continue
  431. };
  432. let comms_timeout = node
  433. .p2p_handler
  434. .p2p
  435. .settings()
  436. .read_arc()
  437. .await
  438. .outbound_connect_timeout(peer.address().scheme());
  439. // Node waits for response
  440. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await else {
  441. debug!(target: "darkfid::task::sync::retrieve_blocks", "Timeout while waiting for `SyncResponse` from peer: {peer:?}");
  442. *failed = true;
  443. continue
  444. };
  445. // Verify and store retrieved blocks
  446. debug!(target: "darkfid::task::sync::retrieve_blocks", "Processing received blocks");
  447. received_blocks += response.blocks.len();
  448. if checkpoint_blocks {
  449. if let Err(e) =
  450. node.validator.add_checkpoint_blocks(&response.blocks, &headers_hashes).await
  451. {
  452. debug!(target: "darkfid::task::sync::retrieve_blocks", "Error while adding checkpoint blocks: {e}");
  453. continue
  454. };
  455. } else {
  456. for block in &response.blocks {
  457. if let Err(e) =
  458. node.validator.append_proposal(&Proposal::new(block.clone())).await
  459. {
  460. debug!(target: "darkfid::task::sync::retrieve_blocks", "Error while appending proposal: {e}");
  461. continue 'peers_loop
  462. };
  463. }
  464. }
  465. last_received = (*synced_headers.last().unwrap(), *headers_hashes.last().unwrap());
  466. // Remove synced headers
  467. node.validator.blockchain.headers.remove_sync(&synced_headers)?;
  468. if checkpoint_blocks {
  469. // Notify subscriber
  470. let mut notif_blocks = Vec::with_capacity(response.blocks.len());
  471. info!(target: "darkfid::task::sync::retrieve_blocks", "Blocks added:");
  472. for (index, block) in response.blocks.iter().enumerate() {
  473. info!(target: "darkfid::task::sync::retrieve_blocks", "\t{} - {}", headers_hashes[index], headers[index].height);
  474. notif_blocks
  475. .push(JsonValue::String(base64::encode(&serialize_async(block).await)));
  476. }
  477. block_sub.notify(JsonValue::Array(notif_blocks)).await;
  478. } else {
  479. // Perform confirmation for received blocks
  480. let confirmed = node.validator.confirmation().await?;
  481. if !confirmed.is_empty() {
  482. // Notify subscriber
  483. let mut notif_blocks = Vec::with_capacity(confirmed.len());
  484. for block in confirmed {
  485. notif_blocks.push(JsonValue::String(base64::encode(
  486. &serialize_async(&block).await,
  487. )));
  488. }
  489. block_sub.notify(JsonValue::Array(notif_blocks)).await;
  490. }
  491. }
  492. info!(target: "darkfid::task::sync::retrieve_blocks", "Blocks received: {received_blocks}/{total}");
  493. }
  494. }
  495. Ok(last_received)
  496. }
  497. /// Auxiliary function to retrieve best fork state from a random peer.
  498. async fn sync_best_fork(node: &DarkfiNodePtr, peers: &[ChannelPtr], last_tip: &HeaderHash) {
  499. info!(target: "darkfid::task::sync::sync_best_fork", "Syncing fork states from peers...");
  500. // Getting a random peer to ask for blocks
  501. let peer = &peers.choose(&mut OsRng).unwrap();
  502. // Communication setup
  503. let Ok(response_sub) = peer.subscribe_msg::<ForkSyncResponse>().await else {
  504. debug!(target: "darkfid::task::sync::sync_best_fork", "Failure during `ForkSyncResponse` communication setup with peer: {peer:?}");
  505. return
  506. };
  507. let notif_sub = node.subscribers.get("proposals").unwrap();
  508. // Node creates a `ForkSyncRequest` and sends it
  509. let request = ForkSyncRequest { tip: *last_tip, fork_tip: None };
  510. if let Err(e) = peer.send(&request).await {
  511. debug!(target: "darkfid::task::sync::sync_best_fork", "Failure during `ForkSyncRequest` send to peer {peer:?}: {e}");
  512. return
  513. };
  514. let comms_timeout = node
  515. .p2p_handler
  516. .p2p
  517. .settings()
  518. .read_arc()
  519. .await
  520. .outbound_connect_timeout(peer.address().scheme());
  521. // Node waits for response
  522. let Ok(response) = response_sub.receive_with_timeout(comms_timeout).await else {
  523. debug!(target: "darkfid::task::sync::sync_best_fork", "Timeout while waiting for `ForkSyncResponse` from peer: {peer:?}");
  524. return
  525. };
  526. // Verify and store retrieved proposals
  527. debug!(target: "darkfid::task::sync::sync_best_fork", "Processing received proposals");
  528. for proposal in &response.proposals {
  529. if let Err(e) = node.validator.append_proposal(proposal).await {
  530. debug!(target: "darkfid::task::sync::sync_best_fork", "Error while appending proposal: {e}");
  531. return
  532. };
  533. // Notify subscriber
  534. let enc_prop = JsonValue::String(base64::encode(&serialize_async(proposal).await));
  535. notif_sub.notify(vec![enc_prop].into()).await;
  536. }
  537. }