use crate::error::{Error, Result};
use crate::pbft::Round;
use crate::types::{Block, NodeId, Role};
use crate::vrf::{sortition, VrfClient, VrfParams};
pub struct Node {
id: NodeId,
weight: u64,
secret_key: Vec<u8>,
public_key: Vec<u8>,
seed: u64,
role: Role,
round: Option<Round>,
ledger: Vec<Block>,
byzantine: bool,
}
impl Node {
pub fn new(id: NodeId, weight: u64) -> Result<Self> {
let mut vrf = VrfClient::new()?;
let (sk, pk) = vrf.generate_keys(id)?;
Ok(Self {
id,
weight,
secret_key: sk,
public_key: pk,
seed: id.wrapping_mul(31).wrapping_add(7),
role: Role::Normal,
round: None,
ledger: vec![Block::genesis()],
byzantine: false,
})
}
pub fn id(&self) -> NodeId {
self.id
}
pub fn weight(&self) -> u64 {
self.weight
}
pub fn role(&self) -> Role {
self.role
}
pub fn seed(&self) -> u64 {
self.seed
}
pub fn ledger(&self) -> &[Block] {
&self.ledger
}
pub fn public_key(&self) -> &[u8] {
&self.public_key
}
pub fn is_byzantine(&self) -> bool {
self.byzantine
}
pub fn set_byzantine(&mut self, byzantine: bool) {
self.byzantine = byzantine;
}
pub fn assign_role(
&mut self,
round_num: u64,
total_weight: u64,
role_expected: &[(Role, u64)],
) -> Result<Role> {
let mut vrf = VrfClient::new()?;
for &(role, expected) in role_expected {
let params = VrfParams {
weight: self.weight,
round: round_num,
seed: self.seed,
role,
};
let vrf_proof = vrf.prove(&self.secret_key, ¶ms)?;
let index = sortition(&vrf_proof.hash, self.weight, expected, total_weight);
if index > 0 {
self.role = role;
return Ok(role);
}
}
self.role = Role::Normal;
Ok(Role::Normal)
}
pub fn propose_block(&self, round_num: u64) -> Result<Block> {
if self.role != Role::Proposer {
return Err(Error::WrongRole(Role::Proposer));
}
let prev_hash = self
.ledger
.last()
.map(|b| b.hash())
.unwrap_or([0u8; 32]);
Ok(Block::new(round_num, prev_hash, self.id, self.seed))
}
pub fn validate_block(&self, block: &Block) -> bool {
if self.byzantine {
return false;
}
match self.ledger.last() {
Some(last) => block.prev_hash == last.hash(),
None => true,
}
}
pub fn commit_block(&mut self, block: Block) {
self.seed = block.seed.wrapping_add(block.round);
self.ledger.push(block);
}
pub fn start_round(&mut self, round_num: u64, threshold: u64) {
self.round = Some(Round::new(round_num, self.role, threshold));
}
}