consensus.rs 17 KB

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