validator: current slot generation added

This commit is contained in:
aggstam
2023-08-04 18:42:00 +03:00
parent 1410e8b280
commit 67d5ed7d28
4 changed files with 129 additions and 9 deletions

View File

@@ -260,4 +260,12 @@ impl SlotStoreOverlay {
Ok(ret)
}
/// Fetch the last slot from the overlay, based on the `Ord`
/// implementation for `Vec<u8>`.
pub fn get_last(&self) -> Result<Slot> {
let found = self.0.lock().unwrap().last(SLED_SLOT_TREE)?.unwrap();
let slot = deserialize(&found.1)?;
Ok(slot)
}
}

View File

@@ -172,8 +172,7 @@ impl ProtocolSync {
let lock = self.state.read().await;
let current = lock.consensus.time_keeper.current_slot();
let participating = lock.consensus.participating;
if participating.is_some() {
let slot = participating.unwrap();
if let Some(slot) = participating {
if current >= slot {
debug!(
target: "consensus::protocol_sync::handle_receive_block()",
@@ -308,8 +307,7 @@ impl ProtocolSync {
let lock = self.state.read().await;
let current = lock.consensus.time_keeper.current_slot();
let participating = lock.consensus.participating;
if participating.is_some() {
let slot = participating.unwrap();
if let Some(slot) = participating {
if current >= slot {
debug!(
target: "consensus::protocol_sync::handle_receive_slot()",

View File

@@ -260,6 +260,9 @@ pub enum Error {
#[error("Proposal contains missmatched hashes")]
ProposalHashesMissmatchError,
#[error("Proposal contains unknown slots")]
ProposalContainsUnknownSlots,
#[error("Proposal contains missmatched headers")]
ProposalHeadersMissmatchError,

View File

@@ -16,13 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi_sdk::blockchain::{PidOutput, PreviousSlot, Slot};
use darkfi_serial::{SerialDecodable, SerialEncodable};
use log::{error, warn};
use crate::{
blockchain::{BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr},
util::time::TimeKeeper,
validator::verify_block,
validator::{consensus::pid::slot_pid_output, verify_block},
Error, Result,
};
@@ -64,6 +65,45 @@ impl Consensus {
}
}
/// Generate current hot/live slot for all current forks.
pub fn generate_slot(&mut self) -> Result<()> {
// Grab current slot id
let id = self.time_keeper.current_slot();
// If no forks exist, create a new one as a basis to extend
if self.forks.is_empty() {
self.forks.push(Fork::new(&self.blockchain)?);
}
// Grab previous slot information
let (producers, last_hashes, second_to_last_hashes) = self.previous_slot_info(id - 1)?;
for fork in self.forks.iter_mut() {
fork.generate_slot(id, producers, &last_hashes, &second_to_last_hashes)?;
}
Ok(())
}
/// Retrieve previous slot producers, last proposal hashes,
/// and their second to last hashes, from all current forks.
fn previous_slot_info(&self, slot: u64) -> Result<(u64, Vec<blake3::Hash>, Vec<blake3::Hash>)> {
let mut producers = 0;
let mut last_hashes = vec![];
let mut second_to_last_hashes = vec![];
for fork in &self.forks {
let last_proposal = fork.last_proposal()?;
if last_proposal.block.header.slot == slot {
producers += 1;
}
last_hashes.push(last_proposal.hash);
second_to_last_hashes.push(last_proposal.block.header.previous);
}
Ok((producers, last_hashes, second_to_last_hashes))
}
/// Given a proposal, the node verifys it and finds which fork it extends.
/// If the proposal extends the canonical blockchain, a new fork chain is created.
/// A proposal is considered valid when the following rules apply:
@@ -71,7 +111,9 @@ impl Consensus {
/// 2. Proposal refers to current slot
/// 3. Proposal hash matches the actual block one
/// 4. Block transactions don't exceed set limit
/// 5. Block is valid
/// 5. If proposal extends a known fork, verify block slots
/// correspond to the fork hot/live ones
/// 6. Block is valid
/// Additional validity rules can be applied.
pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
// Generate a time keeper for current slot
@@ -115,13 +157,28 @@ impl Consensus {
// Check if proposal extends any existing forks
let (mut fork, index) = self.find_extended_fork(proposal).await?;
// Verify block slots correspond to the forks' hot/live ones (5)
if !fork.slots.is_empty() && fork.slots != proposal.block.slots {
return Err(Error::ProposalContainsUnknownSlots)
}
// Insert last block slot so transactions can be validated against.
// Rest (empty) slots will be inserted along with the block.
// Since this fork uses an overlay clone, original overlay is not affected.
fork.overlay.lock().unwrap().slots.insert(&[proposal
.block
.slots
.last()
.unwrap()
.clone()])?;
// Grab overlay last block
let previous = fork.overlay.lock().unwrap().last_block()?;
// Retrieve expected reward
let expected_reward = next_block_reward();
// Verify proposal block (5)
// Verify proposal block (6)
if verify_block(
&fork.overlay,
&time_keeper,
@@ -141,6 +198,7 @@ impl Consensus {
// If a fork index was found, replace forks with the mutated one,
// otherwise push the new fork.
fork.proposals.push(proposal.hash);
fork.slots = vec![];
match index {
Some(i) => {
self.forks[i] = fork;
@@ -273,6 +331,8 @@ pub struct Fork {
pub overlay: BlockchainOverlayPtr,
/// Fork proposal hashes sequence
pub proposals: Vec<blake3::Hash>,
/// Hot/live slots
pub slots: Vec<Slot>,
/// Valid pending transaction hashes
pub mempool: Vec<blake3::Hash>,
}
@@ -280,7 +340,57 @@ pub struct Fork {
impl Fork {
pub fn new(blockchain: &Blockchain) -> Result<Self> {
let overlay = BlockchainOverlay::new(blockchain)?;
Ok(Self { overlay, proposals: vec![], mempool: vec![] })
Ok(Self { overlay, proposals: vec![], slots: vec![], mempool: vec![] })
}
/// Auxiliary function to retrieve last proposal
pub fn last_proposal(&self) -> Result<Proposal> {
let block = if self.proposals.is_empty() {
self.overlay.lock().unwrap().last_block()?
} else {
self.overlay.lock().unwrap().get_blocks_by_hash(&[*self.proposals.last().unwrap()])?[0]
.clone()
};
Ok(Proposal::new(block))
}
/// Generate current hot/live slot
pub fn generate_slot(
&mut self,
id: u64,
producers: u64,
last_hashes: &[blake3::Hash],
second_to_last_hashes: &[blake3::Hash],
) -> Result<()> {
// Grab last known fork slot
let previous_slot = if self.slots.is_empty() {
self.overlay.lock().unwrap().slots.get_last()?
} else {
self.slots.last().unwrap().clone()
};
// Generate previous slot information
let previous = PreviousSlot::new(
producers,
last_hashes.to_vec(),
second_to_last_hashes.to_vec(),
previous_slot.pid.error,
);
// Generate PID controller output
let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot, producers);
let pid = PidOutput::new(f, error, sigma1, sigma2);
// Each slot starts as an empty slot(not reward) when generated, carrying
// last eta
let last_eta = previous_slot.last_eta;
let total_tokens = previous_slot.total_tokens + previous_slot.reward;
let reward = 0;
let slot = Slot::new(id, previous, pid, last_eta, total_tokens, reward);
self.slots.push(slot);
Ok(())
}
/// Auxiliary function to create a full clone using BlockchainOverlay::full_clone.
@@ -289,9 +399,10 @@ impl Fork {
pub fn full_clone(&self) -> Result<Self> {
let overlay = self.overlay.lock().unwrap().full_clone()?;
let proposals = self.proposals.clone();
let slots = self.slots.clone();
let mempool = self.mempool.clone();
Ok(Self { overlay, proposals, mempool })
Ok(Self { overlay, proposals, slots, mempool })
}
}