consensus.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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 darkfi_sdk::{
  19. blockchain::{expected_reward, PidOutput, PreviousSlot, Slot},
  20. crypto::{schnorr::SchnorrSecret, SecretKey},
  21. pasta::{group::ff::PrimeField, pallas},
  22. };
  23. use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
  24. use log::{error, info, warn};
  25. use rand::rngs::OsRng;
  26. use crate::{
  27. blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header},
  28. tx::Transaction,
  29. util::time::{TimeKeeper, Timestamp},
  30. validator::{
  31. pid::slot_pid_output, pow::PoWModule, utils::block_rank, verify_block, verify_transactions,
  32. },
  33. Error, Result,
  34. };
  35. /// Consensus configuration
  36. const TXS_CAP: usize = 50;
  37. /// This struct represents the information required by the consensus algorithm
  38. pub struct Consensus {
  39. /// Canonical (finalized) blockchain
  40. pub blockchain: Blockchain,
  41. /// Helper structure to calculate time related operations
  42. pub time_keeper: TimeKeeper,
  43. /// Currently configured PoW target
  44. pub pow_target: Option<usize>,
  45. /// Node is participating to consensus
  46. pub participating: bool,
  47. /// Last slot node check for finalization
  48. pub checked_finalization: u64,
  49. /// Fork chains containing block proposals
  50. pub forks: Vec<Fork>,
  51. // TODO: remove this once everything goes through forks
  52. /// Canonical blockchain PoW module state
  53. pub module: PoWModule,
  54. /// Flag to enable testing mode
  55. pub testing_mode: bool,
  56. }
  57. impl Consensus {
  58. /// Generate a new Consensus state.
  59. pub fn new(
  60. blockchain: Blockchain,
  61. time_keeper: TimeKeeper,
  62. pow_target: Option<usize>,
  63. testing_mode: bool,
  64. ) -> Self {
  65. let module = PoWModule::new(blockchain.clone(), None, pow_target);
  66. Self {
  67. blockchain,
  68. time_keeper,
  69. pow_target,
  70. participating: false,
  71. checked_finalization: 0,
  72. forks: vec![],
  73. module,
  74. testing_mode,
  75. }
  76. }
  77. /// Generate current hot/live slot for all current forks.
  78. pub fn generate_slot(&mut self) -> Result<()> {
  79. // Grab current slot id
  80. let id = self.time_keeper.current_slot();
  81. // If no forks exist, create a new one as a basis to extend
  82. if self.forks.is_empty() {
  83. self.forks.push(Fork::new(&self.blockchain, self.pow_target)?);
  84. }
  85. // Grab previous slot information
  86. let (producers, last_hashes, second_to_last_hashes) = self.previous_slot_info(id - 1)?;
  87. for fork in self.forks.iter_mut() {
  88. fork.generate_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
  89. }
  90. Ok(())
  91. }
  92. /// Retrieve previous slot producers, last proposal hashes,
  93. /// and their second to last hashes, from all current forks.
  94. fn previous_slot_info(&self, slot: u64) -> Result<(u64, Vec<blake3::Hash>, Vec<blake3::Hash>)> {
  95. let mut producers = 0;
  96. let mut last_hashes = vec![];
  97. let mut second_to_last_hashes = vec![];
  98. for fork in &self.forks {
  99. let last_proposal = fork.last_proposal()?;
  100. if last_proposal.block.header.height == slot {
  101. producers += 1;
  102. }
  103. last_hashes.push(last_proposal.hash);
  104. second_to_last_hashes.push(last_proposal.block.header.previous);
  105. }
  106. Ok((producers, last_hashes, second_to_last_hashes))
  107. }
  108. /// Generate a block proposal for the current hot/live(last) slot,
  109. /// containing all pending transactions. Proposal extends the longest fork
  110. /// chain the node is holding. This should only be called after
  111. /// generate_slot(). Proposal is signed using provided secret key, which
  112. /// must also have signed the provided proposal transaction.
  113. pub async fn generate_proposal(
  114. &self,
  115. secret_key: SecretKey,
  116. proposal_tx: Transaction,
  117. ) -> Result<Proposal> {
  118. // Generate a time keeper for current slot
  119. let time_keeper = self.time_keeper.current();
  120. // Retrieve longest known fork
  121. let mut fork_index = 0;
  122. let mut max_fork_length = 0;
  123. for (index, fork) in self.forks.iter().enumerate() {
  124. if fork.proposals.len() > max_fork_length {
  125. fork_index = index;
  126. max_fork_length = fork.proposals.len();
  127. }
  128. }
  129. let fork = &self.forks[fork_index];
  130. // Grab forks' unproposed transactions
  131. let mut unproposed_txs = fork.unproposed_txs(&self.blockchain, &time_keeper).await?;
  132. unproposed_txs.push(proposal_tx);
  133. // Grab forks' last block proposal(previous)
  134. let previous = fork.last_proposal()?;
  135. // Generate the new header
  136. let slot = fork.slots.last().unwrap();
  137. // TODO: verify if header timestamp should be blockchain or system timestamp
  138. let header = Header::new(
  139. previous.block.hash()?,
  140. time_keeper.slot_epoch(slot.id),
  141. slot.id,
  142. Timestamp::current_time(),
  143. slot.last_nonce,
  144. );
  145. // Generate the block
  146. let mut block = BlockInfo::new_empty(header, fork.slots.clone());
  147. // Add transactions to the block
  148. block.append_txs(unproposed_txs)?;
  149. // TODO: sign more stuff?
  150. // Sign block header using provided secret key
  151. block.signature = secret_key.sign(&mut OsRng, &block.header.hash()?.as_bytes()[..]);
  152. // Generate the block proposal from the block
  153. Proposal::new(block)
  154. }
  155. /// Given a proposal, the node verifys it and finds which fork it extends.
  156. /// If the proposal extends the canonical blockchain, a new fork chain is created.
  157. /// A proposal is considered valid when the following rules apply:
  158. /// 1. Node has not started current slot finalization
  159. /// 2. Proposal refers to current slot
  160. /// 3. Proposal hash matches the actual block one
  161. /// 4. Block transactions don't exceed set limit
  162. /// 5. If proposal extends a known fork, verify block slots
  163. /// correspond to the fork hot/live ones
  164. /// 6. Block is valid
  165. /// Additional validity rules can be applied.
  166. pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
  167. // Generate a time keeper for current slot
  168. let time_keeper = self.time_keeper.current();
  169. // Node have already checked for finalization in this slot (1)
  170. if time_keeper.verifying_slot <= self.checked_finalization {
  171. warn!(target: "validator::consensus::append_proposal", "Proposal received after finalization sync period.");
  172. return Err(Error::ProposalAfterFinalizationError)
  173. }
  174. // Proposal validations
  175. let hdr = &proposal.block.header;
  176. // Ignore proposal if not for current slot (2)
  177. if hdr.height != time_keeper.verifying_slot {
  178. return Err(Error::ProposalNotForCurrentSlotError)
  179. }
  180. // Check if proposal hash matches actual one (3)
  181. let proposal_hash = proposal.block.hash()?;
  182. if proposal.hash != proposal_hash {
  183. warn!(
  184. target: "validator::consensus::append_proposal", "Received proposal contains mismatched hashes: {} - {}",
  185. proposal.hash, proposal_hash
  186. );
  187. return Err(Error::ProposalHashesMissmatchError)
  188. }
  189. // TODO: verify if this should happen here or not.
  190. // Check that proposal transactions don't exceed limit (4)
  191. if proposal.block.txs.len() > TXS_CAP {
  192. warn!(
  193. target: "validator::consensus::append_proposal", "Received proposal transactions exceed configured cap: {} - {}",
  194. proposal.block.txs.len(),
  195. TXS_CAP
  196. );
  197. return Err(Error::ProposalTxsExceedCapError)
  198. }
  199. // Check if proposal extends any existing forks
  200. let (mut fork, index) = self.find_extended_fork(proposal).await?;
  201. // Verify block slots correspond to the forks' hot/live ones (5)
  202. if !fork.slots.is_empty() && fork.slots != proposal.block.slots {
  203. return Err(Error::ProposalContainsUnknownSlots)
  204. }
  205. // Insert last block slot so transactions can be validated against.
  206. // Rest (empty) slots will be inserted along with the block.
  207. // Since this fork uses an overlay clone, original overlay is not affected.
  208. fork.overlay.lock().unwrap().slots.insert(&[proposal
  209. .block
  210. .slots
  211. .last()
  212. .unwrap()
  213. .clone()])?;
  214. // Grab overlay last block
  215. let previous = fork.overlay.lock().unwrap().last_block()?;
  216. // Retrieve expected reward
  217. let expected_reward = expected_reward(time_keeper.verifying_slot);
  218. // Verify proposal block (6)
  219. if verify_block(
  220. &fork.overlay,
  221. &time_keeper,
  222. &fork.module,
  223. &proposal.block,
  224. &previous,
  225. expected_reward,
  226. self.testing_mode,
  227. )
  228. .await
  229. .is_err()
  230. {
  231. error!(target: "validator::consensus::append_proposal", "Erroneous proposal block found");
  232. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  233. return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
  234. };
  235. // Update PoW module
  236. if proposal.block.header.version == 1 {
  237. fork.module.append(proposal.block.header.timestamp.0, &fork.module.next_difficulty());
  238. }
  239. // If a fork index was found, replace forks with the mutated one,
  240. // otherwise push the new fork.
  241. fork.proposals.push(proposal.hash);
  242. fork.slots = vec![];
  243. match index {
  244. Some(i) => {
  245. self.forks[i] = fork;
  246. }
  247. None => {
  248. self.forks.push(fork);
  249. }
  250. }
  251. Ok(())
  252. }
  253. /// Auxiliary function to find current best ranked fork.
  254. pub fn best_fork_index(&self) -> Result<usize> {
  255. // Check if node has any forks
  256. if self.forks.is_empty() {
  257. return Err(Error::ForksNotFound)
  258. }
  259. // Find the best ranked fork
  260. let mut best = 0;
  261. let mut index = 0;
  262. for (f_index, fork) in self.forks.iter().enumerate() {
  263. let rank = fork.rank()?;
  264. if rank <= best {
  265. continue
  266. }
  267. best = rank;
  268. index = f_index;
  269. }
  270. Ok(index)
  271. }
  272. /// Given a proposal, find the index of the fork chain it extends, along with the specific
  273. /// extended proposal index.
  274. fn find_extended_fork_index(&self, proposal: &Proposal) -> Result<(usize, usize)> {
  275. for (f_index, fork) in self.forks.iter().enumerate() {
  276. // Traverse fork proposals sequence in reverse
  277. for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
  278. if &proposal.block.header.previous == p_hash {
  279. return Ok((f_index, p_index))
  280. }
  281. }
  282. }
  283. Err(Error::ExtendedChainIndexNotFound)
  284. }
  285. /// Given a proposal, find the fork chain it extends, and return its full clone.
  286. /// If the proposal extends the fork not on its tail, a new fork is created and
  287. /// we re-apply the proposals up to the extending one. If proposal extends canonical,
  288. /// a new fork is created. Additionally, we return the fork index if a new fork
  289. /// was not created, so caller can replace the fork.
  290. async fn find_extended_fork(&self, proposal: &Proposal) -> Result<(Fork, Option<usize>)> {
  291. // Check if proposal extends any fork
  292. let found = self.find_extended_fork_index(proposal);
  293. if found.is_err() {
  294. // Check if we extend canonical
  295. let (last_slot, last_block) = self.blockchain.last()?;
  296. if proposal.block.header.previous != last_block ||
  297. proposal.block.header.height <= last_slot
  298. {
  299. return Err(Error::ExtendedChainIndexNotFound)
  300. }
  301. return Ok((Fork::new(&self.blockchain, self.pow_target)?, None))
  302. }
  303. let (f_index, p_index) = found.unwrap();
  304. let original_fork = &self.forks[f_index];
  305. // Check if proposal extends fork at last proposal
  306. if p_index == (original_fork.proposals.len() - 1) {
  307. return Ok((original_fork.full_clone()?, Some(f_index)))
  308. }
  309. // Rebuild fork
  310. let mut fork = Fork::new(&self.blockchain, self.pow_target)?;
  311. fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
  312. // Retrieve proposals blocks from original fork
  313. let blocks = &original_fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  314. // Retrieve last block
  315. let mut previous = &fork.overlay.lock().unwrap().last_block()?;
  316. // Create a time keeper to validate each proposal block
  317. let mut time_keeper = self.time_keeper.clone();
  318. // Validate and insert each block
  319. for block in blocks {
  320. // Use block slot in time keeper
  321. time_keeper.verifying_slot = block.header.height;
  322. // Retrieve expected reward
  323. let expected_reward = expected_reward(time_keeper.verifying_slot);
  324. // Verify block
  325. if verify_block(
  326. &fork.overlay,
  327. &time_keeper,
  328. &fork.module,
  329. block,
  330. previous,
  331. expected_reward,
  332. self.testing_mode,
  333. )
  334. .await
  335. .is_err()
  336. {
  337. error!(target: "validator::consensus::find_extended_fork_overlay", "Erroneous block found in set");
  338. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  339. return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
  340. };
  341. // Update PoW module
  342. if block.header.version == 1 {
  343. fork.module.append(block.header.timestamp.0, &fork.module.next_difficulty());
  344. }
  345. // Use last inserted block as next iteration previous
  346. previous = block;
  347. }
  348. Ok((fork, None))
  349. }
  350. /// Node checks if any of the forks can be finalized.
  351. /// Consensus finalization logic:
  352. /// - If the node has observed the creation of a fork and no other forks exists at same or greater height,
  353. /// all proposals in that fork can be finalized (append to canonical blockchain).
  354. /// When a fork can be finalized, blocks(proposals) should be appended to canonical,
  355. /// and forks should be removed.
  356. pub async fn forks_finalization(&mut self) -> Result<Vec<BlockInfo>> {
  357. let slot = self.time_keeper.current_slot();
  358. info!(target: "validator::consensus::forks_finalization", "Started finalization check for slot: {}", slot);
  359. // Set last slot finalization check occured to current slot
  360. self.checked_finalization = slot;
  361. // First we find longest fork without any other forks at same height
  362. let mut fork_index = -1;
  363. let mut max_length = 0;
  364. for (index, fork) in self.forks.iter().enumerate() {
  365. let length = fork.proposals.len();
  366. // Check if less than max
  367. if length < max_length {
  368. continue
  369. }
  370. // Check if same length as max
  371. if length == max_length {
  372. // Setting fork_index so we know we have multiple
  373. // forks at same length.
  374. fork_index = -2;
  375. continue
  376. }
  377. // Set fork as max
  378. fork_index = index as i64;
  379. max_length = length;
  380. }
  381. // Check if we found any fork to finalize
  382. match fork_index {
  383. -2 => {
  384. info!(target: "validator::consensus::forks_finalization", "Eligible forks with same height exist, nothing to finalize.");
  385. return Ok(vec![])
  386. }
  387. -1 => {
  388. info!(target: "validator::consensus::forks_finalization", "Nothing to finalize.");
  389. }
  390. _ => {
  391. info!(target: "validator::consensus::forks_finalization", "Fork {} can be finalized!", fork_index)
  392. }
  393. }
  394. if max_length == 0 {
  395. return Ok(vec![])
  396. }
  397. // Starting finalization
  398. let fork = &self.forks[fork_index as usize];
  399. let finalized = fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  400. info!(target: "validator::consensus::forks_finalization", "Finalized blocks: {}", finalized.len());
  401. Ok(finalized)
  402. }
  403. }
  404. /// This struct represents a block proposal, used for consensus.
  405. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  406. pub struct Proposal {
  407. /// Block hash
  408. pub hash: blake3::Hash,
  409. /// Block data
  410. pub block: BlockInfo,
  411. }
  412. impl Proposal {
  413. pub fn new(block: BlockInfo) -> Result<Self> {
  414. let hash = block.hash()?;
  415. Ok(Self { hash, block })
  416. }
  417. }
  418. impl From<Proposal> for BlockInfo {
  419. fn from(proposal: Proposal) -> BlockInfo {
  420. proposal.block
  421. }
  422. }
  423. /// This struct represents a forked blockchain state, using an overlay over original
  424. /// blockchain, containing all pending to-write records. Additionally, each fork
  425. /// keeps a vector of valid pending transactions hashes, in order of receival, and
  426. /// the proposals hashes sequence, for validations.
  427. #[derive(Clone)]
  428. pub struct Fork {
  429. /// Overlay cache over canonical Blockchain
  430. pub overlay: BlockchainOverlayPtr,
  431. /// Current PoW module state,
  432. pub module: PoWModule,
  433. /// Fork proposal hashes sequence
  434. pub proposals: Vec<blake3::Hash>,
  435. /// Hot/live slots
  436. pub slots: Vec<Slot>,
  437. /// Valid pending transaction hashes
  438. pub mempool: Vec<blake3::Hash>,
  439. }
  440. impl Fork {
  441. pub fn new(blockchain: &Blockchain, pow_target: Option<usize>) -> Result<Self> {
  442. let module = PoWModule::new(blockchain.clone(), None, pow_target);
  443. let mempool =
  444. blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
  445. let overlay = BlockchainOverlay::new(blockchain)?;
  446. Ok(Self { overlay, module, proposals: vec![], slots: vec![], mempool })
  447. }
  448. /// Auxiliary function to retrieve last proposal
  449. pub fn last_proposal(&self) -> Result<Proposal> {
  450. let block = if self.proposals.is_empty() {
  451. self.overlay.lock().unwrap().last_block()?
  452. } else {
  453. self.overlay.lock().unwrap().get_blocks_by_hash(&[*self.proposals.last().unwrap()])?[0]
  454. .clone()
  455. };
  456. Proposal::new(block)
  457. }
  458. /// Utility function to extract leader selection lottery randomness(nonce/eta),
  459. /// defined as the hash of the last block, converted to pallas base.
  460. fn get_last_nonce(&self) -> Result<pallas::Base> {
  461. // Retrieve last block(or proposal)
  462. let proposal = self.last_proposal()?;
  463. match proposal.block.header.version {
  464. 1 => Ok(pallas::Base::from(proposal.block.header.nonce)),
  465. 2 => {
  466. // Read first 240 bits of proposal hash
  467. let mut bytes: [u8; 32] = *proposal.hash.as_bytes();
  468. bytes[30] = 0;
  469. bytes[31] = 0;
  470. Ok(pallas::Base::from_repr(bytes).unwrap())
  471. }
  472. _ => Err(Error::BlockVersionIsInvalid(proposal.block.header.version)),
  473. }
  474. }
  475. /// Auxiliary function to retrieve unproposed valid transactions.
  476. pub async fn unproposed_txs(
  477. &self,
  478. blockchain: &Blockchain,
  479. time_keeper: &TimeKeeper,
  480. ) -> Result<Vec<Transaction>> {
  481. // Retrieve all mempool transactions
  482. let mut unproposed_txs: Vec<Transaction> = blockchain
  483. .pending_txs
  484. .get(&self.mempool, true)?
  485. .iter()
  486. .map(|x| x.clone().unwrap())
  487. .collect();
  488. // Iterate over fork proposals to find already proposed transactions
  489. // and remove them from the unproposed_txs vector.
  490. let proposals = self.overlay.lock().unwrap().get_blocks_by_hash(&self.proposals)?;
  491. for proposal in proposals {
  492. for tx in &proposal.txs {
  493. unproposed_txs.retain(|x| x != tx);
  494. }
  495. }
  496. // Check if transactions exceed configured cap
  497. if unproposed_txs.len() > TXS_CAP {
  498. unproposed_txs = unproposed_txs[0..TXS_CAP].to_vec()
  499. }
  500. // Clone forks' overlay
  501. let overlay = self.overlay.lock().unwrap().full_clone()?;
  502. // Verify transactions
  503. let erroneous_txs = verify_transactions(&overlay, time_keeper, &unproposed_txs).await?;
  504. if !erroneous_txs.is_empty() {
  505. unproposed_txs.retain(|x| !erroneous_txs.contains(x));
  506. }
  507. Ok(unproposed_txs)
  508. }
  509. /// Generate current hot/live slot
  510. pub fn generate_slot(
  511. &mut self,
  512. id: u64,
  513. producers: u64,
  514. last_hashes: &[blake3::Hash],
  515. second_to_last_hashes: &[blake3::Hash],
  516. ) -> Result<()> {
  517. // Grab last known fork slot
  518. let previous_slot = if self.slots.is_empty() {
  519. self.overlay.lock().unwrap().slots.get_last()?
  520. } else {
  521. self.slots.last().unwrap().clone()
  522. };
  523. // Generate previous slot information
  524. let previous = PreviousSlot::new(
  525. producers,
  526. last_hashes.to_vec(),
  527. second_to_last_hashes.to_vec(),
  528. previous_slot.pid.error,
  529. );
  530. // Generate PID controller output
  531. let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot, producers);
  532. let pid = PidOutput::new(f, error, sigma1, sigma2);
  533. // Each slot starts as an empty slot(not reward) when generated, carrying
  534. // last nonce(eta)
  535. let last_nonce = self.get_last_nonce()?;
  536. let total_tokens = previous_slot.total_tokens + previous_slot.reward;
  537. let reward = 0;
  538. let slot = Slot::new(id, previous, pid, last_nonce, total_tokens, reward);
  539. self.slots.push(slot);
  540. Ok(())
  541. }
  542. /// Auxiliarry function to compute fork's rank, assuming all proposals are valid.
  543. pub fn rank(&self) -> Result<u64> {
  544. // If the fork is empty its rank is 0
  545. if self.proposals.is_empty() {
  546. return Ok(0)
  547. }
  548. // Retrieve the sum of all fork proposals ranks
  549. let mut sum = 0;
  550. let proposals = self.overlay.lock().unwrap().get_blocks_by_hash(&self.proposals)?;
  551. for proposal in &proposals {
  552. // For block height > 3, retrieve their previous previous block
  553. let previous_previous = if proposal.header.height > 3 {
  554. let previous = &self
  555. .overlay
  556. .lock()
  557. .unwrap()
  558. .get_blocks_by_hash(&[proposal.header.previous])?[0];
  559. self.overlay.lock().unwrap().get_blocks_by_hash(&[previous.header.previous])?[0]
  560. .clone()
  561. } else {
  562. proposal.clone()
  563. };
  564. sum += block_rank(proposal, &previous_previous)?;
  565. }
  566. // Use fork(proposals) length as a multiplier to compute the actual fork rank
  567. let rank = proposals.len() as u64 * sum;
  568. Ok(rank)
  569. }
  570. /// Auxiliary function to create a full clone using BlockchainOverlay::full_clone.
  571. /// Changes to this copy don't affect original fork overlay records, since underlying
  572. /// overlay pointer have been updated to the cloned one.
  573. pub fn full_clone(&self) -> Result<Self> {
  574. let overlay = self.overlay.lock().unwrap().full_clone()?;
  575. let module = self.module.clone();
  576. let proposals = self.proposals.clone();
  577. let slots = self.slots.clone();
  578. let mempool = self.mempool.clone();
  579. Ok(Self { overlay, module, proposals, slots, mempool })
  580. }
  581. }