consensus_sync.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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 log::{info, warn};
  19. use crate::{
  20. consensus::{
  21. state::{
  22. ConsensusRequest, ConsensusResponse, ConsensusSlotCheckpointsRequest,
  23. ConsensusSlotCheckpointsResponse,
  24. },
  25. ValidatorStatePtr,
  26. },
  27. net::P2pPtr,
  28. util::async_util::sleep,
  29. Result,
  30. };
  31. /// async task used for consensus state syncing.
  32. /// Returns flag if node is not connected to other peers or consensus hasn't started,
  33. /// so it can immediately start proposing proposals.
  34. pub async fn consensus_sync_task(p2p: P2pPtr, state: ValidatorStatePtr) -> Result<bool> {
  35. info!(target: "consensus::consensus_sync", "Starting consensus state sync...");
  36. let current_slot = state.read().await.consensus.current_slot();
  37. // Loop through connected channels
  38. let channels_map = p2p.channels().lock().await;
  39. let values = channels_map.values();
  40. // Using len here because is_empty() uses unstable library feature
  41. // called 'exact_size_is_empty'.
  42. if values.len() == 0 {
  43. warn!(target: "consensus::consensus_sync", "Node is not connected to other nodes");
  44. let mut lock = state.write().await;
  45. lock.consensus.bootstrap_slot = current_slot;
  46. lock.consensus.init_coins().await?;
  47. info!(target: "consensus::consensus_sync", "Consensus state synced!");
  48. return Ok(true)
  49. }
  50. // Node iterates the channel peers to check if at least on peer has seen slot checkpoints
  51. let mut peer = None;
  52. for channel in values {
  53. // Communication setup
  54. let msg_subsystem = channel.get_message_subsystem();
  55. msg_subsystem.add_dispatch::<ConsensusSlotCheckpointsResponse>().await;
  56. let response_sub = channel.subscribe_msg::<ConsensusSlotCheckpointsResponse>().await?;
  57. // Node creates a `ConsensusSlotCheckpointsRequest` and sends it
  58. let request = ConsensusSlotCheckpointsRequest {};
  59. channel.send(request).await?;
  60. // Node checks response
  61. let response = response_sub.receive().await?;
  62. if response.bootstrap_slot == current_slot {
  63. warn!(target: "consensus::consensus_sync", "Network was just bootstraped, checking rest nodes");
  64. continue
  65. }
  66. if response.is_empty {
  67. warn!(target: "consensus::consensus_sync", "Node has not seen any slot checkpoints, retrying...");
  68. continue
  69. }
  70. // Keep peer to ask for consensus state
  71. peer = Some(channel.clone());
  72. break
  73. }
  74. // Release channels lock
  75. drop(channels_map);
  76. // If no peer knows about any slot checkpoints, that means that the network was bootstrapped or restarted
  77. // and no node has started consensus.
  78. if peer.is_none() {
  79. warn!(target: "consensus::consensus_sync", "No node that has seen any slot checkpoints was found, or network was just boostrapped.");
  80. let mut lock = state.write().await;
  81. lock.consensus.bootstrap_slot = current_slot;
  82. lock.consensus.init_coins().await?;
  83. info!(target: "consensus::consensus_sync", "Consensus state synced!");
  84. return Ok(true)
  85. }
  86. let peer = peer.unwrap();
  87. // Listen for next finalization
  88. info!(target: "consensus::consensus_sync", "Waiting for next finalization...");
  89. let subscriber = state.read().await.subscribers.get("blocks").unwrap().clone();
  90. let subscription = subscriber.subscribe().await;
  91. subscription.receive().await;
  92. subscription.unsubscribe().await;
  93. // After finalization occurs, sync our consensus state.
  94. // This ensures that the received state always consists of 1 fork with one proposal.
  95. info!(target: "consensus::consensus_sync", "Finalization signal received, requesting consensus state...");
  96. // Communication setup
  97. let msg_subsystem = peer.get_message_subsystem();
  98. msg_subsystem.add_dispatch::<ConsensusResponse>().await;
  99. let response_sub = peer.subscribe_msg::<ConsensusResponse>().await?;
  100. // Node creates a `ConsensusRequest` and sends it
  101. peer.send(ConsensusRequest {}).await?;
  102. // Node verifies response came from a participating node.
  103. // Extra validations can be added here.
  104. let mut response = response_sub.receive().await?;
  105. // Verify that peer has finished finalizing forks
  106. loop {
  107. if response.forks.len() != 1 || response.forks[0].sequence.len() != 1 {
  108. warn!(target: "consensus::consensus_sync", "Peer has not finished finalization, retrying...");
  109. sleep(1).await;
  110. peer.send(ConsensusRequest {}).await?;
  111. response = response_sub.receive().await?;
  112. continue
  113. }
  114. break
  115. }
  116. // Verify that the node has received all finalized blocks
  117. let last_finalized_slot = response.forks[0].sequence[0].proposal.block.header.slot - 1;
  118. loop {
  119. if !state.read().await.blockchain.has_slot(last_finalized_slot)? {
  120. warn!(target: "consensus::consensus_sync", "Node has not finished finalization, retrying...");
  121. sleep(1).await;
  122. continue
  123. }
  124. break
  125. }
  126. // Node stores response data.
  127. let mut lock = state.write().await;
  128. let mut forks = vec![];
  129. for fork in &response.forks {
  130. forks.push(fork.clone().into());
  131. }
  132. lock.consensus.bootstrap_slot = response.bootstrap_slot;
  133. lock.consensus.forks = forks;
  134. lock.unconfirmed_txs = response.unconfirmed_txs.clone();
  135. lock.consensus.slot_checkpoints = response.slot_checkpoints.clone();
  136. lock.consensus.leaders_history = response.leaders_history.clone();
  137. lock.consensus.nullifiers = response.nullifiers.clone();
  138. lock.consensus.init_coins().await?;
  139. info!(target: "consensus::consensus_sync", "Consensus state synced!");
  140. Ok(false)
  141. }