consensus.rs 24 KB

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