consensus.rs 16 KB

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