|
|
@@ -4,6 +4,7 @@ use rand::rngs::OsRng;
|
|
|
use std::{
|
|
|
collections::{hash_map::DefaultHasher, BTreeMap},
|
|
|
hash::{Hash, Hasher},
|
|
|
+ path::PathBuf,
|
|
|
sync::{Arc, RwLock},
|
|
|
time::Duration,
|
|
|
};
|
|
|
@@ -13,61 +14,106 @@ use crate::{
|
|
|
keypair::{PublicKey, SecretKey},
|
|
|
schnorr::{SchnorrPublic, SchnorrSecret},
|
|
|
},
|
|
|
- encode_payload,
|
|
|
util::serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable},
|
|
|
Error, Result,
|
|
|
};
|
|
|
|
|
|
use super::{
|
|
|
- block::{Block, BlockProposal},
|
|
|
+ block::BlockProposal,
|
|
|
blockchain::{Blockchain, ProposalsChain},
|
|
|
participant::Participant,
|
|
|
tx::Tx,
|
|
|
- util::{get_current_time, Timestamp, GENESIS_HASH_BYTES},
|
|
|
+ util::{get_current_time, to_block_serial, Timestamp, GENESIS_HASH_BYTES},
|
|
|
vote::Vote,
|
|
|
};
|
|
|
|
|
|
const DELTA: u64 = 60;
|
|
|
-const SLED_STATE_TREE: &[u8] = b"_state";
|
|
|
+const SLED_CONSESUS_STATE_TREE: &[u8] = b"_consensus_state";
|
|
|
|
|
|
-/// Atomic pointer to state.
|
|
|
-pub type StatePtr = Arc<RwLock<State>>;
|
|
|
-
|
|
|
-/// This struct represents the state of a consensus node.
|
|
|
-/// Each node is numbered and has a secret-public keys pair, to sign messages.
|
|
|
-/// Nodes hold the canonical(finalized) blockchain, a set of fork chains containing proposals
|
|
|
-/// and a set of unconfirmed pending transactions.
|
|
|
-/// Additionally, each node keeps tracks of all participating nodes.
|
|
|
+/// This struct represents the information required by the consensus algorithm.
|
|
|
+/// Last finalized block hash and slot are used because SLED order follows the Ord implementation for Vec<u8>.
|
|
|
#[derive(Debug, SerialEncodable, SerialDecodable)]
|
|
|
-pub struct State {
|
|
|
- pub id: u64,
|
|
|
+pub struct ConsensusState {
|
|
|
+ /// Genesis block creation timestamp
|
|
|
pub genesis: Timestamp,
|
|
|
- pub secret: SecretKey,
|
|
|
- pub public: PublicKey,
|
|
|
- pub blockchain: Blockchain,
|
|
|
+ /// Last finalized block hash,
|
|
|
+ pub last_block: blake3::Hash,
|
|
|
+ /// Last finalized block slot,
|
|
|
+ pub last_sl: u64,
|
|
|
+ /// Fork chains containing block proposals
|
|
|
pub proposals: Vec<ProposalsChain>,
|
|
|
- pub unconfirmed_txs: Vec<Tx>,
|
|
|
+ /// Orphan votes pool, in case a vote reaches a node before the corresponding block
|
|
|
pub orphan_votes: Vec<Vote>,
|
|
|
+ /// Validators currently participating in the concensus
|
|
|
pub participants: BTreeMap<u64, Participant>,
|
|
|
+ /// Validators to be added on next epoch as participants
|
|
|
pub pending_participants: Vec<Participant>,
|
|
|
}
|
|
|
|
|
|
-impl State {
|
|
|
- pub fn new(id: u64, genesis: Timestamp, init_block: Block) -> State {
|
|
|
+impl ConsensusState {
|
|
|
+ pub fn new(db: &sled::Db, id: u64, genesis: i64) -> Result<ConsensusState> {
|
|
|
+ let tree = db.open_tree(SLED_CONSESUS_STATE_TREE)?;
|
|
|
+ let consensus = if let Some(found) = tree.get(id.to_ne_bytes())? {
|
|
|
+ deserialize(&found).unwrap()
|
|
|
+ } else {
|
|
|
+ let hash = blake3::Hash::from(GENESIS_HASH_BYTES);
|
|
|
+ let genesis_hash = blake3::hash(&to_block_serial(hash, 0, &vec![]));
|
|
|
+ let consensus = ConsensusState {
|
|
|
+ genesis: Timestamp(genesis),
|
|
|
+ last_block: genesis_hash,
|
|
|
+ last_sl: 0,
|
|
|
+ proposals: Vec::new(),
|
|
|
+ orphan_votes: Vec::new(),
|
|
|
+ participants: BTreeMap::new(),
|
|
|
+ pending_participants: Vec::new(),
|
|
|
+ };
|
|
|
+ let serialized = serialize(&consensus);
|
|
|
+ tree.insert(id.to_ne_bytes(), serialized)?;
|
|
|
+ consensus
|
|
|
+ };
|
|
|
+ Ok(consensus)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Atomic pointer to validator state.
|
|
|
+pub type ValidatorStatePtr = Arc<RwLock<ValidatorState>>;
|
|
|
+
|
|
|
+/// This struct represents the state of a validator node.
|
|
|
+pub struct ValidatorState {
|
|
|
+ /// Validator id
|
|
|
+ pub id: u64,
|
|
|
+ /// Secret key, to sign messages
|
|
|
+ pub secret: SecretKey,
|
|
|
+ /// Validator public key
|
|
|
+ pub public: PublicKey,
|
|
|
+ /// Sled database for storage
|
|
|
+ pub db: sled::Db,
|
|
|
+ /// Hot/live data used by the consensus algorithm
|
|
|
+ pub consensus: ConsensusState,
|
|
|
+ /// Canonical (finalized) blockchain
|
|
|
+ pub blockchain: Blockchain,
|
|
|
+ /// Pending transactions
|
|
|
+ pub unconfirmed_txs: Vec<Tx>,
|
|
|
+}
|
|
|
+
|
|
|
+impl ValidatorState {
|
|
|
+ pub fn new(db_path: PathBuf, id: u64, genesis: i64) -> Result<ValidatorStatePtr> {
|
|
|
// TODO: clock sync
|
|
|
let secret = SecretKey::random(&mut OsRng);
|
|
|
- State {
|
|
|
+ let db = sled::open(db_path)?;
|
|
|
+ let public = PublicKey::from_secret(secret);
|
|
|
+ let consensus = ConsensusState::new(&db, id, genesis)?;
|
|
|
+ let blockchain = Blockchain::new(&db)?;
|
|
|
+ let unconfirmed_txs = Vec::new();
|
|
|
+ Ok(Arc::new(RwLock::new(ValidatorState {
|
|
|
id,
|
|
|
- genesis,
|
|
|
secret,
|
|
|
- public: PublicKey::from_secret(secret),
|
|
|
- blockchain: Blockchain::new(init_block),
|
|
|
- proposals: Vec::new(),
|
|
|
- unconfirmed_txs: Vec::new(),
|
|
|
- orphan_votes: Vec::new(),
|
|
|
- participants: BTreeMap::new(),
|
|
|
- pending_participants: Vec::new(),
|
|
|
- }
|
|
|
+ public,
|
|
|
+ db,
|
|
|
+ consensus,
|
|
|
+ blockchain,
|
|
|
+ unconfirmed_txs,
|
|
|
+ })))
|
|
|
}
|
|
|
|
|
|
/// Node retreives a transaction and append it to the unconfirmed transactions list.
|
|
|
@@ -83,7 +129,7 @@ impl State {
|
|
|
/// Node calculates seconds until next epoch starting time.
|
|
|
/// Epochs duration is configured using the delta value.
|
|
|
pub fn next_epoch_start(&self) -> Duration {
|
|
|
- let start_time = NaiveDateTime::from_timestamp(self.genesis.0, 0);
|
|
|
+ let start_time = NaiveDateTime::from_timestamp(self.consensus.genesis.0, 0);
|
|
|
let current_epoch = self.current_epoch() + 1;
|
|
|
let next_epoch_start_timestamp =
|
|
|
(current_epoch * (2 * DELTA)) + (start_time.timestamp() as u64);
|
|
|
@@ -97,7 +143,7 @@ impl State {
|
|
|
/// Node calculates current epoch, based on elapsed time from the genesis block.
|
|
|
/// Epochs duration is configured using the delta value.
|
|
|
pub fn current_epoch(&self) -> u64 {
|
|
|
- self.genesis.clone().elapsed() / (2 * DELTA)
|
|
|
+ self.consensus.genesis.clone().elapsed() / (2 * DELTA)
|
|
|
}
|
|
|
|
|
|
/// Node finds epochs leader, using a simple hash method.
|
|
|
@@ -107,8 +153,8 @@ impl State {
|
|
|
let mut hasher = DefaultHasher::new();
|
|
|
epoch.hash(&mut hasher);
|
|
|
self.zero_participants_check();
|
|
|
- let pos = hasher.finish() % (self.participants.len() as u64);
|
|
|
- self.participants.iter().nth(pos as usize).unwrap().1.id
|
|
|
+ let pos = hasher.finish() % (self.consensus.participants.len() as u64);
|
|
|
+ self.consensus.participants.iter().nth(pos as usize).unwrap().1.id
|
|
|
}
|
|
|
|
|
|
/// Node checks if they are the current epoch leader.
|
|
|
@@ -124,9 +170,8 @@ impl State {
|
|
|
let epoch = self.current_epoch();
|
|
|
let previous_hash = self.longest_notarized_chain_last_hash().unwrap();
|
|
|
let unproposed_txs = self.unproposed_txs();
|
|
|
- let mut encoded_block = vec![];
|
|
|
- encode_payload!(&mut encoded_block, previous_hash, epoch, unproposed_txs);
|
|
|
- let signed_block = self.secret.sign(&encoded_block[..]);
|
|
|
+ let signed_block =
|
|
|
+ self.secret.sign(&to_block_serial(previous_hash, epoch, &unproposed_txs)[..]);
|
|
|
Ok(Some(BlockProposal::new(
|
|
|
self.public,
|
|
|
signed_block,
|
|
|
@@ -138,14 +183,14 @@ impl State {
|
|
|
String::from("proof"),
|
|
|
String::from("r"),
|
|
|
String::from("s"),
|
|
|
- self.participants.values().cloned().collect(),
|
|
|
+ self.consensus.participants.values().cloned().collect(),
|
|
|
)))
|
|
|
}
|
|
|
|
|
|
/// Node retrieves all unconfiremd transactions not proposed in previous blocks.
|
|
|
pub fn unproposed_txs(&self) -> Vec<Tx> {
|
|
|
let mut unproposed_txs = self.unconfirmed_txs.clone();
|
|
|
- for chain in &self.proposals {
|
|
|
+ for chain in &self.consensus.proposals {
|
|
|
for proposal in &chain.proposals {
|
|
|
for tx in &proposal.txs {
|
|
|
if let Some(pos) = unproposed_txs.iter().position(|txs| *txs == *tx) {
|
|
|
@@ -159,12 +204,11 @@ impl State {
|
|
|
|
|
|
/// Finds the longest fully notarized blockchain the node holds and returns the last block hash.
|
|
|
pub fn longest_notarized_chain_last_hash(&self) -> Result<blake3::Hash> {
|
|
|
- let mut buf = vec![];
|
|
|
- if !self.proposals.is_empty() {
|
|
|
- let mut longest_notarized_chain = &self.proposals[0];
|
|
|
+ let hash = if !self.consensus.proposals.is_empty() {
|
|
|
+ let mut longest_notarized_chain = &self.consensus.proposals[0];
|
|
|
let mut length = longest_notarized_chain.proposals.len();
|
|
|
- if self.proposals.len() > 1 {
|
|
|
- for chain in &self.proposals[1..] {
|
|
|
+ if self.consensus.proposals.len() > 1 {
|
|
|
+ for chain in &self.consensus.proposals[1..] {
|
|
|
if chain.notarized() && chain.proposals.len() > length {
|
|
|
length = chain.proposals.len();
|
|
|
longest_notarized_chain = chain;
|
|
|
@@ -172,12 +216,11 @@ impl State {
|
|
|
}
|
|
|
}
|
|
|
let last = longest_notarized_chain.proposals.last().unwrap();
|
|
|
- encode_payload!(&mut buf, last.st, last.sl, last.txs);
|
|
|
+ blake3::hash(&to_block_serial(last.st, last.sl, &last.txs))
|
|
|
} else {
|
|
|
- let last = self.blockchain.blocks.last().unwrap();
|
|
|
- encode_payload!(&mut buf, last.st, last.sl, last.txs);
|
|
|
+ self.consensus.last_block
|
|
|
};
|
|
|
- Ok(blake3::hash(&serialize(&buf)))
|
|
|
+ Ok(hash)
|
|
|
}
|
|
|
|
|
|
/// Node receives the proposed block, verifies its sender(epoch leader),
|
|
|
@@ -191,9 +234,10 @@ impl State {
|
|
|
);
|
|
|
return Ok(None)
|
|
|
}
|
|
|
- let mut encoded_block = vec![];
|
|
|
- encode_payload!(&mut encoded_block, proposal.st, proposal.sl, proposal.txs);
|
|
|
- if !proposal.public_key.verify(&encoded_block[..], &proposal.signature) {
|
|
|
+ if !proposal.public_key.verify(
|
|
|
+ &to_block_serial(proposal.st, proposal.sl, &proposal.txs)[..],
|
|
|
+ &proposal.signature,
|
|
|
+ ) {
|
|
|
debug!("Proposer signature couldn't be verified. Proposer: {:?}", proposal.id);
|
|
|
return Ok(None)
|
|
|
}
|
|
|
@@ -208,20 +252,18 @@ impl State {
|
|
|
let mut proposal = proposal.clone();
|
|
|
|
|
|
// Generate proposal hash
|
|
|
- let mut buf = vec![];
|
|
|
- encode_payload!(&mut buf, proposal.st, proposal.sl, proposal.txs);
|
|
|
- let proposal_hash = blake3::hash(&serialize(&buf));
|
|
|
+ let proposal_hash = blake3::hash(&to_block_serial(proposal.st, proposal.sl, &proposal.txs));
|
|
|
|
|
|
// Add orphan votes
|
|
|
let mut orphans = Vec::new();
|
|
|
- for vote in self.orphan_votes.iter() {
|
|
|
+ for vote in self.consensus.orphan_votes.iter() {
|
|
|
if vote.proposal == proposal_hash {
|
|
|
proposal.metadata.sm.votes.push(vote.clone());
|
|
|
orphans.push(vote.clone());
|
|
|
}
|
|
|
}
|
|
|
for vote in orphans {
|
|
|
- self.orphan_votes.retain(|v| *v != vote);
|
|
|
+ self.consensus.orphan_votes.retain(|v| *v != vote);
|
|
|
}
|
|
|
|
|
|
let index = self.find_extended_chain_index(&proposal).unwrap();
|
|
|
@@ -232,19 +274,17 @@ impl State {
|
|
|
let chain = match index {
|
|
|
-1 => {
|
|
|
let proposalschain = ProposalsChain::new(proposal.clone());
|
|
|
- self.proposals.push(proposalschain);
|
|
|
- self.proposals.last().unwrap()
|
|
|
+ self.consensus.proposals.push(proposalschain);
|
|
|
+ self.consensus.proposals.last().unwrap()
|
|
|
}
|
|
|
_ => {
|
|
|
- self.proposals[index as usize].add(&proposal);
|
|
|
- &self.proposals[index as usize]
|
|
|
+ self.consensus.proposals[index as usize].add(&proposal);
|
|
|
+ &self.consensus.proposals[index as usize]
|
|
|
}
|
|
|
};
|
|
|
|
|
|
if self.extends_notarized_chain(chain) {
|
|
|
- let mut encoded_hash = vec![];
|
|
|
- encode_payload!(&mut encoded_hash, proposal_hash);
|
|
|
- let signed_hash = self.secret.sign(&encoded_hash[..]);
|
|
|
+ let signed_hash = self.secret.sign(&serialize(&proposal_hash)[..]);
|
|
|
return Ok(Some(Vote::new(
|
|
|
self.public,
|
|
|
signed_hash,
|
|
|
@@ -268,11 +308,9 @@ impl State {
|
|
|
|
|
|
/// Given a proposal, node finds the index of the chain it extends.
|
|
|
pub fn find_extended_chain_index(&self, proposal: &BlockProposal) -> Result<i64> {
|
|
|
- for (index, chain) in self.proposals.iter().enumerate() {
|
|
|
+ for (index, chain) in self.consensus.proposals.iter().enumerate() {
|
|
|
let last = chain.proposals.last().unwrap();
|
|
|
- let mut buf = vec![];
|
|
|
- encode_payload!(&mut buf, last.st, last.sl, last.txs);
|
|
|
- let hash = blake3::hash(&serialize(&buf));
|
|
|
+ let hash = blake3::hash(&to_block_serial(last.st, last.sl, &last.txs));
|
|
|
if proposal.st == hash && proposal.sl > last.sl {
|
|
|
return Ok(index as i64)
|
|
|
}
|
|
|
@@ -282,14 +320,11 @@ impl State {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- let last = self.blockchain.blocks.last().unwrap();
|
|
|
- let mut buf = vec![];
|
|
|
- encode_payload!(&mut buf, last.st, last.sl, last.txs);
|
|
|
- let hash = blake3::hash(&serialize(&buf));
|
|
|
- if proposal.st != hash || proposal.sl <= last.sl {
|
|
|
+ if proposal.st != self.consensus.last_block || proposal.sl <= self.consensus.last_sl {
|
|
|
debug!("Proposal doesn't extend any known chains.");
|
|
|
return Ok(-2)
|
|
|
}
|
|
|
+
|
|
|
Ok(-1)
|
|
|
}
|
|
|
|
|
|
@@ -302,32 +337,32 @@ impl State {
|
|
|
/// nodes unconfirmed transactions list.
|
|
|
/// Finally, we check if the notarization of the proposal can finalize parent proposals
|
|
|
/// in its chain.
|
|
|
- pub fn receive_vote(&mut self, vote: &Vote) -> bool {
|
|
|
+ pub fn receive_vote(&mut self, vote: &Vote) -> Result<bool> {
|
|
|
let mut encoded_proposal = vec![];
|
|
|
let result = vote.proposal.encode(&mut encoded_proposal);
|
|
|
match result {
|
|
|
Ok(_) => (),
|
|
|
Err(e) => {
|
|
|
error!("Proposal encoding failed. Error: {:?}", e);
|
|
|
- return false
|
|
|
+ return Ok(false)
|
|
|
}
|
|
|
};
|
|
|
|
|
|
if !vote.public_key.verify(&encoded_proposal[..], &vote.vote) {
|
|
|
debug!("Voter signature couldn't be verified. Voter: {:?}", vote.id);
|
|
|
- return false
|
|
|
+ return Ok(false)
|
|
|
}
|
|
|
|
|
|
- let nodes_count = self.participants.len();
|
|
|
+ let nodes_count = self.consensus.participants.len();
|
|
|
self.zero_participants_check();
|
|
|
|
|
|
let proposal = self.find_proposal(&vote.proposal).unwrap();
|
|
|
if proposal == None {
|
|
|
debug!("Received vote for unknown proposal.");
|
|
|
- if !self.orphan_votes.contains(vote) {
|
|
|
- self.orphan_votes.push(vote.clone());
|
|
|
+ if !self.consensus.orphan_votes.contains(vote) {
|
|
|
+ self.consensus.orphan_votes.push(vote.clone());
|
|
|
}
|
|
|
- return false
|
|
|
+ return Ok(false)
|
|
|
}
|
|
|
|
|
|
let (unwrapped, chain_index) = proposal.unwrap();
|
|
|
@@ -338,21 +373,21 @@ impl State {
|
|
|
unwrapped.metadata.sm.votes.len() > (2 * nodes_count / 3)
|
|
|
{
|
|
|
unwrapped.metadata.sm.notarized = true;
|
|
|
- self.chain_finalization(chain_index);
|
|
|
+ self.chain_finalization(chain_index)?;
|
|
|
}
|
|
|
|
|
|
// updating participant vote
|
|
|
- let exists = self.participants.get(&vote.id);
|
|
|
+ let exists = self.consensus.participants.get(&vote.id);
|
|
|
let mut participant = match exists {
|
|
|
Some(p) => p.clone(),
|
|
|
None => Participant::new(vote.id, vote.sl),
|
|
|
};
|
|
|
participant.voted = Some(vote.sl);
|
|
|
- self.participants.insert(participant.id, participant);
|
|
|
+ self.consensus.participants.insert(participant.id, participant);
|
|
|
|
|
|
- return true
|
|
|
+ return Ok(true)
|
|
|
}
|
|
|
- false
|
|
|
+ Ok(false)
|
|
|
}
|
|
|
|
|
|
/// Node searches it the chains it holds for provided proposal.
|
|
|
@@ -360,11 +395,10 @@ impl State {
|
|
|
&mut self,
|
|
|
vote_proposal: &blake3::Hash,
|
|
|
) -> Result<Option<(&mut BlockProposal, i64)>> {
|
|
|
- for (index, chain) in &mut self.proposals.iter_mut().enumerate() {
|
|
|
+ for (index, chain) in &mut self.consensus.proposals.iter_mut().enumerate() {
|
|
|
for proposal in chain.proposals.iter_mut().rev() {
|
|
|
- let mut buf = vec![];
|
|
|
- encode_payload!(&mut buf, proposal.st, proposal.sl, proposal.txs);
|
|
|
- let proposal_hash = blake3::hash(&serialize(&buf));
|
|
|
+ let proposal_hash =
|
|
|
+ blake3::hash(&to_block_serial(proposal.st, proposal.sl, &proposal.txs));
|
|
|
if vote_proposal == &proposal_hash {
|
|
|
return Ok(Some((proposal, index as i64)))
|
|
|
}
|
|
|
@@ -377,8 +411,8 @@ impl State {
|
|
|
/// Consensus finalization logic: If node has observed the notarization of 3 consecutive
|
|
|
/// proposals in a fork chain, it finalizes (appends to canonical blockchain) all proposals up to the middle block.
|
|
|
/// When fork chain proposals are finalized, rest fork chains not starting by those proposals are removed.
|
|
|
- pub fn chain_finalization(&mut self, chain_index: i64) {
|
|
|
- let chain = &mut self.proposals[chain_index as usize];
|
|
|
+ pub fn chain_finalization(&mut self, chain_index: i64) -> Result<()> {
|
|
|
+ let chain = &mut self.consensus.proposals[chain_index as usize];
|
|
|
let len = chain.proposals.len();
|
|
|
if len > 2 {
|
|
|
let mut consecutive = 0;
|
|
|
@@ -403,53 +437,55 @@ impl State {
|
|
|
}
|
|
|
chain.proposals.drain(0..(consecutive - 1));
|
|
|
for proposal in &finalized {
|
|
|
- self.blockchain.blocks.push(Block::from_proposal(proposal.clone()));
|
|
|
+ let hash = self.blockchain.add(proposal.clone())?;
|
|
|
+ self.consensus.last_block = hash;
|
|
|
+ self.consensus.last_sl = proposal.sl;
|
|
|
}
|
|
|
|
|
|
- let last = self.blockchain.blocks.last().unwrap();
|
|
|
- let hash = blake3::hash(&serialize(last));
|
|
|
let mut dropped = Vec::new();
|
|
|
- for chain in self.proposals.iter() {
|
|
|
+ for chain in self.consensus.proposals.iter() {
|
|
|
let first = chain.proposals.first().unwrap();
|
|
|
- if first.st != hash || first.sl <= last.sl {
|
|
|
+ if first.st != self.consensus.last_block || first.sl <= self.consensus.last_sl {
|
|
|
dropped.push(chain.clone());
|
|
|
}
|
|
|
}
|
|
|
for chain in dropped {
|
|
|
- self.proposals.retain(|c| *c != chain);
|
|
|
+ self.consensus.proposals.retain(|c| *c != chain);
|
|
|
}
|
|
|
|
|
|
// Remove orphan votes
|
|
|
let mut orphans = Vec::new();
|
|
|
- for vote in self.orphan_votes.iter() {
|
|
|
- if vote.sl <= last.sl {
|
|
|
+ for vote in self.consensus.orphan_votes.iter() {
|
|
|
+ if vote.sl <= self.consensus.last_sl {
|
|
|
orphans.push(vote.clone());
|
|
|
}
|
|
|
}
|
|
|
for vote in orphans {
|
|
|
- self.orphan_votes.retain(|v| *v != vote);
|
|
|
+ self.consensus.orphan_votes.retain(|v| *v != vote);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
/// Node retreives a new participant and appends it to the pending participants list.
|
|
|
pub fn append_participant(&mut self, participant: Participant) -> bool {
|
|
|
- if self.pending_participants.contains(&participant) {
|
|
|
+ if self.consensus.pending_participants.contains(&participant) {
|
|
|
return false
|
|
|
}
|
|
|
- self.pending_participants.push(participant);
|
|
|
+ self.consensus.pending_participants.push(participant);
|
|
|
true
|
|
|
}
|
|
|
|
|
|
/// This prevent the extreme case scenario where network is initialized, but some nodes
|
|
|
/// have not pushed the initial participants in the map.
|
|
|
pub fn zero_participants_check(&mut self) {
|
|
|
- if self.participants.len() == 0 {
|
|
|
- for participant in &self.pending_participants {
|
|
|
- self.participants.insert(participant.id, participant.clone());
|
|
|
+ if self.consensus.participants.len() == 0 {
|
|
|
+ for participant in &self.consensus.pending_participants {
|
|
|
+ self.consensus.participants.insert(participant.id, participant.clone());
|
|
|
}
|
|
|
- self.pending_participants = Vec::new();
|
|
|
+ self.consensus.pending_participants = Vec::new();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -457,14 +493,14 @@ impl State {
|
|
|
/// Active nodes are considered those who joined or voted on previous epoch.
|
|
|
pub fn refresh_participants(&mut self) {
|
|
|
// adding pending participants
|
|
|
- for participant in &self.pending_participants {
|
|
|
- self.participants.insert(participant.id, participant.clone());
|
|
|
+ for participant in &self.consensus.pending_participants {
|
|
|
+ self.consensus.participants.insert(participant.id, participant.clone());
|
|
|
}
|
|
|
- self.pending_participants = Vec::new();
|
|
|
+ self.consensus.pending_participants = Vec::new();
|
|
|
|
|
|
let mut inactive = Vec::new();
|
|
|
let previous_epoch = self.current_epoch() - 1;
|
|
|
- for (index, participant) in self.participants.clone().iter() {
|
|
|
+ for (index, participant) in self.consensus.participants.clone().iter() {
|
|
|
match participant.voted {
|
|
|
Some(epoch) => {
|
|
|
if epoch < previous_epoch {
|
|
|
@@ -479,57 +515,17 @@ impl State {
|
|
|
}
|
|
|
}
|
|
|
for index in inactive {
|
|
|
- self.participants.remove(&index);
|
|
|
+ self.consensus.participants.remove(&index);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// Util function to save the current node state to provided file path.
|
|
|
- pub fn save(&self, db: &sled::Db) -> Result<()> {
|
|
|
- let tree = db.open_tree(SLED_STATE_TREE).unwrap();
|
|
|
- let serialized = serialize(self);
|
|
|
+ /// Util function to save the current consensus state to provided file path.
|
|
|
+ pub fn save_consensus_state(&self) -> Result<()> {
|
|
|
+ let tree = self.db.open_tree(SLED_CONSESUS_STATE_TREE).unwrap();
|
|
|
+ let serialized = serialize(&self.consensus);
|
|
|
match tree.insert(self.id.to_ne_bytes(), serialized) {
|
|
|
Err(_) => Err(Error::OperationFailed),
|
|
|
_ => Ok(()),
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- /// Util function to load current node state by the provided file path.
|
|
|
- // If file is not found, node state is reset.
|
|
|
- pub fn load_or_create(genesis: i64, id: u64, db: &sled::Db) -> Result<State> {
|
|
|
- let tree = db.open_tree(SLED_STATE_TREE).unwrap();
|
|
|
- if let Some(found) = tree.get(id.to_ne_bytes()).unwrap() {
|
|
|
- Ok(deserialize(&found).unwrap())
|
|
|
- } else {
|
|
|
- Self::reset(genesis, id, db)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Util function to load the current node state by the provided folder path.
|
|
|
- pub fn load_current_state(genesis: i64, id: u64, db: &sled::Db) -> Result<StatePtr> {
|
|
|
- let state = Self::load_or_create(genesis, id, db)?;
|
|
|
- Ok(Arc::new(RwLock::new(state)))
|
|
|
- }
|
|
|
-
|
|
|
- /// Util function to reset node state.
|
|
|
- pub fn reset(genesis: i64, id: u64, db: &sled::Db) -> Result<State> {
|
|
|
- // Genesis block is generated.
|
|
|
- let mut genesis_block = Block::new(
|
|
|
- blake3::Hash::from(GENESIS_HASH_BYTES),
|
|
|
- 0,
|
|
|
- vec![],
|
|
|
- get_current_time(),
|
|
|
- String::from("proof"),
|
|
|
- String::from("r"),
|
|
|
- String::from("s"),
|
|
|
- vec![],
|
|
|
- );
|
|
|
- genesis_block.metadata.sm.notarized = true;
|
|
|
- genesis_block.metadata.sm.finalized = true;
|
|
|
-
|
|
|
- let genesis_time = Timestamp(genesis);
|
|
|
-
|
|
|
- let state = Self::new(id, genesis_time, genesis_block);
|
|
|
- state.save(db)?;
|
|
|
- Ok(state)
|
|
|
- }
|
|
|
}
|