consensus.rs 19 KB

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