consensus.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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. blockchain::{expected_reward, PidOutput, PreviousSlot, Slot},
  20. crypto::{schnorr::SchnorrSecret, MerkleNode, MerkleTree, SecretKey},
  21. pasta::{group::ff::PrimeField, pallas},
  22. };
  23. use darkfi_serial::{async_trait, serialize, SerialDecodable, SerialEncodable};
  24. use log::{error, info, warn};
  25. use rand::rngs::OsRng;
  26. use crate::{
  27. blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header},
  28. tx::Transaction,
  29. util::time::{TimeKeeper, Timestamp},
  30. validator::{pid::slot_pid_output, verify_block, verify_transactions},
  31. Error, Result,
  32. };
  33. /// Consensus configuration
  34. const TXS_CAP: usize = 50;
  35. /// This struct represents the information required by the consensus algorithm
  36. pub struct Consensus {
  37. /// Canonical (finalized) blockchain
  38. pub blockchain: Blockchain,
  39. /// Helper structure to calculate time related operations
  40. pub time_keeper: TimeKeeper,
  41. /// Node is participating to consensus
  42. pub participating: bool,
  43. /// Last slot node check for finalization
  44. pub checked_finalization: u64,
  45. /// Fork chains containing block proposals
  46. pub forks: Vec<Fork>,
  47. /// Flag to enable testing mode
  48. pub testing_mode: bool,
  49. }
  50. impl Consensus {
  51. /// Generate a new Consensus state.
  52. pub fn new(blockchain: Blockchain, time_keeper: TimeKeeper, testing_mode: bool) -> Self {
  53. Self {
  54. blockchain,
  55. time_keeper,
  56. participating: false,
  57. checked_finalization: 0,
  58. forks: vec![],
  59. testing_mode,
  60. }
  61. }
  62. /// Generate current hot/live slot for all current forks.
  63. pub fn generate_slot(&mut self) -> Result<()> {
  64. // Grab current slot id
  65. let id = self.time_keeper.current_slot();
  66. // If no forks exist, create a new one as a basis to extend
  67. if self.forks.is_empty() {
  68. self.forks.push(Fork::new(&self.blockchain)?);
  69. }
  70. // Grab previous slot information
  71. let (producers, last_hashes, second_to_last_hashes) = self.previous_slot_info(id - 1)?;
  72. for fork in self.forks.iter_mut() {
  73. fork.generate_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
  74. }
  75. Ok(())
  76. }
  77. /// Retrieve previous slot producers, last proposal hashes,
  78. /// and their second to last hashes, from all current forks.
  79. fn previous_slot_info(&self, slot: u64) -> Result<(u64, Vec<blake3::Hash>, Vec<blake3::Hash>)> {
  80. let mut producers = 0;
  81. let mut last_hashes = vec![];
  82. let mut second_to_last_hashes = vec![];
  83. for fork in &self.forks {
  84. let last_proposal = fork.last_proposal()?;
  85. if last_proposal.block.header.slot == slot {
  86. producers += 1;
  87. }
  88. last_hashes.push(last_proposal.hash);
  89. second_to_last_hashes.push(last_proposal.block.header.previous);
  90. }
  91. Ok((producers, last_hashes, second_to_last_hashes))
  92. }
  93. /// Generate a block proposal for the current hot/live(last) slot,
  94. /// containing all pending transactions. Proposal extends the longest fork
  95. /// chain the node is holding. This should only be called after
  96. /// generate_slot(). Proposal is signed using provided secret key, which
  97. /// must also have signed the provided proposal transaction.
  98. pub async fn generate_proposal(
  99. &self,
  100. secret_key: SecretKey,
  101. proposal_tx: Transaction,
  102. ) -> Result<Proposal> {
  103. // Generate a time keeper for current slot
  104. let time_keeper = self.time_keeper.current();
  105. // Retrieve longest known fork
  106. let mut fork_index = 0;
  107. let mut max_fork_length = 0;
  108. for (index, fork) in self.forks.iter().enumerate() {
  109. if fork.proposals.len() > max_fork_length {
  110. fork_index = index;
  111. max_fork_length = fork.proposals.len();
  112. }
  113. }
  114. let fork = &self.forks[fork_index];
  115. // Grab forks' unproposed transactions and their root
  116. let unproposed_txs = fork.unproposed_txs(&self.blockchain, &time_keeper).await?;
  117. let mut tree = MerkleTree::new(100);
  118. // The following is pretty weird, so something better should be done.
  119. for tx in &unproposed_txs {
  120. let mut hash = [0_u8; 32];
  121. hash[0..31].copy_from_slice(&blake3::hash(&serialize(tx)).as_bytes()[0..31]);
  122. tree.append(MerkleNode::from(pallas::Base::from_repr(hash).unwrap()));
  123. }
  124. let root = tree.root(0).unwrap();
  125. // Grab forks' last block proposal(previous)
  126. let previous = fork.last_proposal()?;
  127. // Generate the new header
  128. let slot = fork.slots.last().unwrap();
  129. // TODO: verify if header timestamp should be blockchain or system timestamp
  130. let header = Header::new(
  131. previous.block.blockhash(),
  132. time_keeper.slot_epoch(slot.id),
  133. slot.id,
  134. Timestamp::current_time(),
  135. root,
  136. );
  137. // TODO: sign more stuff?
  138. // Sign block header using provided secret key
  139. let signature = secret_key.sign(&mut OsRng, &header.headerhash()?.as_bytes()[..]);
  140. // Generate the block and its proposal
  141. let block = BlockInfo::new(
  142. header,
  143. unproposed_txs,
  144. signature,
  145. proposal_tx,
  146. slot.last_eta,
  147. fork.slots.clone(),
  148. );
  149. let proposal = Proposal::new(block);
  150. Ok(proposal)
  151. }
  152. /// Given a proposal, the node verifys it and finds which fork it extends.
  153. /// If the proposal extends the canonical blockchain, a new fork chain is created.
  154. /// A proposal is considered valid when the following rules apply:
  155. /// 1. Node has not started current slot finalization
  156. /// 2. Proposal refers to current slot
  157. /// 3. Proposal hash matches the actual block one
  158. /// 4. Block transactions don't exceed set limit
  159. /// 5. If proposal extends a known fork, verify block slots
  160. /// correspond to the fork hot/live ones
  161. /// 6. Block is valid
  162. /// Additional validity rules can be applied.
  163. pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
  164. // Generate a time keeper for current slot
  165. let time_keeper = self.time_keeper.current();
  166. // Node have already checked for finalization in this slot (1)
  167. if time_keeper.verifying_slot <= self.checked_finalization {
  168. warn!(target: "validator::consensus::append_proposal", "Proposal received after finalization sync period.");
  169. return Err(Error::ProposalAfterFinalizationError)
  170. }
  171. // Proposal validations
  172. let hdr = &proposal.block.header;
  173. // Ignore proposal if not for current slot (2)
  174. if hdr.slot != time_keeper.verifying_slot {
  175. return Err(Error::ProposalNotForCurrentSlotError)
  176. }
  177. // Check if proposal hash matches actual one (3)
  178. let proposal_hash = proposal.block.blockhash();
  179. if proposal.hash != proposal_hash {
  180. warn!(
  181. target: "validator::consensus::append_proposal", "Received proposal contains mismatched hashes: {} - {}",
  182. proposal.hash, proposal_hash
  183. );
  184. return Err(Error::ProposalHashesMissmatchError)
  185. }
  186. // TODO: verify if this should happen here or not.
  187. // Check that proposal transactions don't exceed limit (4)
  188. if proposal.block.txs.len() > TXS_CAP {
  189. warn!(
  190. target: "validator::consensus::append_proposal", "Received proposal transactions exceed configured cap: {} - {}",
  191. proposal.block.txs.len(),
  192. TXS_CAP
  193. );
  194. return Err(Error::ProposalTxsExceedCapError)
  195. }
  196. // Check if proposal extends any existing forks
  197. let (mut fork, index) = self.find_extended_fork(proposal).await?;
  198. // Verify block slots correspond to the forks' hot/live ones (5)
  199. if !fork.slots.is_empty() && fork.slots != proposal.block.slots {
  200. return Err(Error::ProposalContainsUnknownSlots)
  201. }
  202. // Insert last block slot so transactions can be validated against.
  203. // Rest (empty) slots will be inserted along with the block.
  204. // Since this fork uses an overlay clone, original overlay is not affected.
  205. fork.overlay.lock().unwrap().slots.insert(&[proposal
  206. .block
  207. .slots
  208. .last()
  209. .unwrap()
  210. .clone()])?;
  211. // Grab overlay last block
  212. let previous = fork.overlay.lock().unwrap().last_block()?;
  213. // Retrieve expected reward
  214. let expected_reward = expected_reward(time_keeper.verifying_slot);
  215. // Verify proposal block (6)
  216. if verify_block(
  217. &fork.overlay,
  218. &time_keeper,
  219. &proposal.block,
  220. &previous,
  221. expected_reward,
  222. self.testing_mode,
  223. )
  224. .await
  225. .is_err()
  226. {
  227. error!(target: "validator::consensus::append_proposal", "Erroneous proposal block found");
  228. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  229. return Err(Error::BlockIsInvalid(proposal.hash.to_string()))
  230. };
  231. // If a fork index was found, replace forks with the mutated one,
  232. // otherwise push the new fork.
  233. fork.proposals.push(proposal.hash);
  234. fork.slots = vec![];
  235. match index {
  236. Some(i) => {
  237. self.forks[i] = fork;
  238. }
  239. None => {
  240. self.forks.push(fork);
  241. }
  242. }
  243. Ok(())
  244. }
  245. /// Given a proposal, find the index of the fork chain it extends, along with the specific
  246. /// extended proposal index.
  247. fn find_extended_fork_index(&self, proposal: &Proposal) -> Result<(usize, usize)> {
  248. for (f_index, fork) in self.forks.iter().enumerate() {
  249. // Traverse fork proposals sequence in reverse
  250. for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
  251. if &proposal.block.header.previous == p_hash {
  252. return Ok((f_index, p_index))
  253. }
  254. }
  255. }
  256. Err(Error::ExtendedChainIndexNotFound)
  257. }
  258. /// Given a proposal, find the fork chain it extends, and return its full clone.
  259. /// If the proposal extends the fork not on its tail, a new fork is created and
  260. /// we re-apply the proposals up to the extending one. If proposal extends canonical,
  261. /// a new fork is created. Additionally, we return the fork index if a new fork
  262. /// was not created, so caller can replace the fork.
  263. async fn find_extended_fork(&self, proposal: &Proposal) -> Result<(Fork, Option<usize>)> {
  264. // Check if proposal extends any fork
  265. let found = self.find_extended_fork_index(proposal);
  266. if found.is_err() {
  267. // Check if we extend canonical
  268. let (last_slot, last_block) = self.blockchain.last()?;
  269. if proposal.block.header.previous != last_block ||
  270. proposal.block.header.slot <= last_slot
  271. {
  272. return Err(Error::ExtendedChainIndexNotFound)
  273. }
  274. return Ok((Fork::new(&self.blockchain)?, None))
  275. }
  276. let (f_index, p_index) = found.unwrap();
  277. let original_fork = &self.forks[f_index];
  278. // Check if proposal extends fork at last proposal
  279. if p_index == (original_fork.proposals.len() - 1) {
  280. return Ok((original_fork.full_clone()?, Some(f_index)))
  281. }
  282. // Rebuild fork
  283. let mut fork = Fork::new(&self.blockchain)?;
  284. fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
  285. // Retrieve proposals blocks from original fork
  286. let blocks = &original_fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  287. // Retrieve last block
  288. let mut previous = &fork.overlay.lock().unwrap().last_block()?;
  289. // Create a time keeper to validate each proposal block
  290. let mut time_keeper = self.time_keeper.clone();
  291. // Validate and insert each block
  292. for block in blocks {
  293. // Use block slot in time keeper
  294. time_keeper.verifying_slot = block.header.slot;
  295. // Retrieve expected reward
  296. let expected_reward = expected_reward(time_keeper.verifying_slot);
  297. // Verify block
  298. if verify_block(
  299. &fork.overlay,
  300. &time_keeper,
  301. block,
  302. previous,
  303. expected_reward,
  304. self.testing_mode,
  305. )
  306. .await
  307. .is_err()
  308. {
  309. error!(target: "validator::consensus::find_extended_fork_overlay", "Erroneous block found in set");
  310. fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  311. return Err(Error::BlockIsInvalid(block.blockhash().to_string()))
  312. };
  313. // Use last inserted block as next iteration previous
  314. previous = block;
  315. }
  316. Ok((fork, None))
  317. }
  318. /// Node checks if any of the forks can be finalized.
  319. /// Consensus finalization logic:
  320. /// - If the node has observed the creation of a fork and no other forks exists at same or greater height,
  321. /// all proposals in that fork can be finalized (append to canonical blockchain).
  322. /// When a fork can be finalized, blocks(proposals) should be appended to canonical,
  323. /// and forks should be removed.
  324. pub async fn forks_finalization(&mut self) -> Result<Vec<BlockInfo>> {
  325. let slot = self.time_keeper.current_slot();
  326. info!(target: "validator::consensus::forks_finalization", "Started finalization check for slot: {}", slot);
  327. // Set last slot finalization check occured to current slot
  328. self.checked_finalization = slot;
  329. // First we find longest fork without any other forks at same height
  330. let mut fork_index = -1;
  331. let mut max_length = 0;
  332. for (index, fork) in self.forks.iter().enumerate() {
  333. let length = fork.proposals.len();
  334. // Check if less than max
  335. if length < max_length {
  336. continue
  337. }
  338. // Check if same length as max
  339. if length == max_length {
  340. // Setting fork_index so we know we have multiple
  341. // forks at same length.
  342. fork_index = -2;
  343. continue
  344. }
  345. // Set fork as max
  346. fork_index = index as i64;
  347. max_length = length;
  348. }
  349. // Check if we found any fork to finalize
  350. match fork_index {
  351. -2 => {
  352. info!(target: "validator::consensus::forks_finalization", "Eligible forks with same height exist, nothing to finalize.");
  353. return Ok(vec![])
  354. }
  355. -1 => {
  356. info!(target: "validator::consensus::forks_finalization", "Nothing to finalize.");
  357. }
  358. _ => {
  359. info!(target: "validator::consensus::forks_finalization", "Fork {} can be finalized!", fork_index)
  360. }
  361. }
  362. if max_length == 0 {
  363. return Ok(vec![])
  364. }
  365. // Starting finalization
  366. let fork = &self.forks[fork_index as usize];
  367. let finalized = fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
  368. info!(target: "validator::consensus::forks_finalization", "Finalized blocks: {}", finalized.len());
  369. Ok(finalized)
  370. }
  371. }
  372. /// This struct represents a block proposal, used for consensus.
  373. #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
  374. pub struct Proposal {
  375. /// Block hash
  376. pub hash: blake3::Hash,
  377. /// Block data
  378. pub block: BlockInfo,
  379. }
  380. impl Proposal {
  381. pub fn new(block: BlockInfo) -> Self {
  382. let hash = block.blockhash();
  383. Self { hash, block }
  384. }
  385. }
  386. impl From<Proposal> for BlockInfo {
  387. fn from(proposal: Proposal) -> BlockInfo {
  388. proposal.block
  389. }
  390. }
  391. /// This struct represents a forked blockchain state, using an overlay over original
  392. /// blockchain, containing all pending to-write records. Additionally, each fork
  393. /// keeps a vector of valid pending transactions hashes, in order of receival, and
  394. /// the proposals hashes sequence, for validations.
  395. #[derive(Clone)]
  396. pub struct Fork {
  397. /// Overlay cache over canonical Blockchain
  398. pub overlay: BlockchainOverlayPtr,
  399. /// Fork proposal hashes sequence
  400. pub proposals: Vec<blake3::Hash>,
  401. /// Hot/live slots
  402. pub slots: Vec<Slot>,
  403. /// Valid pending transaction hashes
  404. pub mempool: Vec<blake3::Hash>,
  405. }
  406. impl Fork {
  407. pub fn new(blockchain: &Blockchain) -> Result<Self> {
  408. let mempool =
  409. blockchain.get_pending_txs()?.iter().map(|tx| blake3::hash(&serialize(tx))).collect();
  410. let overlay = BlockchainOverlay::new(blockchain)?;
  411. Ok(Self { overlay, proposals: vec![], slots: vec![], mempool })
  412. }
  413. /// Auxiliary function to retrieve last proposal
  414. pub fn last_proposal(&self) -> Result<Proposal> {
  415. let block = if self.proposals.is_empty() {
  416. self.overlay.lock().unwrap().last_block()?
  417. } else {
  418. self.overlay.lock().unwrap().get_blocks_by_hash(&[*self.proposals.last().unwrap()])?[0]
  419. .clone()
  420. };
  421. Ok(Proposal::new(block))
  422. }
  423. /// Utility function to extract leader selection lottery randomness(eta),
  424. /// defined as the hash of the last block, converted to pallas base.
  425. fn get_last_eta(&self) -> Result<pallas::Base> {
  426. // Retrieve last block(or proposal) hash
  427. let hash = if self.proposals.is_empty() {
  428. self.overlay.lock().unwrap().last_block()?.blockhash()
  429. } else {
  430. *self.proposals.last().unwrap()
  431. };
  432. // Read first 240 bits
  433. let mut bytes: [u8; 32] = *hash.as_bytes();
  434. bytes[30] = 0;
  435. bytes[31] = 0;
  436. Ok(pallas::Base::from_repr(bytes).unwrap())
  437. }
  438. /// Auxiliary function to retrieve unproposed valid transactions.
  439. pub async fn unproposed_txs(
  440. &self,
  441. blockchain: &Blockchain,
  442. time_keeper: &TimeKeeper,
  443. ) -> Result<Vec<Transaction>> {
  444. // Retrieve all mempool transactions
  445. let mut unproposed_txs: Vec<Transaction> = blockchain
  446. .pending_txs
  447. .get(&self.mempool, true)?
  448. .iter()
  449. .map(|x| x.clone().unwrap())
  450. .collect();
  451. // Iterate over fork proposals to find already proposed transactions
  452. // and remove them from the unproposed_txs vector.
  453. let proposals = self.overlay.lock().unwrap().get_blocks_by_hash(&self.proposals)?;
  454. for proposal in proposals {
  455. for tx in &proposal.txs {
  456. unproposed_txs.retain(|x| x != tx);
  457. }
  458. }
  459. // Check if transactions exceed configured cap
  460. if unproposed_txs.len() > TXS_CAP {
  461. unproposed_txs = unproposed_txs[0..TXS_CAP].to_vec()
  462. }
  463. // Clone forks' overlay
  464. let overlay = self.overlay.lock().unwrap().full_clone()?;
  465. // Verify transactions
  466. let erroneous_txs = verify_transactions(&overlay, time_keeper, &unproposed_txs).await?;
  467. if !erroneous_txs.is_empty() {
  468. unproposed_txs.retain(|x| !erroneous_txs.contains(x));
  469. }
  470. Ok(unproposed_txs)
  471. }
  472. /// Generate current hot/live slot
  473. pub fn generate_slot(
  474. &mut self,
  475. id: u64,
  476. producers: u64,
  477. last_hashes: &[blake3::Hash],
  478. second_to_last_hashes: &[blake3::Hash],
  479. ) -> Result<()> {
  480. // Grab last known fork slot
  481. let previous_slot = if self.slots.is_empty() {
  482. self.overlay.lock().unwrap().slots.get_last()?
  483. } else {
  484. self.slots.last().unwrap().clone()
  485. };
  486. // Generate previous slot information
  487. let previous = PreviousSlot::new(
  488. producers,
  489. last_hashes.to_vec(),
  490. second_to_last_hashes.to_vec(),
  491. previous_slot.pid.error,
  492. );
  493. // Generate PID controller output
  494. let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot, producers);
  495. let pid = PidOutput::new(f, error, sigma1, sigma2);
  496. // Each slot starts as an empty slot(not reward) when generated, carrying
  497. // last eta
  498. let last_eta = self.get_last_eta()?;
  499. let total_tokens = previous_slot.total_tokens + previous_slot.reward;
  500. let reward = 0;
  501. let slot = Slot::new(id, previous, pid, last_eta, total_tokens, reward);
  502. self.slots.push(slot);
  503. Ok(())
  504. }
  505. /// Auxiliary function to create a full clone using BlockchainOverlay::full_clone.
  506. /// Changes to this copy don't affect original fork overlay records, since underlying
  507. /// overlay pointer have been updated to the cloned one.
  508. pub fn full_clone(&self) -> Result<Self> {
  509. let overlay = self.overlay.lock().unwrap().full_clone()?;
  510. let proposals = self.proposals.clone();
  511. let slots = self.slots.clone();
  512. let mempool = self.mempool.clone();
  513. Ok(Self { overlay, proposals, slots, mempool })
  514. }
  515. }