consensus.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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::crypto::{MerkleTree, SecretKey};
  19. use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
  20. use log::{debug, error, info};
  21. use num_bigint::BigUint;
  22. use smol::lock::RwLock;
  23. use crate::{
  24. blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header},
  25. tx::Transaction,
  26. util::time::Timestamp,
  27. validator::{
  28. pow::PoWModule,
  29. utils::{best_forks_indexes, block_rank, find_extended_fork_index},
  30. verify_block, verify_proposal, verify_transactions, TxVerifyFailed,
  31. },
  32. Error, Result,
  33. };
  34. // Consensus configuration
  35. /// Block/proposal maximum transactions, exluding producer transaction
  36. pub 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. /// Fork size(length) after which it can be finalized
  42. pub finalization_threshold: usize,
  43. /// Node is participating to consensus
  44. pub participating: bool,
  45. /// Fork chains containing block proposals
  46. pub forks: RwLock<Vec<Fork>>,
  47. /// Canonical blockchain PoW module state
  48. pub module: RwLock<PoWModule>,
  49. }
  50. impl Consensus {
  51. /// Generate a new Consensus state.
  52. pub fn new(
  53. blockchain: Blockchain,
  54. finalization_threshold: usize,
  55. pow_target: usize,
  56. pow_fixed_difficulty: Option<BigUint>,
  57. ) -> Result<Self> {
  58. let module =
  59. RwLock::new(PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty)?);
  60. Ok(Self {
  61. blockchain,
  62. finalization_threshold,
  63. participating: false,
  64. forks: RwLock::new(vec![]),
  65. module,
  66. })
  67. }
  68. /// Generate an unsigned block for provided fork, containing all
  69. /// pending transactions.
  70. pub async fn generate_unsigned_block(
  71. &self,
  72. fork: &Fork,
  73. producer_tx: Transaction,
  74. ) -> Result<BlockInfo> {
  75. // Grab forks' next block height
  76. let next_block_height = fork.get_next_block_height()?;
  77. // Grab forks' unproposed transactions
  78. let mut unproposed_txs = fork.unproposed_txs(&self.blockchain, next_block_height).await?;
  79. unproposed_txs.push(producer_tx);
  80. // Grab forks' last block proposal(previous)
  81. let previous = fork.last_proposal()?;
  82. // Generate the new header
  83. let header =
  84. Header::new(previous.block.hash()?, next_block_height, Timestamp::current_time(), 0);
  85. // Generate the block
  86. let mut block = BlockInfo::new_empty(header);
  87. // Add transactions to the block
  88. block.append_txs(unproposed_txs)?;
  89. Ok(block)
  90. }
  91. /// Generate a block proposal for provided fork, containing all
  92. /// pending transactions. Proposal is signed using provided secret key,
  93. /// which must also have signed the provided proposal transaction.
  94. pub async fn generate_signed_proposal(
  95. &self,
  96. fork: &Fork,
  97. producer_tx: Transaction,
  98. secret_key: &SecretKey,
  99. ) -> Result<Proposal> {
  100. let mut block = self.generate_unsigned_block(fork, producer_tx).await?;
  101. // Sign block
  102. block.sign(secret_key)?;
  103. // Generate the block proposal from the block
  104. let proposal = Proposal::new(block)?;
  105. Ok(proposal)
  106. }
  107. /// Generate a new empty fork.
  108. pub async fn generate_empty_fork(&self) -> Result<()> {
  109. debug!(target: "validator::consensus::generate_empty_fork", "Generating new empty fork...");
  110. let mut lock = self.forks.write().await;
  111. let fork = Fork::new(&self.blockchain, self.module.read().await.clone()).await?;
  112. lock.push(fork);
  113. drop(lock);
  114. debug!(target: "validator::consensus::generate_empty_fork", "Fork generated!");
  115. Ok(())
  116. }
  117. /// Given a proposal, the node verifys it and finds which fork it extends.
  118. /// If the proposal extends the canonical blockchain, a new fork chain is created.
  119. pub async fn append_proposal(&self, proposal: &Proposal) -> Result<()> {
  120. debug!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
  121. // Verify proposal and grab corresponding fork
  122. let (mut fork, index) = verify_proposal(self, proposal).await?;
  123. // Append proposal to the fork
  124. fork.append_proposal(proposal.hash).await?;
  125. // Update PoW module
  126. fork.module.append(proposal.block.header.timestamp.0, &fork.module.next_difficulty()?);
  127. // If a fork index was found, replace forks with the mutated one,
  128. // otherwise push the new fork.
  129. let mut lock = self.forks.write().await;
  130. match index {
  131. Some(i) => {
  132. lock[i] = fork;
  133. }
  134. None => {
  135. lock.push(fork);
  136. }
  137. }
  138. drop(lock);
  139. info!(target: "validator::consensus::append_proposal", "Appended proposal {}", proposal.hash);
  140. Ok(())
  141. }
  142. /// Given a proposal, find the fork chain it extends, and return its full clone.
  143. /// If the proposal extends the fork not on its tail, a new fork is created and
  144. /// we re-apply the proposals up to the extending one. If proposal extends canonical,
  145. /// a new fork is created. Additionally, we return the fork index if a new fork
  146. /// was not created, so caller can replace the fork.
  147. pub async fn find_extended_fork(&self, proposal: &Proposal) -> Result<(Fork, Option<usize>)> {
  148. // Grab a lock over current forks
  149. let forks = self.forks.read().await;
  150. // Check if proposal extends any fork
  151. let found = find_extended_fork_index(&forks, proposal);
  152. if found.is_err() {
  153. if let Err(Error::ProposalAlreadyExists) = found {
  154. return Err(Error::ProposalAlreadyExists)
  155. }
  156. // Check if proposal extends canonical
  157. let (last_height, last_block) = self.blockchain.last()?;
  158. if proposal.block.header.previous != last_block ||
  159. proposal.block.header.height <= last_height
  160. {
  161. return Err(Error::ExtendedChainIndexNotFound)
  162. }
  163. // Check if we have an empty fork to use
  164. for (f_index, fork) in forks.iter().enumerate() {
  165. if fork.proposals.is_empty() {
  166. return Ok((forks[f_index].full_clone()?, Some(f_index)))
  167. }
  168. }
  169. // Generate a new fork extending canonical
  170. let fork = Fork::new(&self.blockchain, self.module.read().await.clone()).await?;
  171. return Ok((fork, None))
  172. }
  173. let (f_index, p_index) = found.unwrap();
  174. let original_fork = &forks[f_index];
  175. // Check if proposal extends fork at last proposal
  176. if p_index == (original_fork.proposals.len() - 1) {
  177. return Ok((original_fork.full_clone()?, Some(f_index)))
  178. }
  179. // Rebuild fork
  180. let mut fork = Fork::new(&self.blockchain, self.module.read().await.clone()).await?;
  181. fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
  182. // Retrieve proposals blocks from original fork
  183. let blocks = &original_fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  184. // Retrieve last block
  185. let mut previous = &fork.overlay.lock().unwrap().last_block()?;
  186. // Validate and insert each block
  187. for block in blocks {
  188. // Verify block
  189. if verify_block(&fork.overlay, &fork.module, block, previous).await.is_err() {
  190. error!(target: "validator::consensus::find_extended_fork_overlay", "Erroneous block found in set");
  191. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  192. return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
  193. };
  194. // Update PoW module
  195. fork.module.append(block.header.timestamp.0, &fork.module.next_difficulty()?);
  196. // Use last inserted block as next iteration previous
  197. previous = block;
  198. }
  199. // Drop forks lock
  200. drop(forks);
  201. Ok((fork, None))
  202. }
  203. /// Consensus finalization logic:
  204. /// - If the current best fork has reached greater length than the security threshold, and
  205. /// no other fork exist with same rank, all proposals excluding the last one in that fork
  206. // can be finalized (append to canonical blockchain).
  207. /// When best fork can be finalized, blocks(proposals) should be appended to canonical, excluding the
  208. /// last one, and fork should be rebuilt.
  209. pub async fn finalization(&self) -> Result<Vec<BlockInfo>> {
  210. debug!(target: "validator::consensus::finalization", "Started finalization check");
  211. // Grab best forks
  212. let forks = self.forks.read().await;
  213. let forks_indexes = best_forks_indexes(&forks)?;
  214. // Check if multiple forks with same rank were found
  215. if forks_indexes.len() > 1 {
  216. debug!(target: "validator::consensus::finalization", "Multiple best ranked forks were found");
  217. return Ok(vec![])
  218. }
  219. // Grag the actual best fork
  220. let fork = &forks[forks_indexes[0]];
  221. // Check its length
  222. let length = fork.proposals.len();
  223. if length < self.finalization_threshold {
  224. debug!(target: "validator::consensus::finalization", "Nothing to finalize yet, best fork size: {}", length);
  225. return Ok(vec![])
  226. }
  227. // Grab finalized blocks
  228. let finalized = fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  229. // Drop forks lock
  230. drop(forks);
  231. Ok(finalized)
  232. }
  233. }
  234. /// This struct represents a block proposal, used for consensus.
  235. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  236. pub struct Proposal {
  237. /// Block hash
  238. pub hash: blake3::Hash,
  239. /// Block data
  240. pub block: BlockInfo,
  241. }
  242. impl Proposal {
  243. pub fn new(block: BlockInfo) -> Result<Self> {
  244. let hash = block.hash()?;
  245. Ok(Self { hash, block })
  246. }
  247. }
  248. impl From<Proposal> for BlockInfo {
  249. fn from(proposal: Proposal) -> BlockInfo {
  250. proposal.block
  251. }
  252. }
  253. /// This struct represents a forked blockchain state, using an overlay over original
  254. /// blockchain, containing all pending to-write records. Additionally, each fork
  255. /// keeps a vector of valid pending transactions hashes, in order of receival, and
  256. /// the proposals hashes sequence, for validations.
  257. #[derive(Clone)]
  258. pub struct Fork {
  259. /// Overlay cache over canonical Blockchain
  260. pub overlay: BlockchainOverlayPtr,
  261. /// Current PoW module state,
  262. pub module: PoWModule,
  263. /// Fork proposal hashes sequence
  264. pub proposals: Vec<blake3::Hash>,
  265. /// Valid pending transaction hashes
  266. pub mempool: Vec<blake3::Hash>,
  267. /// Current fork rank, cached for better performance
  268. pub rank: u64,
  269. }
  270. impl Fork {
  271. pub async fn new(blockchain: &Blockchain, module: PoWModule) -> Result<Self> {
  272. let mempool =
  273. blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
  274. let overlay = BlockchainOverlay::new(blockchain)?;
  275. Ok(Self { overlay, module, proposals: vec![], mempool, rank: 0 })
  276. }
  277. /// Auxiliary function to append a proposal and recalculate current fork rank
  278. pub async fn append_proposal(&mut self, proposal: blake3::Hash) -> Result<()> {
  279. self.proposals.push(proposal);
  280. self.rank = self.rank().await?;
  281. Ok(())
  282. }
  283. /// Auxiliary function to retrieve last proposal
  284. pub fn last_proposal(&self) -> Result<Proposal> {
  285. let block = if self.proposals.is_empty() {
  286. self.overlay.lock().unwrap().last_block()?
  287. } else {
  288. self.overlay.lock().unwrap().get_blocks_by_hash(&[*self.proposals.last().unwrap()])?[0]
  289. .clone()
  290. };
  291. Proposal::new(block)
  292. }
  293. /// Auxiliary function to compute forks' next block height.
  294. pub fn get_next_block_height(&self) -> Result<u64> {
  295. let proposal = self.last_proposal()?;
  296. Ok(proposal.block.header.height + 1)
  297. }
  298. /// Auxiliary function to retrieve unproposed valid transactions.
  299. pub async fn unproposed_txs(
  300. &self,
  301. blockchain: &Blockchain,
  302. verifying_block_height: u64,
  303. ) -> Result<Vec<Transaction>> {
  304. // Check if our mempool is not empty
  305. if self.mempool.is_empty() {
  306. return Ok(vec![])
  307. }
  308. // Grab all current proposals transactions hashes
  309. let proposals_txs = self.overlay.lock().unwrap().get_blocks_txs_hashes(&self.proposals)?;
  310. // Iterate through all pending transactions in the forks' mempool
  311. let mut unproposed_txs = vec![];
  312. for tx in &self.mempool {
  313. // If the hash is contained in the proposals transactions vec, skip it
  314. if proposals_txs.contains(tx) {
  315. continue
  316. }
  317. // Push the tx hash into the unproposed transactions vector
  318. unproposed_txs.push(*tx);
  319. // Check limit
  320. if unproposed_txs.len() == TXS_CAP {
  321. break
  322. }
  323. }
  324. // Check if we have any unproposed transactions
  325. if unproposed_txs.is_empty() {
  326. return Ok(vec![])
  327. }
  328. // Retrieve the actual unproposed transactions
  329. let mut unproposed_txs: Vec<Transaction> = blockchain
  330. .pending_txs
  331. .get(&unproposed_txs, true)?
  332. .iter()
  333. .map(|x| x.clone().unwrap())
  334. .collect();
  335. // Clone forks' overlay
  336. let overlay = self.overlay.lock().unwrap().full_clone()?;
  337. // Verify transactions
  338. if let Err(e) = verify_transactions(
  339. &overlay,
  340. verifying_block_height,
  341. &unproposed_txs,
  342. &mut MerkleTree::new(1),
  343. false,
  344. )
  345. .await
  346. {
  347. match e {
  348. crate::Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(erroneous_txs)) => {
  349. unproposed_txs.retain(|x| !erroneous_txs.contains(x))
  350. }
  351. _ => return Err(e),
  352. }
  353. }
  354. Ok(unproposed_txs)
  355. }
  356. /// Auxiliarry function to compute fork's rank, assuming all proposals are valid.
  357. pub async fn rank(&self) -> Result<u64> {
  358. // If the fork is empty its rank is 0
  359. if self.proposals.is_empty() {
  360. return Ok(0)
  361. }
  362. // Retrieve the sum of all fork proposals ranks
  363. let mut sum = 0;
  364. let proposals = self.overlay.lock().unwrap().get_blocks_by_hash(&self.proposals)?;
  365. for proposal in &proposals {
  366. // For block height > 3, retrieve their previous previous block
  367. let previous_previous = if proposal.header.height > 3 {
  368. let previous = &self
  369. .overlay
  370. .lock()
  371. .unwrap()
  372. .get_blocks_by_hash(&[proposal.header.previous])?[0];
  373. self.overlay.lock().unwrap().get_blocks_by_hash(&[previous.header.previous])?[0]
  374. .clone()
  375. } else {
  376. proposal.clone()
  377. };
  378. sum += block_rank(proposal, &previous_previous).await?;
  379. }
  380. // Use fork(proposals) length as a multiplier to compute the actual fork rank
  381. let rank = proposals.len() as u64 * sum;
  382. Ok(rank)
  383. }
  384. /// Auxiliary function to create a full clone using BlockchainOverlay::full_clone.
  385. /// Changes to this copy don't affect original fork overlay records, since underlying
  386. /// overlay pointer have been updated to the cloned one.
  387. pub fn full_clone(&self) -> Result<Self> {
  388. let overlay = self.overlay.lock().unwrap().full_clone()?;
  389. let module = self.module.clone();
  390. let proposals = self.proposals.clone();
  391. let mempool = self.mempool.clone();
  392. let rank = self.rank;
  393. Ok(Self { overlay, module, proposals, mempool, rank })
  394. }
  395. }