state.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. // TODO: Use sets instead of vectors where possible.
  2. use std::time::Duration;
  3. use async_std::sync::{Arc, RwLock};
  4. use chrono::{NaiveDateTime, Utc};
  5. use fxhash::FxBuildHasher;
  6. use indexmap::IndexMap;
  7. use log::{debug, error, info, warn};
  8. use rand::{rngs::OsRng, Rng};
  9. use super::{
  10. Block, BlockProposal, Metadata, Participant, ProposalChain, StreamletMetadata, Timestamp, Tx,
  11. Vote,
  12. };
  13. use crate::{
  14. blockchain::Blockchain,
  15. crypto::{
  16. keypair::{PublicKey, SecretKey},
  17. schnorr::{SchnorrPublic, SchnorrSecret},
  18. },
  19. util::serial::{serialize, Encodable},
  20. Result,
  21. };
  22. type FxIndexMap<K, V> = IndexMap<K, V, FxBuildHasher>;
  23. const DELTA: u64 = 10;
  24. /// This struct represents the information required by the consensus algorithm
  25. #[derive(Debug)]
  26. pub struct ConsensusState {
  27. /// Genesis block creation timestamp
  28. pub genesis_ts: Timestamp,
  29. /// Genesis block hash
  30. pub genesis_block: blake3::Hash,
  31. /// Fork chains containing block proposals
  32. pub proposals: Vec<ProposalChain>,
  33. /// Orphan votes pool, in case a vote reaches a node before the
  34. /// corresponding block
  35. pub orphan_votes: Vec<Vote>,
  36. /// Validators currently participating in the consensus
  37. pub participants: FxIndexMap<u64, Participant>,
  38. /// Validators to be added on the next epoch as participants
  39. pub pending_participants: Vec<Participant>,
  40. }
  41. impl ConsensusState {
  42. pub fn new(genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Result<Self> {
  43. let genesis_block =
  44. blake3::hash(&serialize(&Block::genesis_block(genesis_ts, genesis_data)));
  45. Ok(Self {
  46. genesis_ts,
  47. genesis_block,
  48. proposals: vec![],
  49. orphan_votes: vec![],
  50. participants: FxIndexMap::with_hasher(FxBuildHasher::default()),
  51. pending_participants: vec![],
  52. })
  53. }
  54. }
  55. /// Atomic pointer to validator state.
  56. pub type ValidatorStatePtr = Arc<RwLock<ValidatorState>>;
  57. /// This struct represents the state of a validator node.
  58. pub struct ValidatorState {
  59. /// Validator ID
  60. pub id: u64,
  61. /// Secret key, to sign messages
  62. pub secret: SecretKey,
  63. /// Validator public key
  64. pub public: PublicKey,
  65. /// Hot/Live data used by the consensus algorithm
  66. pub consensus: ConsensusState,
  67. /// Canonical (finalized) blockchain
  68. pub blockchain: Blockchain,
  69. /// Pending transactions
  70. pub unconfirmed_txs: Vec<Tx>,
  71. }
  72. impl ValidatorState {
  73. // TODO: Clock sync
  74. // TODO: ID shouldn't be done like this
  75. pub fn new(
  76. db: &sled::Db, // <-- TODO: Avoid this with some wrapping, sled should only be in blockchain
  77. id: u64,
  78. genesis_ts: Timestamp,
  79. genesis_data: blake3::Hash,
  80. ) -> Result<ValidatorStatePtr> {
  81. let secret = SecretKey::random(&mut OsRng);
  82. let public = PublicKey::from_secret(secret);
  83. let consensus = ConsensusState::new(genesis_ts, genesis_data)?;
  84. let blockchain = Blockchain::new(db, genesis_ts, genesis_data)?;
  85. let unconfirmed_txs = vec![];
  86. let state = Arc::new(RwLock::new(ValidatorState {
  87. id,
  88. secret,
  89. public,
  90. consensus,
  91. blockchain,
  92. unconfirmed_txs,
  93. }));
  94. Ok(state)
  95. }
  96. /// The node retrieves a transaction and appends it to the unconfirmed
  97. /// transactions list. Additional validity rules must be defined by the
  98. /// protocol for transactions.
  99. pub fn append_tx(&mut self, tx: Tx) -> bool {
  100. if self.unconfirmed_txs.contains(&tx) {
  101. debug!("append_tx(): We already have this tx");
  102. return false
  103. }
  104. debug!("append_tx(): Appended tx to mempool");
  105. self.unconfirmed_txs.push(tx);
  106. true
  107. }
  108. /// Calculates current epoch, based on elapsed time from the genesis block.
  109. /// Epoch duration is configured using the `DELTA` value.
  110. pub fn current_epoch(&self) -> u64 {
  111. self.consensus.genesis_ts.elapsed() / (2 * DELTA)
  112. }
  113. /// Calculates seconds until next epoch starting time.
  114. /// Epochs durationis configured using the delta value.
  115. pub fn next_epoch_start(&self) -> Duration {
  116. let start_time = NaiveDateTime::from_timestamp(self.consensus.genesis_ts.0, 0);
  117. let current_epoch = self.current_epoch() + 1;
  118. let next_epoch_start = (current_epoch * (2 * DELTA)) + (start_time.timestamp() as u64);
  119. let next_epoch_start = NaiveDateTime::from_timestamp(next_epoch_start as i64, 0);
  120. let current_time = NaiveDateTime::from_timestamp(Utc::now().timestamp(), 0);
  121. let diff = next_epoch_start - current_time;
  122. Duration::new(diff.num_seconds().try_into().unwrap(), 0)
  123. }
  124. /// Find epoch leader, using a simple hash method.
  125. /// Leader calculation is based on how many nodes are participating
  126. /// in the network.
  127. pub fn epoch_leader(&mut self) -> u64 {
  128. let len = self.consensus.participants.len();
  129. assert!(len > 0);
  130. let idx = rand::thread_rng().gen_range(0..len);
  131. self.consensus.participants.get_index(idx).unwrap().1.id
  132. }
  133. /// Check if we're the current epoch leader
  134. pub fn is_epoch_leader(&mut self) -> bool {
  135. self.id == self.epoch_leader()
  136. }
  137. /// Generate a block proposal for the current epoch, containing all
  138. /// unconfirmed transactions. Proposal extends the longest notarized fork
  139. /// chain the node is holding.
  140. pub fn propose(&self) -> Result<Option<BlockProposal>> {
  141. let epoch = self.current_epoch();
  142. let prev_hash = self.longest_notarized_chain_last_hash().unwrap();
  143. let unproposed_txs = self.unproposed_txs();
  144. let metadata = Metadata::new(
  145. Timestamp::current_time(),
  146. String::from("proof"),
  147. String::from("r"),
  148. String::from("s"),
  149. );
  150. let sm = StreamletMetadata::new(self.consensus.participants.values().cloned().collect());
  151. let prop = BlockProposal::to_proposal_hash(prev_hash, epoch, &unproposed_txs, &metadata);
  152. let signed_proposal = self.secret.sign(&prop.as_bytes()[..]);
  153. Ok(Some(BlockProposal::new(
  154. self.public,
  155. signed_proposal,
  156. self.id,
  157. prev_hash,
  158. epoch,
  159. unproposed_txs,
  160. metadata,
  161. sm,
  162. )))
  163. }
  164. /// Retrieve all unconfirmed transactions not proposed in previous blocks.
  165. pub fn unproposed_txs(&self) -> Vec<Tx> {
  166. let mut unproposed_txs = self.unconfirmed_txs.clone();
  167. for chain in &self.consensus.proposals {
  168. for proposal in &chain.proposals {
  169. for tx in &proposal.block.txs {
  170. if let Some(pos) = unproposed_txs.iter().position(|txs| *txs == *tx) {
  171. unproposed_txs.remove(pos);
  172. }
  173. }
  174. }
  175. }
  176. unproposed_txs
  177. }
  178. /// Finds the longest fully notarized blockchain the node holds and
  179. /// returns the last block hash.
  180. pub fn longest_notarized_chain_last_hash(&self) -> Result<blake3::Hash> {
  181. let hash = if !self.consensus.proposals.is_empty() {
  182. let mut longest_notarized_chain = &self.consensus.proposals[0];
  183. let mut length = longest_notarized_chain.proposals.len();
  184. if self.consensus.proposals.len() > 1 {
  185. for chain in &self.consensus.proposals[1..] {
  186. if chain.notarized() && chain.proposals.len() > length {
  187. length = chain.proposals.len();
  188. longest_notarized_chain = chain;
  189. }
  190. }
  191. }
  192. longest_notarized_chain.proposals.last().unwrap().hash()
  193. } else {
  194. self.blockchain.last()?.unwrap().1
  195. };
  196. Ok(hash)
  197. }
  198. /// Receive the proposed block, verify its sender (epoch leader),
  199. /// and proceed with voting on it.
  200. pub fn receive_proposal(&mut self, proposal: &BlockProposal) -> Result<Option<Vote>> {
  201. let leader = self.epoch_leader();
  202. if leader != proposal.id {
  203. warn!(
  204. "Received proposal not from epoch leader ({}), but from ({})",
  205. leader, proposal.id
  206. );
  207. return Ok(None)
  208. }
  209. if !proposal.public_key.verify(
  210. BlockProposal::to_proposal_hash(
  211. proposal.block.st,
  212. proposal.block.sl,
  213. &proposal.block.txs,
  214. &proposal.block.metadata,
  215. )
  216. .as_bytes(),
  217. &proposal.signature,
  218. ) {
  219. warn!("Proposer ({}) signature could not be verified", proposal.id);
  220. return Ok(None)
  221. }
  222. self.vote(proposal)
  223. }
  224. /// Given a proposal, the node finds which blockchain it extends.
  225. /// If the proposal extends the canonical blockchain, a new fork chain
  226. /// is created. The node votes on the proposal only if it extends the
  227. /// longest notarized fork chain it has seen.
  228. pub fn vote(&mut self, proposal: &BlockProposal) -> Result<Option<Vote>> {
  229. self.zero_participants_check();
  230. let mut proposal = proposal.clone();
  231. // Generate proposal hash
  232. let proposal_hash = proposal.hash();
  233. // Add orphan votes
  234. let mut orphans = Vec::new();
  235. for vote in self.consensus.orphan_votes.iter() {
  236. if vote.proposal == proposal_hash {
  237. proposal.block.sm.votes.push(vote.clone());
  238. orphans.push(vote.clone());
  239. }
  240. }
  241. for vote in orphans {
  242. self.consensus.orphan_votes.retain(|v| *v != vote);
  243. }
  244. let index = self.find_extended_chain_index(&proposal)?;
  245. if index == -2 {
  246. return Ok(None)
  247. }
  248. let chain = match index {
  249. -1 => {
  250. let pc = ProposalChain::new(self.consensus.genesis_block, proposal.clone());
  251. self.consensus.proposals.push(pc);
  252. self.consensus.proposals.last().unwrap()
  253. }
  254. _ => {
  255. self.consensus.proposals[index as usize].add(&proposal);
  256. &self.consensus.proposals[index as usize]
  257. }
  258. };
  259. if !self.extends_notarized_chain(chain) {
  260. debug!("vote(): Proposal does not extend notarized chain");
  261. return Ok(None)
  262. }
  263. let signed_hash = self.secret.sign(&serialize(&proposal_hash));
  264. Ok(Some(Vote::new(self.public, signed_hash, proposal_hash, proposal.block.sl, self.id)))
  265. }
  266. /// Verify if the provided chain is notarized excluding the last block.
  267. pub fn extends_notarized_chain(&self, chain: &ProposalChain) -> bool {
  268. for proposal in &chain.proposals[..(chain.proposals.len() - 1)] {
  269. if !proposal.block.sm.notarized {
  270. return false
  271. }
  272. }
  273. true
  274. }
  275. /// Given a proposal, find the index of the chain it extends.
  276. pub fn find_extended_chain_index(&self, proposal: &BlockProposal) -> Result<i64> {
  277. for (index, chain) in self.consensus.proposals.iter().enumerate() {
  278. let last = chain.proposals.last().unwrap();
  279. let hash = last.hash();
  280. if proposal.block.st == hash && proposal.block.sl > last.block.sl {
  281. return Ok(index as i64)
  282. }
  283. if proposal.block.st == last.block.st && proposal.block.sl == last.block.sl {
  284. debug!("find_extended_chain_index(): Proposal already received");
  285. return Ok(-2)
  286. }
  287. }
  288. let (last_sl, last_block) = self.blockchain.last()?.unwrap();
  289. if proposal.block.st != last_block || proposal.block.sl <= last_sl {
  290. debug!("find_extended_chain_index(): Proposal doesn't extend any known chain");
  291. return Ok(-2)
  292. }
  293. Ok(-1)
  294. }
  295. /// Receive a vote for a proposal.
  296. /// First, sender is verified using their public key.
  297. /// The proposal is then searched for in the node's fork chains.
  298. /// If the vote wasn't received before, it is appended to the proposal
  299. /// votes list.
  300. /// When a node sees 2n/3 votes for a proposal, it notarizes it.
  301. /// When a proposal gets notarized, the transactions it contains are
  302. /// removed from the node's unconfirmed tx list.
  303. /// Finally, we check if the notarization of the proposal can finalize
  304. /// parent proposals in its chain.
  305. pub fn receive_vote(&mut self, vote: &Vote) -> Result<bool> {
  306. let mut encoded_proposal = vec![];
  307. match vote.proposal.encode(&mut encoded_proposal) {
  308. Ok(_) => (),
  309. Err(e) => {
  310. error!(target: "consensus", "Proposal encoding failed: {:?}", e);
  311. return Ok(false)
  312. }
  313. };
  314. if !vote.public_key.verify(&encoded_proposal, &vote.vote) {
  315. warn!(target: "consensus", "Voter ({}), signature couldn't be verified", vote.id);
  316. return Ok(false)
  317. }
  318. let node_count = self.consensus.participants.len();
  319. self.zero_participants_check();
  320. // Checking that the voter can actually vote.
  321. match self.consensus.participants.get(&vote.id) {
  322. Some(participant) => {
  323. if self.current_epoch() <= participant.joined {
  324. warn!(target: "consensus", "Voter ({}) joined after current epoch.", vote.id);
  325. return Ok(false)
  326. }
  327. }
  328. None => {
  329. warn!(target: "consensus", "Voter ({}) is not a participant!", vote.id);
  330. return Ok(false)
  331. }
  332. }
  333. let proposal = match self.find_proposal(&vote.proposal) {
  334. Ok(v) => v,
  335. Err(e) => {
  336. error!(target: "consensus", "find_proposal() failed: {}", e);
  337. return Err(e)
  338. }
  339. };
  340. if proposal.is_none() {
  341. warn!(target: "consensus", "Received vote for unknown proposal.");
  342. if !self.consensus.orphan_votes.contains(vote) {
  343. self.consensus.orphan_votes.push(vote.clone());
  344. }
  345. return Ok(false)
  346. }
  347. let (proposal, chain_idx) = proposal.unwrap();
  348. if proposal.block.sm.votes.contains(vote) {
  349. debug!("receive_vote(): Already seen this proposal");
  350. return Ok(false)
  351. }
  352. proposal.block.sm.votes.push(vote.clone());
  353. if !proposal.block.sm.notarized && proposal.block.sm.votes.len() > (2 * node_count / 3) {
  354. debug!("receive_vote(): Notarized a block");
  355. proposal.block.sm.notarized = true;
  356. match self.chain_finalization(chain_idx) {
  357. Ok(()) => {}
  358. Err(e) => {
  359. error!(target: "consensus", "Block finalization failed: {}", e);
  360. return Err(e)
  361. }
  362. }
  363. }
  364. // Updating participant vote
  365. let mut participant = match self.consensus.participants.get(&vote.id) {
  366. Some(p) => p.clone(),
  367. None => Participant::new(vote.id, vote.sl),
  368. };
  369. match participant.voted {
  370. Some(voted) => {
  371. if vote.sl > voted {
  372. participant.voted = Some(vote.sl);
  373. }
  374. }
  375. None => participant.voted = Some(vote.sl),
  376. }
  377. self.consensus.participants.insert(participant.id, participant);
  378. Ok(true)
  379. }
  380. /// Search the chains we're holding for the given proposal.
  381. pub fn find_proposal(
  382. &mut self,
  383. vote_proposal: &blake3::Hash,
  384. ) -> Result<Option<(&mut BlockProposal, i64)>> {
  385. for (index, chain) in &mut self.consensus.proposals.iter_mut().enumerate() {
  386. for proposal in chain.proposals.iter_mut().rev() {
  387. let proposal_hash = proposal.hash();
  388. if vote_proposal == &proposal_hash {
  389. return Ok(Some((proposal, index as i64)))
  390. }
  391. }
  392. }
  393. Ok(None)
  394. }
  395. /// Provided an index, the node checks if the chain can be finalized.
  396. /// Consensus finalization logic:
  397. /// - If the node has observed the notarization of 3 consecutive
  398. /// proposals in a fork chain, it finalizes (appends to canonical
  399. /// blockchain) all proposals up to the middle block.
  400. /// When fork chain proposals are finalized, the rest of fork chains not
  401. /// starting by those proposals are removed.
  402. pub fn chain_finalization(&mut self, chain_index: i64) -> Result<()> {
  403. let chain = &mut self.consensus.proposals[chain_index as usize];
  404. if chain.proposals.len() < 3 {
  405. debug!(
  406. "chain_finalization(): Less than 3 proposals in chain {}, nothing to finalize",
  407. chain_index
  408. );
  409. return Ok(())
  410. }
  411. let mut consecutive = 0;
  412. for proposal in &chain.proposals {
  413. if proposal.block.sm.notarized {
  414. consecutive += 1;
  415. continue
  416. }
  417. break
  418. }
  419. if consecutive < 3 {
  420. debug!(
  421. "chain_finalization(): Less than 3 notarized blocks in chain {}, nothing to finalize",
  422. chain_index
  423. );
  424. return Ok(())
  425. }
  426. let mut finalized = vec![];
  427. for proposal in &mut chain.proposals[..(consecutive - 1)] {
  428. proposal.block.sm.finalized = true;
  429. finalized.push(proposal.clone().into());
  430. for tx in proposal.block.txs.clone() {
  431. if let Some(pos) = self.unconfirmed_txs.iter().position(|txs| *txs == tx) {
  432. self.unconfirmed_txs.remove(pos);
  433. }
  434. }
  435. }
  436. chain.proposals.drain(0..(consecutive - 1));
  437. info!(target: "consensus", "Adding finalized block to canonical chain");
  438. let blockhashes = match self.blockchain.add(&finalized) {
  439. Ok(v) => v,
  440. Err(e) => {
  441. error!(target: "consensus", "Failed appending finalized blocks to canonical chain: {}", e);
  442. return Err(e)
  443. }
  444. };
  445. let last_block = *blockhashes.last().unwrap();
  446. let last_sl = finalized.last().unwrap().sl;
  447. let mut dropped = vec![];
  448. for chain in self.consensus.proposals.iter() {
  449. let first = chain.proposals.first().unwrap();
  450. if first.block.st != last_block || first.block.sl <= last_sl {
  451. dropped.push(chain.clone());
  452. }
  453. }
  454. for chain in dropped {
  455. self.consensus.proposals.retain(|c| *c != chain);
  456. }
  457. // Remove orphan votes
  458. let mut orphans = vec![];
  459. for vote in self.consensus.orphan_votes.iter() {
  460. if vote.sl <= last_sl {
  461. orphans.push(vote.clone());
  462. }
  463. }
  464. for vote in orphans {
  465. self.consensus.orphan_votes.retain(|v| *v != vote);
  466. }
  467. Ok(())
  468. }
  469. /// Append a new participant to the pending participants list.
  470. pub fn append_participant(&mut self, participant: Participant) -> bool {
  471. if self.consensus.pending_participants.contains(&participant) {
  472. return false
  473. }
  474. self.consensus.pending_participants.push(participant);
  475. true
  476. }
  477. /// Prevent the extreme case scenario where network is initialized, but
  478. /// some nodes have not pushed the initial participants in the map.
  479. pub fn zero_participants_check(&mut self) {
  480. if self.consensus.participants.is_empty() {
  481. debug!("zero_participants_check(): Participants are empty, trying to add pending ones");
  482. for participant in &self.consensus.pending_participants {
  483. self.consensus.participants.insert(participant.id, participant.clone());
  484. }
  485. if self.consensus.participants.is_empty() {
  486. debug!("zero_participants_check(): Didn't manage to add any participant, pending were empty");
  487. }
  488. self.consensus.pending_participants = Vec::new();
  489. }
  490. }
  491. /// Refresh the participants map, to retain only the active ones.
  492. /// Active nodes are considered those who joined or voted on a previous epoch.
  493. pub fn refresh_participants(&mut self) {
  494. debug!("refresh_participants(): Adding pending participants");
  495. for participant in &self.consensus.pending_participants {
  496. self.consensus.participants.insert(participant.id, participant.clone());
  497. }
  498. if self.consensus.participants.is_empty() {
  499. debug!(
  500. "refresh_participants(): Didn't manage to add any participant, pending were empty"
  501. );
  502. }
  503. self.consensus.pending_participants = vec![];
  504. let mut inactive = Vec::new();
  505. let previous_epoch = self.current_epoch() - 1;
  506. for (index, participant) in self.consensus.participants.clone().iter() {
  507. match participant.voted {
  508. Some(epoch) => {
  509. if epoch < previous_epoch {
  510. inactive.push(*index);
  511. }
  512. }
  513. None => {
  514. if participant.joined < previous_epoch {
  515. inactive.push(*index);
  516. }
  517. }
  518. }
  519. }
  520. for index in inactive {
  521. self.consensus.participants.remove(&index);
  522. }
  523. }
  524. /// Utility function to reset the current consensus state.
  525. pub fn reset_consensus_state(&mut self) -> Result<()> {
  526. let genesis_ts = self.consensus.genesis_ts.clone();
  527. let genesis_block = self.consensus.genesis_block.clone();
  528. let consensus = ConsensusState {
  529. genesis_ts,
  530. genesis_block,
  531. proposals: vec![],
  532. orphan_votes: vec![],
  533. participants: FxIndexMap::with_hasher(FxBuildHasher::default()),
  534. pending_participants: vec![],
  535. };
  536. self.consensus = consensus;
  537. Ok(())
  538. }
  539. }