Skip to main content

vrf_pbft/
node.rs

1use crate::error::{Error, Result};
2use crate::pbft::Round;
3use crate::types::{Block, NodeId, Role};
4use crate::vrf::{sortition, VrfClient, VrfParams};
5
6pub struct Node {
7    id: NodeId,
8    weight: u64,
9    secret_key: Vec<u8>,
10    public_key: Vec<u8>,
11    seed: u64,
12    role: Role,
13    round: Option<Round>,
14    ledger: Vec<Block>,
15    byzantine: bool,
16}
17
18impl Node {
19    pub fn new(id: NodeId, weight: u64) -> Result<Self> {
20        let mut vrf = VrfClient::new()?;
21        let (sk, pk) = vrf.generate_keys(id)?;
22        Ok(Self {
23            id,
24            weight,
25            secret_key: sk,
26            public_key: pk,
27            seed: id.wrapping_mul(31).wrapping_add(7),
28            role: Role::Normal,
29            round: None,
30            ledger: vec![Block::genesis()],
31            byzantine: false,
32        })
33    }
34
35    pub fn id(&self) -> NodeId {
36        self.id
37    }
38
39    pub fn weight(&self) -> u64 {
40        self.weight
41    }
42
43    pub fn role(&self) -> Role {
44        self.role
45    }
46
47    pub fn seed(&self) -> u64 {
48        self.seed
49    }
50
51    pub fn ledger(&self) -> &[Block] {
52        &self.ledger
53    }
54
55    pub fn public_key(&self) -> &[u8] {
56        &self.public_key
57    }
58
59    pub fn is_byzantine(&self) -> bool {
60        self.byzantine
61    }
62
63    pub fn set_byzantine(&mut self, byzantine: bool) {
64        self.byzantine = byzantine;
65    }
66
67    /// Use VRF sortition to determine this node's role for a round.
68    /// Checks roles in order: Proposer, Validator, Packer.
69    /// First role selected wins; otherwise Normal.
70    pub fn assign_role(
71        &mut self,
72        round_num: u64,
73        total_weight: u64,
74        role_expected: &[(Role, u64)],
75    ) -> Result<Role> {
76        let mut vrf = VrfClient::new()?;
77
78        for &(role, expected) in role_expected {
79            let params = VrfParams {
80                weight: self.weight,
81                round: round_num,
82                seed: self.seed,
83                role,
84            };
85            let vrf_proof = vrf.prove(&self.secret_key, &params)?;
86            let index = sortition(&vrf_proof.hash, self.weight, expected, total_weight);
87            if index > 0 {
88                self.role = role;
89                return Ok(role);
90            }
91        }
92
93        self.role = Role::Normal;
94        Ok(Role::Normal)
95    }
96
97    /// Propose a new block for this round. Only valid for Proposers.
98    pub fn propose_block(&self, round_num: u64) -> Result<Block> {
99        if self.role != Role::Proposer {
100            return Err(Error::WrongRole(Role::Proposer));
101        }
102        let prev_hash = self
103            .ledger
104            .last()
105            .map(|b| b.hash())
106            .unwrap_or([0u8; 32]);
107        Ok(Block::new(round_num, prev_hash, self.id, self.seed))
108    }
109
110    /// Validate a proposed block. Byzantine nodes always reject.
111    pub fn validate_block(&self, block: &Block) -> bool {
112        if self.byzantine {
113            return false;
114        }
115        match self.ledger.last() {
116            Some(last) => block.prev_hash == last.hash(),
117            None => true,
118        }
119    }
120
121    /// Commit a finalized block to the local ledger.
122    pub fn commit_block(&mut self, block: Block) {
123        self.seed = block.seed.wrapping_add(block.round);
124        self.ledger.push(block);
125    }
126
127    pub fn start_round(&mut self, round_num: u64, threshold: u64) {
128        self.round = Some(Round::new(round_num, self.role, threshold));
129    }
130}