consensus.rs 22 KB

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