state.rs 20 KB

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