sync.rs 27 KB

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