consensus.rs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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 std::collections::{BTreeSet, HashMap, HashSet};
  19. use darkfi_sdk::{crypto::MerkleTree, tx::TransactionHash};
  20. use darkfi_serial::{async_trait, deserialize, SerialDecodable, SerialEncodable};
  21. use num_bigint::BigUint;
  22. use sled_overlay::{database::SledDbOverlayStateDiff, sled::IVec};
  23. use smol::lock::RwLock;
  24. use tracing::{debug, info, warn};
  25. use crate::{
  26. blockchain::{
  27. block_store::{BlockDifficulty, BlockRanks},
  28. BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header, HeaderHash,
  29. },
  30. runtime::vm_runtime::GAS_LIMIT,
  31. tx::{Transaction, MAX_TX_CALLS},
  32. validator::{
  33. pow::{PoWModule, RANDOMX_KEY_CHANGE_DELAY, RANDOMX_KEY_CHANGING_HEIGHT},
  34. utils::{best_fork_index, block_rank, find_extended_fork_index},
  35. verification::{verify_proposal, verify_transaction},
  36. },
  37. zk::VerifyingKey,
  38. Error, Result,
  39. };
  40. /// Gas limit for total block transactions(50 full transactions).
  41. pub const BLOCK_GAS_LIMIT: u64 = GAS_LIMIT * MAX_TX_CALLS as u64 * 50;
  42. /// This struct represents the information required by the consensus algorithm
  43. pub struct Consensus {
  44. /// Canonical (confirmed) blockchain
  45. pub blockchain: Blockchain,
  46. /// Fork size(length) after which it can be confirmed
  47. pub confirmation_threshold: usize,
  48. /// Fork chains containing block proposals
  49. pub forks: RwLock<Vec<Fork>>,
  50. /// Canonical blockchain PoW module state
  51. pub module: RwLock<PoWModule>,
  52. /// Lock to restrict when proposals appends can happen
  53. pub append_lock: RwLock<()>,
  54. }
  55. impl Consensus {
  56. /// Generate a new Consensus state.
  57. pub fn new(
  58. blockchain: Blockchain,
  59. confirmation_threshold: usize,
  60. pow_target: u32,
  61. pow_fixed_difficulty: Option<BigUint>,
  62. ) -> Result<Self> {
  63. let forks = RwLock::new(vec![]);
  64. let module = RwLock::new(PoWModule::new(
  65. blockchain.clone(),
  66. pow_target,
  67. pow_fixed_difficulty,
  68. None,
  69. )?);
  70. let append_lock = RwLock::new(());
  71. Ok(Self { blockchain, confirmation_threshold, forks, module, append_lock })
  72. }
  73. /// Generate a new empty fork.
  74. pub async fn generate_empty_fork(&self) -> Result<()> {
  75. debug!(target: "validator::consensus::generate_empty_fork", "Generating new empty fork...");
  76. let mut forks = self.forks.write().await;
  77. // Check if we already have an empty fork
  78. for fork in forks.iter() {
  79. if fork.proposals.is_empty() {
  80. debug!(target: "validator::consensus::generate_empty_fork", "An empty fork already exists.");
  81. drop(forks);
  82. return Ok(())
  83. }
  84. }
  85. let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
  86. forks.push(fork);
  87. drop(forks);
  88. debug!(target: "validator::consensus::generate_empty_fork", "Fork generated!");
  89. Ok(())
  90. }
  91. /// Given a proposal, the node verifys it and finds which fork it extends.
  92. /// If the proposal extends the canonical blockchain, a new fork chain is created.
  93. pub async fn append_proposal(&self, proposal: &Proposal, verify_fees: bool) -> Result<()> {
  94. debug!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
  95. // Check if proposal already exists
  96. let lock = self.forks.read().await;
  97. for fork in lock.iter() {
  98. for p in fork.proposals.iter().rev() {
  99. if p == &proposal.hash {
  100. drop(lock);
  101. debug!(target: "validator::consensus::append_proposal", "Proposal {} already exists", proposal.hash);
  102. return Err(Error::ProposalAlreadyExists)
  103. }
  104. }
  105. }
  106. // Check if proposal is canonical
  107. if let Ok(canonical_headers) =
  108. self.blockchain.blocks.get_order(&[proposal.block.header.height], true)
  109. {
  110. if canonical_headers[0].unwrap() == proposal.hash {
  111. drop(lock);
  112. debug!(target: "validator::consensus::append_proposal", "Proposal {} already exists", proposal.hash);
  113. return Err(Error::ProposalAlreadyExists)
  114. }
  115. }
  116. drop(lock);
  117. // Verify proposal and grab corresponding fork
  118. let (mut fork, index) = verify_proposal(self, proposal, verify_fees).await?;
  119. // Append proposal to the fork
  120. fork.append_proposal(proposal).await?;
  121. // TODO: to keep memory usage low, we should only append forks that
  122. // are higher ranking than our current best one
  123. // If a fork index was found, replace forks with the mutated one,
  124. // otherwise push the new fork.
  125. let mut lock = self.forks.write().await;
  126. match index {
  127. Some(i) => {
  128. if i < lock.len() && lock[i].proposals == fork.proposals[..fork.proposals.len() - 1]
  129. {
  130. lock[i] = fork;
  131. } else {
  132. lock.push(fork);
  133. }
  134. }
  135. None => {
  136. lock.push(fork);
  137. }
  138. }
  139. drop(lock);
  140. info!(target: "validator::consensus::append_proposal", "Appended proposal {}", proposal.hash);
  141. Ok(())
  142. }
  143. /// Given a proposal, find the fork chain it extends, and return its full clone.
  144. /// If the proposal extends the fork not on its tail, a new fork is created and
  145. /// we re-apply the proposals up to the extending one. If proposal extends canonical,
  146. /// a new fork is created. Additionally, we return the fork index if a new fork
  147. /// was not created, so caller can replace the fork.
  148. pub async fn find_extended_fork(&self, proposal: &Proposal) -> Result<(Fork, Option<usize>)> {
  149. // Grab a lock over current forks
  150. let forks = self.forks.read().await;
  151. // Check if proposal extends any fork
  152. let found = find_extended_fork_index(&forks, proposal);
  153. if found.is_err() {
  154. if let Err(Error::ProposalAlreadyExists) = found {
  155. return Err(Error::ProposalAlreadyExists)
  156. }
  157. // Check if proposal extends canonical
  158. let (last_height, last_block) = self.blockchain.last()?;
  159. if proposal.block.header.previous != last_block ||
  160. proposal.block.header.height <= last_height
  161. {
  162. return Err(Error::ExtendedChainIndexNotFound)
  163. }
  164. // Check if we have an empty fork to use
  165. for (f_index, fork) in forks.iter().enumerate() {
  166. if fork.proposals.is_empty() {
  167. return Ok((forks[f_index].full_clone()?, Some(f_index)))
  168. }
  169. }
  170. // Generate a new fork extending canonical
  171. let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
  172. return Ok((fork, None))
  173. }
  174. let (f_index, p_index) = found.unwrap();
  175. let original_fork = &forks[f_index];
  176. // Check if proposal extends fork at last proposal
  177. if p_index == (original_fork.proposals.len() - 1) {
  178. return Ok((original_fork.full_clone()?, Some(f_index)))
  179. }
  180. // Rebuild fork
  181. let mut fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
  182. fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
  183. fork.diffs = original_fork.diffs[..p_index + 1].to_vec();
  184. // Retrieve proposals blocks from original fork
  185. let blocks = &original_fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  186. for (index, block) in blocks.iter().enumerate() {
  187. // Apply block diffs
  188. fork.overlay.lock().unwrap().overlay.lock().unwrap().add_diff(&fork.diffs[index])?;
  189. // Grab next mine target and difficulty
  190. let (next_target, next_difficulty) = fork.module.next_mine_target_and_difficulty()?;
  191. // Calculate block rank
  192. let (target_distance_sq, hash_distance_sq) = block_rank(block, &next_target)?;
  193. // Update PoW module
  194. fork.module.append(&block.header, &next_difficulty)?;
  195. // Update fork ranks
  196. fork.targets_rank += target_distance_sq;
  197. fork.hashes_rank += hash_distance_sq;
  198. }
  199. // Drop forks lock
  200. drop(forks);
  201. Ok((fork, None))
  202. }
  203. /// Check if best fork proposals can be confirmed.
  204. /// Consensus confirmation logic:
  205. /// - If the current best fork has reached greater length than the security threshold,
  206. /// and no other fork exist with same rank, first proposal(s) in that fork can be
  207. /// appended to canonical blockchain (confirme).
  208. ///
  209. /// When best fork can be confirmed, first block(s) should be appended to canonical,
  210. /// and forks should be rebuilt.
  211. pub async fn confirmation(&self) -> Result<Option<usize>> {
  212. debug!(target: "validator::consensus::confirmation", "Started confirmation check");
  213. // Grab best fork
  214. let forks = self.forks.read().await;
  215. let index = best_fork_index(&forks)?;
  216. let fork = &forks[index];
  217. // Check its length
  218. let length = fork.proposals.len();
  219. if length < self.confirmation_threshold {
  220. debug!(target: "validator::consensus::confirmation", "Nothing to confirme yet, best fork size: {length}");
  221. drop(forks);
  222. return Ok(None)
  223. }
  224. // Drop forks lock
  225. drop(forks);
  226. Ok(Some(index))
  227. }
  228. /// Auxiliary function to retrieve the fork header hash of provided height.
  229. /// The fork is identified by the provided header hash.
  230. pub async fn get_fork_header_hash(
  231. &self,
  232. height: u32,
  233. fork_header: &HeaderHash,
  234. ) -> Result<Option<HeaderHash>> {
  235. // Grab a lock over current forks
  236. let forks = self.forks.read().await;
  237. // Find the fork containing the provided header
  238. let mut found = None;
  239. 'outer: for (index, fork) in forks.iter().enumerate() {
  240. for p in fork.proposals.iter().rev() {
  241. if p == fork_header {
  242. found = Some(index);
  243. break 'outer
  244. }
  245. }
  246. }
  247. if found.is_none() {
  248. drop(forks);
  249. return Ok(None)
  250. }
  251. let index = found.unwrap();
  252. // Grab header if it exists
  253. let header = forks[index].overlay.lock().unwrap().blocks.get_order(&[height], false)?[0];
  254. // Drop forks lock
  255. drop(forks);
  256. Ok(header)
  257. }
  258. /// Auxiliary function to retrieve the fork headers of provided hashes.
  259. /// The fork is identified by the provided header hash. If fork doesn't
  260. /// exists, an empty vector is returned.
  261. pub async fn get_fork_headers(
  262. &self,
  263. headers: &[HeaderHash],
  264. fork_header: &HeaderHash,
  265. ) -> Result<Vec<Header>> {
  266. // Grab a lock over current forks
  267. let forks = self.forks.read().await;
  268. // Find the fork containing the provided header
  269. let mut found = None;
  270. 'outer: for (index, fork) in forks.iter().enumerate() {
  271. for p in fork.proposals.iter().rev() {
  272. if p == fork_header {
  273. found = Some(index);
  274. break 'outer
  275. }
  276. }
  277. }
  278. let Some(index) = found else {
  279. drop(forks);
  280. return Ok(vec![])
  281. };
  282. // Grab headers
  283. let headers = forks[index].overlay.lock().unwrap().get_headers_by_hash(headers)?;
  284. // Drop forks lock
  285. drop(forks);
  286. Ok(headers)
  287. }
  288. /// Auxiliary function to retrieve the fork proposals of provided hashes.
  289. /// The fork is identified by the provided header hash. If fork doesn't
  290. /// exists, an empty vector is returned.
  291. pub async fn get_fork_proposals(
  292. &self,
  293. headers: &[HeaderHash],
  294. fork_header: &HeaderHash,
  295. ) -> Result<Vec<Proposal>> {
  296. // Grab a lock over current forks
  297. let forks = self.forks.read().await;
  298. // Find the fork containing the provided header
  299. let mut found = None;
  300. 'outer: for (index, fork) in forks.iter().enumerate() {
  301. for p in fork.proposals.iter().rev() {
  302. if p == fork_header {
  303. found = Some(index);
  304. break 'outer
  305. }
  306. }
  307. }
  308. let Some(index) = found else {
  309. drop(forks);
  310. return Ok(vec![])
  311. };
  312. // Grab proposals
  313. let blocks = forks[index].overlay.lock().unwrap().get_blocks_by_hash(headers)?;
  314. let mut proposals = Vec::with_capacity(blocks.len());
  315. for block in blocks {
  316. proposals.push(Proposal::new(block));
  317. }
  318. // Drop forks lock
  319. drop(forks);
  320. Ok(proposals)
  321. }
  322. /// Auxiliary function to retrieve a fork proposals, starting from provided tip.
  323. /// If provided tip is too far behind, unknown, or fork doesn't exists, an empty
  324. /// vector is returned. The fork is identified by the optional provided header hash.
  325. /// If its `None`, we use our best fork.
  326. pub async fn get_fork_proposals_after(
  327. &self,
  328. tip: HeaderHash,
  329. fork_tip: Option<HeaderHash>,
  330. limit: u32,
  331. ) -> Result<Vec<Proposal>> {
  332. // Grab a lock over current forks
  333. let forks = self.forks.read().await;
  334. // Create return vector
  335. let mut proposals = vec![];
  336. // Grab fork index to use
  337. let index = match fork_tip {
  338. Some(fork_tip) => {
  339. let mut found = None;
  340. 'outer: for (index, fork) in forks.iter().enumerate() {
  341. for p in fork.proposals.iter().rev() {
  342. if p == &fork_tip {
  343. found = Some(index);
  344. break 'outer
  345. }
  346. }
  347. }
  348. if found.is_none() {
  349. drop(forks);
  350. return Ok(proposals)
  351. }
  352. found.unwrap()
  353. }
  354. None => best_fork_index(&forks)?,
  355. };
  356. // Check tip exists
  357. let Ok(existing_tips) = forks[index].overlay.lock().unwrap().get_blocks_by_hash(&[tip])
  358. else {
  359. drop(forks);
  360. return Ok(proposals)
  361. };
  362. // Check tip is not far behind
  363. let last_block_height = forks[index].overlay.lock().unwrap().last()?.0;
  364. if last_block_height - existing_tips[0].header.height >= limit {
  365. drop(forks);
  366. return Ok(proposals)
  367. }
  368. // Retrieve all proposals after requested one
  369. let headers = self.blockchain.blocks.get_all_after(existing_tips[0].header.height)?;
  370. let blocks = self.blockchain.get_blocks_by_hash(&headers)?;
  371. for block in blocks {
  372. proposals.push(Proposal::new(block));
  373. }
  374. let blocks =
  375. forks[index].overlay.lock().unwrap().get_blocks_by_hash(&forks[index].proposals)?;
  376. for block in blocks {
  377. proposals.push(Proposal::new(block));
  378. }
  379. // Drop forks lock
  380. drop(forks);
  381. Ok(proposals)
  382. }
  383. /// Auxiliary function to grab current mining RandomX key,
  384. /// based on next block height.
  385. /// If no forks exist, returns the canonical key.
  386. pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
  387. // Grab a lock over current forks
  388. let forks = self.forks.read().await;
  389. // Grab next block height and current keys.
  390. // If no forks exist, use canonical keys
  391. let (next_block_height, rx_keys) = if forks.is_empty() {
  392. let (next_block_height, _) = self.blockchain.last()?;
  393. (next_block_height + 1, self.module.read().await.darkfi_rx_keys)
  394. } else {
  395. // Grab best fork and its last proposal
  396. let fork = &forks[best_fork_index(&forks)?];
  397. let last = fork.last_proposal()?;
  398. (last.block.header.height + 1, fork.module.darkfi_rx_keys)
  399. };
  400. // We only use the next key when the next block is the
  401. // height changing one.
  402. if next_block_height > RANDOMX_KEY_CHANGING_HEIGHT &&
  403. next_block_height % RANDOMX_KEY_CHANGING_HEIGHT == RANDOMX_KEY_CHANGE_DELAY
  404. {
  405. // Its safe to unwrap here since we know the key has been set
  406. Ok(rx_keys.1.unwrap())
  407. } else {
  408. Ok(rx_keys.0)
  409. }
  410. }
  411. /// Auxiliary function to grab best current fork full clone.
  412. pub async fn best_current_fork(&self) -> Result<Fork> {
  413. let forks = self.forks.read().await;
  414. let index = best_fork_index(&forks)?;
  415. forks[index].full_clone()
  416. }
  417. /// Auxiliary function to retrieve current best fork last header.
  418. /// If no forks exist, grab the last header from canonical.
  419. pub async fn best_fork_last_header(&self) -> Result<(u32, HeaderHash)> {
  420. // Grab a lock over current forks
  421. let forks = self.forks.read().await;
  422. // Check if node has any forks
  423. if forks.is_empty() {
  424. drop(forks);
  425. return self.blockchain.last()
  426. }
  427. // Grab best fork
  428. let fork = &forks[best_fork_index(&forks)?];
  429. // Grab its last header
  430. let last = fork.last_proposal()?;
  431. drop(forks);
  432. Ok((last.block.header.height, last.hash))
  433. }
  434. /// Auxiliary function to purge current forks and reset the ones
  435. /// starting with the provided prefix, excluding provided confirmed
  436. /// fork. Additionally, remove confirmed transactions from the
  437. /// forks mempools. This function assumes that the prefix blocks
  438. /// have already been appended to canonical chain from the
  439. /// confirmed fork.
  440. ///
  441. /// Note: Always remember to purge new trees from the database if
  442. /// not needed.
  443. pub async fn reset_forks(
  444. &self,
  445. prefix: &[HeaderHash],
  446. confirmed_fork_index: &usize,
  447. confirmed_txs: &[Transaction],
  448. ) -> Result<()> {
  449. // Grab a lock over current forks
  450. let mut forks = self.forks.write().await;
  451. // Find all the forks that start with the provided prefix,
  452. // excluding confirmed fork index, and remove their prefixed
  453. // proposals, and their corresponding diffs. If the fork is not
  454. // starting with the provided prefix, drop it.
  455. let excess = prefix.len();
  456. let prefix_last_index = excess - 1;
  457. let prefix_last = prefix.last().unwrap();
  458. let mut keep = vec![true; forks.len()];
  459. let confirmed_txs_hashes: Vec<TransactionHash> =
  460. confirmed_txs.iter().map(|tx| tx.hash()).collect();
  461. for (index, fork) in forks.iter_mut().enumerate() {
  462. if &index == confirmed_fork_index {
  463. // Remove confirmed proposals txs from fork's mempool
  464. fork.mempool.retain(|tx| !confirmed_txs_hashes.contains(tx));
  465. continue
  466. }
  467. if fork.proposals.is_empty() ||
  468. prefix_last_index >= fork.proposals.len() ||
  469. &fork.proposals[prefix_last_index] != prefix_last
  470. {
  471. keep[index] = false;
  472. continue
  473. }
  474. // Remove confirmed proposals txs from fork's mempool
  475. fork.mempool.retain(|tx| !confirmed_txs_hashes.contains(tx));
  476. // Remove the commited differences
  477. let rest_proposals = fork.proposals.split_off(excess);
  478. let rest_diffs = fork.diffs.split_off(excess);
  479. let mut diffs = fork.diffs.clone();
  480. fork.proposals = rest_proposals;
  481. fork.diffs = rest_diffs;
  482. for diff in diffs.iter_mut() {
  483. fork.overlay.lock().unwrap().overlay.lock().unwrap().remove_diff(diff);
  484. }
  485. }
  486. // Drop invalid forks
  487. let mut iter = keep.iter();
  488. forks.retain(|_| *iter.next().unwrap());
  489. // Remove confirmed proposals txs from the unporposed txs sled tree
  490. self.blockchain.remove_pending_txs_hashes(&confirmed_txs_hashes)?;
  491. // Drop forks lock
  492. drop(forks);
  493. Ok(())
  494. }
  495. /// Auxiliary function to fully purge current forks and leave only a new empty fork.
  496. pub async fn purge_forks(&self) -> Result<()> {
  497. debug!(target: "validator::consensus::purge_forks", "Purging current forks...");
  498. let mut forks = self.forks.write().await;
  499. *forks = vec![Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?];
  500. drop(forks);
  501. debug!(target: "validator::consensus::purge_forks", "Forks purged!");
  502. Ok(())
  503. }
  504. /// Auxiliary function to reset PoW module.
  505. pub async fn reset_pow_module(&self) -> Result<()> {
  506. debug!(target: "validator::consensus::reset_pow_module", "Resetting PoW module...");
  507. let mut module = self.module.write().await;
  508. *module = PoWModule::new(
  509. self.blockchain.clone(),
  510. module.target,
  511. module.fixed_difficulty.clone(),
  512. None,
  513. )?;
  514. drop(module);
  515. debug!(target: "validator::consensus::reset_pow_module", "PoW module reset successfully!");
  516. Ok(())
  517. }
  518. /// Auxiliary function to check current contracts states
  519. /// Monotree(SMT) validity in all active forks and canonical.
  520. pub async fn healthcheck(&self) -> Result<()> {
  521. // Grab a lock over current forks
  522. let lock = self.forks.read().await;
  523. // Grab current canonical contracts states monotree root
  524. let state_root = self.blockchain.contracts.get_state_monotree_root()?;
  525. // Check that the root matches last block header state root
  526. let last_block_state_root = self.blockchain.last_header()?.state_root;
  527. if state_root != last_block_state_root {
  528. return Err(Error::ContractsStatesRootError(
  529. blake3::Hash::from_bytes(state_root).to_string(),
  530. blake3::Hash::from_bytes(last_block_state_root).to_string(),
  531. ));
  532. }
  533. // Check each fork health
  534. for fork in lock.iter() {
  535. fork.healthcheck()?;
  536. }
  537. Ok(())
  538. }
  539. /// Auxiliary function to purge all unreferenced contract trees
  540. /// from the database.
  541. pub async fn purge_unreferenced_trees(
  542. &self,
  543. referenced_trees: &mut BTreeSet<IVec>,
  544. ) -> Result<()> {
  545. // Grab a lock over current forks
  546. let lock = self.forks.read().await;
  547. // Check if we have forks
  548. if lock.is_empty() {
  549. // If no forks exist, build a new one so we retrieve the
  550. // native/protected trees references.
  551. let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
  552. fork.referenced_trees(referenced_trees);
  553. } else {
  554. // Iterate over current forks to retrieve referenced trees
  555. for fork in lock.iter() {
  556. fork.referenced_trees(referenced_trees);
  557. }
  558. }
  559. // Retrieve current database trees
  560. let current_trees = self.blockchain.sled_db.tree_names();
  561. // Iterate over current database trees and drop unreferenced
  562. // contracts ones.
  563. for tree in current_trees {
  564. // Check if its referenced
  565. if referenced_trees.contains(&tree) {
  566. continue
  567. }
  568. // Check if its a contract tree pointer
  569. let Ok(tree) = deserialize::<[u8; 32]>(&tree) else { continue };
  570. // Drop it
  571. debug!(target: "validator::consensus::purge_unreferenced_trees", "Dropping unreferenced tree: {}", blake3::Hash::from(tree));
  572. self.blockchain.sled_db.drop_tree(tree)?;
  573. }
  574. Ok(())
  575. }
  576. /// Auxiliary function to purge all unproposed pending
  577. /// transactions from the database.
  578. pub async fn purge_unproposed_pending_txs(&self) -> Result<()> {
  579. // Grab a lock over current forks
  580. let mut forks = self.forks.write().await;
  581. // Keep track of proposed txs
  582. let mut proposed_txs = HashSet::new();
  583. // Iterate over all forks to find proposed txs
  584. for fork in forks.iter() {
  585. // Grab all current proposals transactions hashes
  586. let proposals_txs =
  587. fork.overlay.lock().unwrap().get_blocks_txs_hashes(&fork.proposals)?;
  588. for tx in proposals_txs {
  589. proposed_txs.insert(tx);
  590. }
  591. }
  592. // Iterate over all forks again to remove unproposed txs from
  593. // their mempools.
  594. for fork in forks.iter_mut() {
  595. fork.mempool.retain(|tx| proposed_txs.contains(tx));
  596. }
  597. // Remove unproposed txs from the pending store
  598. let proposed_txs: Vec<TransactionHash> = proposed_txs.into_iter().collect();
  599. self.blockchain.reset_pending_txs(&proposed_txs)?;
  600. Ok(())
  601. }
  602. }
  603. /// This struct represents a block proposal, used for consensus.
  604. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  605. pub struct Proposal {
  606. /// Block hash
  607. pub hash: HeaderHash,
  608. /// Block data
  609. pub block: BlockInfo,
  610. }
  611. impl Proposal {
  612. pub fn new(block: BlockInfo) -> Self {
  613. let hash = block.hash();
  614. Self { hash, block }
  615. }
  616. }
  617. impl From<Proposal> for BlockInfo {
  618. fn from(proposal: Proposal) -> BlockInfo {
  619. proposal.block
  620. }
  621. }
  622. /// Struct representing a forked blockchain state.
  623. ///
  624. /// An overlay over the original blockchain is used, containing all
  625. /// pending to-write records. Additionally, each fork keeps a vector of
  626. /// valid pending transactions hashes, in order of receival, and the
  627. /// proposals hashes sequence, for validations.
  628. #[derive(Clone)]
  629. pub struct Fork {
  630. /// Canonical (confirmed) blockchain
  631. pub blockchain: Blockchain,
  632. /// Overlay cache over canonical Blockchain
  633. pub overlay: BlockchainOverlayPtr,
  634. /// Current PoW module state
  635. pub module: PoWModule,
  636. /// Fork proposal hashes sequence
  637. pub proposals: Vec<HeaderHash>,
  638. /// Fork proposal overlay diffs sequence
  639. pub diffs: Vec<SledDbOverlayStateDiff>,
  640. /// Valid pending transaction hashes
  641. pub mempool: Vec<TransactionHash>,
  642. /// Current fork mining targets rank, cached for better performance
  643. pub targets_rank: BigUint,
  644. /// Current fork hashes rank, cached for better performance
  645. pub hashes_rank: BigUint,
  646. }
  647. impl Fork {
  648. pub async fn new(blockchain: Blockchain, module: PoWModule) -> Result<Self> {
  649. let mempool = blockchain.get_pending_txs()?.iter().map(|tx| tx.hash()).collect();
  650. let overlay = BlockchainOverlay::new(&blockchain)?;
  651. // Retrieve last block difficulty to access current ranks
  652. let last_difficulty = blockchain.last_block_difficulty()?;
  653. let targets_rank = last_difficulty.ranks.targets_rank;
  654. let hashes_rank = last_difficulty.ranks.hashes_rank;
  655. Ok(Self {
  656. blockchain,
  657. overlay,
  658. module,
  659. proposals: vec![],
  660. diffs: vec![],
  661. mempool,
  662. targets_rank,
  663. hashes_rank,
  664. })
  665. }
  666. /// Auxiliary function to append a proposal and update current fork rank.
  667. pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
  668. // Grab next mine target and difficulty
  669. let (next_target, next_difficulty) = self.module.next_mine_target_and_difficulty()?;
  670. // Calculate block rank
  671. let (target_distance_sq, hash_distance_sq) = block_rank(&proposal.block, &next_target)?;
  672. // Update fork ranks
  673. self.targets_rank += target_distance_sq.clone();
  674. self.hashes_rank += hash_distance_sq.clone();
  675. // Generate block difficulty and update PoW module
  676. let cumulative_difficulty =
  677. self.module.cumulative_difficulty.clone() + next_difficulty.clone();
  678. let ranks = BlockRanks::new(
  679. target_distance_sq,
  680. self.targets_rank.clone(),
  681. hash_distance_sq,
  682. self.hashes_rank.clone(),
  683. );
  684. let block_difficulty = BlockDifficulty::new(
  685. proposal.block.header.height,
  686. proposal.block.header.timestamp,
  687. next_difficulty,
  688. cumulative_difficulty,
  689. ranks,
  690. );
  691. self.module.append_difficulty(&self.overlay, &proposal.block.header, block_difficulty)?;
  692. // Push proposal's hash
  693. self.proposals.push(proposal.hash);
  694. // Push proposal overlay diff
  695. self.diffs.push(self.overlay.lock().unwrap().overlay.lock().unwrap().diff(&self.diffs)?);
  696. Ok(())
  697. }
  698. /// Auxiliary function to retrieve last proposal.
  699. pub fn last_proposal(&self) -> Result<Proposal> {
  700. let block = if let Some(last) = self.proposals.last() {
  701. self.overlay.lock().unwrap().get_blocks_by_hash(&[*last])?[0].clone()
  702. } else {
  703. self.overlay.lock().unwrap().last_block()?
  704. };
  705. Ok(Proposal::new(block))
  706. }
  707. /// Auxiliary function to compute forks' next block height.
  708. pub fn get_next_block_height(&self) -> Result<u32> {
  709. let proposal = self.last_proposal()?;
  710. Ok(proposal.block.header.height + 1)
  711. }
  712. /// Auxiliary function to retrieve unproposed valid transactions,
  713. /// along with their total gas used and total paid fees.
  714. ///
  715. /// Note: Always remember to purge new trees from the database if
  716. /// not needed.
  717. pub async fn unproposed_txs(
  718. &self,
  719. verifying_block_height: u32,
  720. verify_fees: bool,
  721. ) -> Result<(Vec<Transaction>, u64, u64)> {
  722. // Check if our mempool is not empty
  723. if self.mempool.is_empty() {
  724. return Ok((vec![], 0, 0))
  725. }
  726. // Transactions Merkle tree
  727. let mut tree = MerkleTree::new(1);
  728. // Total gas accumulators
  729. let mut total_gas_used = 0;
  730. let mut total_gas_paid = 0;
  731. // Map of ZK proof verifying keys for the current transaction batch
  732. let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  733. // Grab all current proposals transactions hashes
  734. let proposals_txs = self.overlay.lock().unwrap().get_blocks_txs_hashes(&self.proposals)?;
  735. // Iterate through all pending transactions in the forks' mempool
  736. let mut unproposed_txs = vec![];
  737. for tx in &self.mempool {
  738. // If the hash is contained in the proposals transactions vec, skip it
  739. if proposals_txs.contains(tx) {
  740. continue
  741. }
  742. // Retrieve the actual unproposed transaction
  743. let unproposed_tx =
  744. self.blockchain.transactions.get_pending(&[*tx], true)?[0].clone().unwrap();
  745. // Update the verifying keys map
  746. for call in &unproposed_tx.calls {
  747. vks.entry(call.data.contract_id.to_bytes()).or_default();
  748. }
  749. // Verify the transaction against current state
  750. self.overlay.lock().unwrap().checkpoint();
  751. let gas_data = match verify_transaction(
  752. &self.overlay,
  753. verifying_block_height,
  754. self.module.target,
  755. &unproposed_tx,
  756. &mut tree,
  757. &mut vks,
  758. verify_fees,
  759. )
  760. .await
  761. {
  762. Ok(gas_values) => gas_values,
  763. Err(e) => {
  764. debug!(target: "validator::consensus::unproposed_txs", "Transaction verification failed: {e}");
  765. self.overlay.lock().unwrap().revert_to_checkpoint();
  766. continue
  767. }
  768. };
  769. // Store the gas used by the verified transaction
  770. let tx_gas_used = gas_data.total_gas_used();
  771. // Calculate current accumulated gas usage
  772. let accumulated_gas_usage = total_gas_used + tx_gas_used;
  773. // Check gas limit - if accumulated gas used exceeds it, break out of loop
  774. if accumulated_gas_usage > BLOCK_GAS_LIMIT {
  775. warn!(
  776. target: "validator::consensus::unproposed_txs",
  777. "Retrieving transaction {tx} would exceed configured unproposed transaction gas limit: {accumulated_gas_usage} - {BLOCK_GAS_LIMIT}"
  778. );
  779. self.overlay.lock().unwrap().revert_to_checkpoint();
  780. break
  781. }
  782. // Update accumulated total gas
  783. total_gas_used += tx_gas_used;
  784. total_gas_paid += gas_data.paid;
  785. // Push the tx hash into the unproposed transactions vector
  786. unproposed_txs.push(unproposed_tx);
  787. }
  788. Ok((unproposed_txs, total_gas_used, total_gas_paid))
  789. }
  790. /// Auxiliary function to create a full clone using
  791. /// BlockchainOverlay::full_clone. Changes to this copy don't
  792. /// affect original fork overlay records, since underlying overlay
  793. /// pointer have been updated to the cloned one.
  794. pub fn full_clone(&self) -> Result<Self> {
  795. let blockchain = self.blockchain.clone();
  796. let overlay = self.overlay.lock().unwrap().full_clone()?;
  797. let module = self.module.clone();
  798. let proposals = self.proposals.clone();
  799. let diffs = self.diffs.clone();
  800. let mempool = self.mempool.clone();
  801. let targets_rank = self.targets_rank.clone();
  802. let hashes_rank = self.hashes_rank.clone();
  803. Ok(Self {
  804. blockchain,
  805. overlay,
  806. module,
  807. proposals,
  808. diffs,
  809. mempool,
  810. targets_rank,
  811. hashes_rank,
  812. })
  813. }
  814. /// Auxiliary function to check current contracts states
  815. /// Monotree(SMT) validity.
  816. ///
  817. /// Note: This should be executed on fresh forks and/or when
  818. /// a fork doesn't contain changes over the last appended
  819. // proposal.
  820. pub fn healthcheck(&self) -> Result<()> {
  821. // Grab current contracts states monotree root
  822. let state_root = self.overlay.lock().unwrap().contracts.get_state_monotree_root()?;
  823. // Check that the root matches last block header state root
  824. let last_block_state_root = self.last_proposal()?.block.header.state_root;
  825. if state_root != last_block_state_root {
  826. return Err(Error::ContractsStatesRootError(
  827. blake3::Hash::from_bytes(state_root).to_string(),
  828. blake3::Hash::from_bytes(last_block_state_root).to_string(),
  829. ));
  830. }
  831. Ok(())
  832. }
  833. /// Auxiliary function to retrieve all referenced trees from the
  834. /// fork overlay and insert them to provided `BTreeSet`.
  835. pub fn referenced_trees(&self, trees: &mut BTreeSet<IVec>) {
  836. // Grab its current overlay
  837. let fork_overlay = self.overlay.lock().unwrap();
  838. let overlay = fork_overlay.overlay.lock().unwrap();
  839. // Retrieve its initial trees
  840. for initial_tree in &overlay.state.initial_tree_names {
  841. trees.insert(initial_tree.clone());
  842. }
  843. // Retrieve its new trees
  844. for new_tree in &overlay.state.new_tree_names {
  845. trees.insert(new_tree.clone());
  846. }
  847. // Retrieve its dropped trees
  848. for dropped_tree in overlay.state.dropped_trees.keys() {
  849. trees.insert(dropped_tree.clone());
  850. }
  851. // Retrieve its protected trees
  852. for protected_tree in &overlay.state.protected_tree_names {
  853. trees.insert(protected_tree.clone());
  854. }
  855. }
  856. }