pub struct ConsensusEngine { /* private fields */ }Implementations§
Source§impl ConsensusEngine
impl ConsensusEngine
Sourcepub fn new(config: Config) -> Self
pub fn new(config: Config) -> Self
Examples found in repository?
examples/single_round.rs (line 5)
3fn main() {
4 let config = Config::new(1); // f=1, needs 4 nodes
5 let mut engine = ConsensusEngine::new(config);
6
7 for i in 1..=4 {
8 let node = Node::new(i, 1000).expect("failed to create node");
9 engine.add_node(node);
10 }
11
12 println!("Running VRF-PBFT with 4 nodes (f=1)...\n");
13
14 // VRF selection is probabilistic, so retry until a round succeeds
15 loop {
16 match engine.run_round() {
17 Ok(block) => {
18 println!("Consensus reached on round {}!", engine.round());
19 println!(" Proposer: node {}", block.proposer);
20 println!(" Seed: {}", block.seed);
21 println!(" Prev hash: {}...", &hex(&block.prev_hash)[..16]);
22 println!(" Hash: {}...", &hex(&block.hash())[..16]);
23 println!("\nMessages exchanged: {}", engine.messages().len());
24 break;
25 }
26 Err(vrf_pbft::Error::NoProposer(r)) => {
27 println!("Round {}: no proposer elected, retrying...", r);
28 }
29 Err(vrf_pbft::Error::InsufficientVotes { needed, got }) => {
30 println!(
31 "Round {}: not enough votes ({}/{}), retrying...",
32 engine.round(),
33 got,
34 needed
35 );
36 }
37 Err(e) => {
38 eprintln!("Fatal error: {}", e);
39 std::process::exit(1);
40 }
41 }
42 }
43}More examples
examples/byzantine.rs (line 6)
3fn main() {
4 // f=2: can tolerate 2 byzantine nodes, needs 7 total
5 let config = Config::new(2);
6 let mut engine = ConsensusEngine::new(config);
7
8 for i in 1..=7 {
9 let mut node = Node::new(i, 1000).expect("failed to create node");
10 // Mark first 2 nodes as byzantine (they vote against valid blocks)
11 if i <= 2 {
12 node.set_byzantine(true);
13 }
14 engine.add_node(node);
15 }
16
17 println!("Byzantine fault tolerance demo");
18 println!(" Nodes: 7 (5 honest, 2 byzantine)");
19 println!(" f=2: system tolerates up to 2 faults");
20 println!(" Threshold: 2f+1 = {}\n", 2 * 2 + 1);
21
22 let total_rounds = 100;
23 let blocks = engine.run(total_rounds).expect("simulation failed");
24
25 println!("After {} round attempts:", engine.round());
26 println!(" Blocks committed: {}", blocks.len());
27
28 if !blocks.is_empty() {
29 println!("\n Byzantine nodes voted AGAINST every block,");
30 println!(" but consensus still passed because 5 honest");
31 println!(" nodes exceed the threshold of 5 (2f+1).\n");
32
33 // Verify ledger consistency
34 let first_len = engine.nodes()[0].ledger().len();
35 let consistent = engine.nodes().iter().all(|n| n.ledger().len() == first_len);
36 println!(
37 " Ledger consistency: {} ({} blocks per node)",
38 if consistent { "PASS" } else { "FAIL" },
39 first_len
40 );
41 } else {
42 println!("\n No blocks committed (VRF didn't elect enough validators).");
43 println!(" Try running again -- VRF selection is probabilistic.");
44 }
45}examples/simulation.rs (line 6)
4fn main() {
5 let config = Config::new(3); // f=3, needs 10 nodes
6 let mut engine = ConsensusEngine::new(config);
7
8 // 10 nodes with varying weights (simulating different stakes)
9 let weights = [500, 300, 200, 150, 100, 100, 80, 60, 40, 20];
10 for (i, &w) in weights.iter().enumerate() {
11 let node = Node::new((i + 1) as u64, w).expect("failed to create node");
12 engine.add_node(node);
13 }
14
15 let total_rounds = 100;
16 println!(
17 "Simulating {} rounds with {} nodes (f=3)\n",
18 total_rounds,
19 weights.len()
20 );
21 println!(
22 "Node weights: {:?}",
23 weights
24 );
25 println!("Total weight: {}\n", weights.iter().sum::<u64>());
26
27 let blocks = engine.run(total_rounds).expect("simulation failed");
28
29 // Proposer distribution
30 let mut proposer_count: HashMap<u64, u32> = HashMap::new();
31 for block in &blocks {
32 *proposer_count.entry(block.proposer).or_default() += 1;
33 }
34
35 println!("Results:");
36 println!(" Rounds attempted: {}", engine.round());
37 println!(" Blocks committed: {}", blocks.len());
38 println!(
39 " Success rate: {:.1}%",
40 blocks.len() as f64 / engine.round() as f64 * 100.0
41 );
42
43 println!("\nProposer distribution:");
44 let mut sorted: Vec<_> = proposer_count.iter().collect();
45 sorted.sort_by_key(|(id, _)| *id);
46 for (id, count) in sorted {
47 let w = weights[(*id - 1) as usize];
48 println!(" Node {} (weight {:>3}): {} blocks", id, w, count);
49 }
50
51 // Verify all nodes have the same ledger length
52 let ledger_lens: Vec<usize> = engine.nodes().iter().map(|n| n.ledger().len()).collect();
53 let all_same = ledger_lens.windows(2).all(|w| w[0] == w[1]);
54 println!(
55 "\nLedger consistency: {} (all nodes have {} blocks)",
56 if all_same { "PASS" } else { "FAIL" },
57 ledger_lens[0]
58 );
59}Sourcepub fn add_node(&mut self, node: Node)
pub fn add_node(&mut self, node: Node)
Examples found in repository?
examples/single_round.rs (line 9)
3fn main() {
4 let config = Config::new(1); // f=1, needs 4 nodes
5 let mut engine = ConsensusEngine::new(config);
6
7 for i in 1..=4 {
8 let node = Node::new(i, 1000).expect("failed to create node");
9 engine.add_node(node);
10 }
11
12 println!("Running VRF-PBFT with 4 nodes (f=1)...\n");
13
14 // VRF selection is probabilistic, so retry until a round succeeds
15 loop {
16 match engine.run_round() {
17 Ok(block) => {
18 println!("Consensus reached on round {}!", engine.round());
19 println!(" Proposer: node {}", block.proposer);
20 println!(" Seed: {}", block.seed);
21 println!(" Prev hash: {}...", &hex(&block.prev_hash)[..16]);
22 println!(" Hash: {}...", &hex(&block.hash())[..16]);
23 println!("\nMessages exchanged: {}", engine.messages().len());
24 break;
25 }
26 Err(vrf_pbft::Error::NoProposer(r)) => {
27 println!("Round {}: no proposer elected, retrying...", r);
28 }
29 Err(vrf_pbft::Error::InsufficientVotes { needed, got }) => {
30 println!(
31 "Round {}: not enough votes ({}/{}), retrying...",
32 engine.round(),
33 got,
34 needed
35 );
36 }
37 Err(e) => {
38 eprintln!("Fatal error: {}", e);
39 std::process::exit(1);
40 }
41 }
42 }
43}More examples
examples/byzantine.rs (line 14)
3fn main() {
4 // f=2: can tolerate 2 byzantine nodes, needs 7 total
5 let config = Config::new(2);
6 let mut engine = ConsensusEngine::new(config);
7
8 for i in 1..=7 {
9 let mut node = Node::new(i, 1000).expect("failed to create node");
10 // Mark first 2 nodes as byzantine (they vote against valid blocks)
11 if i <= 2 {
12 node.set_byzantine(true);
13 }
14 engine.add_node(node);
15 }
16
17 println!("Byzantine fault tolerance demo");
18 println!(" Nodes: 7 (5 honest, 2 byzantine)");
19 println!(" f=2: system tolerates up to 2 faults");
20 println!(" Threshold: 2f+1 = {}\n", 2 * 2 + 1);
21
22 let total_rounds = 100;
23 let blocks = engine.run(total_rounds).expect("simulation failed");
24
25 println!("After {} round attempts:", engine.round());
26 println!(" Blocks committed: {}", blocks.len());
27
28 if !blocks.is_empty() {
29 println!("\n Byzantine nodes voted AGAINST every block,");
30 println!(" but consensus still passed because 5 honest");
31 println!(" nodes exceed the threshold of 5 (2f+1).\n");
32
33 // Verify ledger consistency
34 let first_len = engine.nodes()[0].ledger().len();
35 let consistent = engine.nodes().iter().all(|n| n.ledger().len() == first_len);
36 println!(
37 " Ledger consistency: {} ({} blocks per node)",
38 if consistent { "PASS" } else { "FAIL" },
39 first_len
40 );
41 } else {
42 println!("\n No blocks committed (VRF didn't elect enough validators).");
43 println!(" Try running again -- VRF selection is probabilistic.");
44 }
45}examples/simulation.rs (line 12)
4fn main() {
5 let config = Config::new(3); // f=3, needs 10 nodes
6 let mut engine = ConsensusEngine::new(config);
7
8 // 10 nodes with varying weights (simulating different stakes)
9 let weights = [500, 300, 200, 150, 100, 100, 80, 60, 40, 20];
10 for (i, &w) in weights.iter().enumerate() {
11 let node = Node::new((i + 1) as u64, w).expect("failed to create node");
12 engine.add_node(node);
13 }
14
15 let total_rounds = 100;
16 println!(
17 "Simulating {} rounds with {} nodes (f=3)\n",
18 total_rounds,
19 weights.len()
20 );
21 println!(
22 "Node weights: {:?}",
23 weights
24 );
25 println!("Total weight: {}\n", weights.iter().sum::<u64>());
26
27 let blocks = engine.run(total_rounds).expect("simulation failed");
28
29 // Proposer distribution
30 let mut proposer_count: HashMap<u64, u32> = HashMap::new();
31 for block in &blocks {
32 *proposer_count.entry(block.proposer).or_default() += 1;
33 }
34
35 println!("Results:");
36 println!(" Rounds attempted: {}", engine.round());
37 println!(" Blocks committed: {}", blocks.len());
38 println!(
39 " Success rate: {:.1}%",
40 blocks.len() as f64 / engine.round() as f64 * 100.0
41 );
42
43 println!("\nProposer distribution:");
44 let mut sorted: Vec<_> = proposer_count.iter().collect();
45 sorted.sort_by_key(|(id, _)| *id);
46 for (id, count) in sorted {
47 let w = weights[(*id - 1) as usize];
48 println!(" Node {} (weight {:>3}): {} blocks", id, w, count);
49 }
50
51 // Verify all nodes have the same ledger length
52 let ledger_lens: Vec<usize> = engine.nodes().iter().map(|n| n.ledger().len()).collect();
53 let all_same = ledger_lens.windows(2).all(|w| w[0] == w[1]);
54 println!(
55 "\nLedger consistency: {} (all nodes have {} blocks)",
56 if all_same { "PASS" } else { "FAIL" },
57 ledger_lens[0]
58 );
59}pub fn committed_blocks(&self) -> &[Block]
Sourcepub fn round(&self) -> u64
pub fn round(&self) -> u64
Examples found in repository?
examples/single_round.rs (line 18)
3fn main() {
4 let config = Config::new(1); // f=1, needs 4 nodes
5 let mut engine = ConsensusEngine::new(config);
6
7 for i in 1..=4 {
8 let node = Node::new(i, 1000).expect("failed to create node");
9 engine.add_node(node);
10 }
11
12 println!("Running VRF-PBFT with 4 nodes (f=1)...\n");
13
14 // VRF selection is probabilistic, so retry until a round succeeds
15 loop {
16 match engine.run_round() {
17 Ok(block) => {
18 println!("Consensus reached on round {}!", engine.round());
19 println!(" Proposer: node {}", block.proposer);
20 println!(" Seed: {}", block.seed);
21 println!(" Prev hash: {}...", &hex(&block.prev_hash)[..16]);
22 println!(" Hash: {}...", &hex(&block.hash())[..16]);
23 println!("\nMessages exchanged: {}", engine.messages().len());
24 break;
25 }
26 Err(vrf_pbft::Error::NoProposer(r)) => {
27 println!("Round {}: no proposer elected, retrying...", r);
28 }
29 Err(vrf_pbft::Error::InsufficientVotes { needed, got }) => {
30 println!(
31 "Round {}: not enough votes ({}/{}), retrying...",
32 engine.round(),
33 got,
34 needed
35 );
36 }
37 Err(e) => {
38 eprintln!("Fatal error: {}", e);
39 std::process::exit(1);
40 }
41 }
42 }
43}More examples
examples/byzantine.rs (line 25)
3fn main() {
4 // f=2: can tolerate 2 byzantine nodes, needs 7 total
5 let config = Config::new(2);
6 let mut engine = ConsensusEngine::new(config);
7
8 for i in 1..=7 {
9 let mut node = Node::new(i, 1000).expect("failed to create node");
10 // Mark first 2 nodes as byzantine (they vote against valid blocks)
11 if i <= 2 {
12 node.set_byzantine(true);
13 }
14 engine.add_node(node);
15 }
16
17 println!("Byzantine fault tolerance demo");
18 println!(" Nodes: 7 (5 honest, 2 byzantine)");
19 println!(" f=2: system tolerates up to 2 faults");
20 println!(" Threshold: 2f+1 = {}\n", 2 * 2 + 1);
21
22 let total_rounds = 100;
23 let blocks = engine.run(total_rounds).expect("simulation failed");
24
25 println!("After {} round attempts:", engine.round());
26 println!(" Blocks committed: {}", blocks.len());
27
28 if !blocks.is_empty() {
29 println!("\n Byzantine nodes voted AGAINST every block,");
30 println!(" but consensus still passed because 5 honest");
31 println!(" nodes exceed the threshold of 5 (2f+1).\n");
32
33 // Verify ledger consistency
34 let first_len = engine.nodes()[0].ledger().len();
35 let consistent = engine.nodes().iter().all(|n| n.ledger().len() == first_len);
36 println!(
37 " Ledger consistency: {} ({} blocks per node)",
38 if consistent { "PASS" } else { "FAIL" },
39 first_len
40 );
41 } else {
42 println!("\n No blocks committed (VRF didn't elect enough validators).");
43 println!(" Try running again -- VRF selection is probabilistic.");
44 }
45}examples/simulation.rs (line 36)
4fn main() {
5 let config = Config::new(3); // f=3, needs 10 nodes
6 let mut engine = ConsensusEngine::new(config);
7
8 // 10 nodes with varying weights (simulating different stakes)
9 let weights = [500, 300, 200, 150, 100, 100, 80, 60, 40, 20];
10 for (i, &w) in weights.iter().enumerate() {
11 let node = Node::new((i + 1) as u64, w).expect("failed to create node");
12 engine.add_node(node);
13 }
14
15 let total_rounds = 100;
16 println!(
17 "Simulating {} rounds with {} nodes (f=3)\n",
18 total_rounds,
19 weights.len()
20 );
21 println!(
22 "Node weights: {:?}",
23 weights
24 );
25 println!("Total weight: {}\n", weights.iter().sum::<u64>());
26
27 let blocks = engine.run(total_rounds).expect("simulation failed");
28
29 // Proposer distribution
30 let mut proposer_count: HashMap<u64, u32> = HashMap::new();
31 for block in &blocks {
32 *proposer_count.entry(block.proposer).or_default() += 1;
33 }
34
35 println!("Results:");
36 println!(" Rounds attempted: {}", engine.round());
37 println!(" Blocks committed: {}", blocks.len());
38 println!(
39 " Success rate: {:.1}%",
40 blocks.len() as f64 / engine.round() as f64 * 100.0
41 );
42
43 println!("\nProposer distribution:");
44 let mut sorted: Vec<_> = proposer_count.iter().collect();
45 sorted.sort_by_key(|(id, _)| *id);
46 for (id, count) in sorted {
47 let w = weights[(*id - 1) as usize];
48 println!(" Node {} (weight {:>3}): {} blocks", id, w, count);
49 }
50
51 // Verify all nodes have the same ledger length
52 let ledger_lens: Vec<usize> = engine.nodes().iter().map(|n| n.ledger().len()).collect();
53 let all_same = ledger_lens.windows(2).all(|w| w[0] == w[1]);
54 println!(
55 "\nLedger consistency: {} (all nodes have {} blocks)",
56 if all_same { "PASS" } else { "FAIL" },
57 ledger_lens[0]
58 );
59}Sourcepub fn nodes(&self) -> &[Node]
pub fn nodes(&self) -> &[Node]
Examples found in repository?
examples/byzantine.rs (line 34)
3fn main() {
4 // f=2: can tolerate 2 byzantine nodes, needs 7 total
5 let config = Config::new(2);
6 let mut engine = ConsensusEngine::new(config);
7
8 for i in 1..=7 {
9 let mut node = Node::new(i, 1000).expect("failed to create node");
10 // Mark first 2 nodes as byzantine (they vote against valid blocks)
11 if i <= 2 {
12 node.set_byzantine(true);
13 }
14 engine.add_node(node);
15 }
16
17 println!("Byzantine fault tolerance demo");
18 println!(" Nodes: 7 (5 honest, 2 byzantine)");
19 println!(" f=2: system tolerates up to 2 faults");
20 println!(" Threshold: 2f+1 = {}\n", 2 * 2 + 1);
21
22 let total_rounds = 100;
23 let blocks = engine.run(total_rounds).expect("simulation failed");
24
25 println!("After {} round attempts:", engine.round());
26 println!(" Blocks committed: {}", blocks.len());
27
28 if !blocks.is_empty() {
29 println!("\n Byzantine nodes voted AGAINST every block,");
30 println!(" but consensus still passed because 5 honest");
31 println!(" nodes exceed the threshold of 5 (2f+1).\n");
32
33 // Verify ledger consistency
34 let first_len = engine.nodes()[0].ledger().len();
35 let consistent = engine.nodes().iter().all(|n| n.ledger().len() == first_len);
36 println!(
37 " Ledger consistency: {} ({} blocks per node)",
38 if consistent { "PASS" } else { "FAIL" },
39 first_len
40 );
41 } else {
42 println!("\n No blocks committed (VRF didn't elect enough validators).");
43 println!(" Try running again -- VRF selection is probabilistic.");
44 }
45}More examples
examples/simulation.rs (line 52)
4fn main() {
5 let config = Config::new(3); // f=3, needs 10 nodes
6 let mut engine = ConsensusEngine::new(config);
7
8 // 10 nodes with varying weights (simulating different stakes)
9 let weights = [500, 300, 200, 150, 100, 100, 80, 60, 40, 20];
10 for (i, &w) in weights.iter().enumerate() {
11 let node = Node::new((i + 1) as u64, w).expect("failed to create node");
12 engine.add_node(node);
13 }
14
15 let total_rounds = 100;
16 println!(
17 "Simulating {} rounds with {} nodes (f=3)\n",
18 total_rounds,
19 weights.len()
20 );
21 println!(
22 "Node weights: {:?}",
23 weights
24 );
25 println!("Total weight: {}\n", weights.iter().sum::<u64>());
26
27 let blocks = engine.run(total_rounds).expect("simulation failed");
28
29 // Proposer distribution
30 let mut proposer_count: HashMap<u64, u32> = HashMap::new();
31 for block in &blocks {
32 *proposer_count.entry(block.proposer).or_default() += 1;
33 }
34
35 println!("Results:");
36 println!(" Rounds attempted: {}", engine.round());
37 println!(" Blocks committed: {}", blocks.len());
38 println!(
39 " Success rate: {:.1}%",
40 blocks.len() as f64 / engine.round() as f64 * 100.0
41 );
42
43 println!("\nProposer distribution:");
44 let mut sorted: Vec<_> = proposer_count.iter().collect();
45 sorted.sort_by_key(|(id, _)| *id);
46 for (id, count) in sorted {
47 let w = weights[(*id - 1) as usize];
48 println!(" Node {} (weight {:>3}): {} blocks", id, w, count);
49 }
50
51 // Verify all nodes have the same ledger length
52 let ledger_lens: Vec<usize> = engine.nodes().iter().map(|n| n.ledger().len()).collect();
53 let all_same = ledger_lens.windows(2).all(|w| w[0] == w[1]);
54 println!(
55 "\nLedger consistency: {} (all nodes have {} blocks)",
56 if all_same { "PASS" } else { "FAIL" },
57 ledger_lens[0]
58 );
59}Sourcepub fn messages(&self) -> &[Message]
pub fn messages(&self) -> &[Message]
Examples found in repository?
examples/single_round.rs (line 23)
3fn main() {
4 let config = Config::new(1); // f=1, needs 4 nodes
5 let mut engine = ConsensusEngine::new(config);
6
7 for i in 1..=4 {
8 let node = Node::new(i, 1000).expect("failed to create node");
9 engine.add_node(node);
10 }
11
12 println!("Running VRF-PBFT with 4 nodes (f=1)...\n");
13
14 // VRF selection is probabilistic, so retry until a round succeeds
15 loop {
16 match engine.run_round() {
17 Ok(block) => {
18 println!("Consensus reached on round {}!", engine.round());
19 println!(" Proposer: node {}", block.proposer);
20 println!(" Seed: {}", block.seed);
21 println!(" Prev hash: {}...", &hex(&block.prev_hash)[..16]);
22 println!(" Hash: {}...", &hex(&block.hash())[..16]);
23 println!("\nMessages exchanged: {}", engine.messages().len());
24 break;
25 }
26 Err(vrf_pbft::Error::NoProposer(r)) => {
27 println!("Round {}: no proposer elected, retrying...", r);
28 }
29 Err(vrf_pbft::Error::InsufficientVotes { needed, got }) => {
30 println!(
31 "Round {}: not enough votes ({}/{}), retrying...",
32 engine.round(),
33 got,
34 needed
35 );
36 }
37 Err(e) => {
38 eprintln!("Fatal error: {}", e);
39 std::process::exit(1);
40 }
41 }
42 }
43}Sourcepub fn run_round(&mut self) -> Result<Block>
pub fn run_round(&mut self) -> Result<Block>
Run a single consensus round through all PBFT phases.
Examples found in repository?
examples/single_round.rs (line 16)
3fn main() {
4 let config = Config::new(1); // f=1, needs 4 nodes
5 let mut engine = ConsensusEngine::new(config);
6
7 for i in 1..=4 {
8 let node = Node::new(i, 1000).expect("failed to create node");
9 engine.add_node(node);
10 }
11
12 println!("Running VRF-PBFT with 4 nodes (f=1)...\n");
13
14 // VRF selection is probabilistic, so retry until a round succeeds
15 loop {
16 match engine.run_round() {
17 Ok(block) => {
18 println!("Consensus reached on round {}!", engine.round());
19 println!(" Proposer: node {}", block.proposer);
20 println!(" Seed: {}", block.seed);
21 println!(" Prev hash: {}...", &hex(&block.prev_hash)[..16]);
22 println!(" Hash: {}...", &hex(&block.hash())[..16]);
23 println!("\nMessages exchanged: {}", engine.messages().len());
24 break;
25 }
26 Err(vrf_pbft::Error::NoProposer(r)) => {
27 println!("Round {}: no proposer elected, retrying...", r);
28 }
29 Err(vrf_pbft::Error::InsufficientVotes { needed, got }) => {
30 println!(
31 "Round {}: not enough votes ({}/{}), retrying...",
32 engine.round(),
33 got,
34 needed
35 );
36 }
37 Err(e) => {
38 eprintln!("Fatal error: {}", e);
39 std::process::exit(1);
40 }
41 }
42 }
43}Sourcepub fn run(&mut self, rounds: u64) -> Result<Vec<Block>>
pub fn run(&mut self, rounds: u64) -> Result<Vec<Block>>
Run multiple rounds. Skips rounds where VRF doesn’t elect a proposer.
Examples found in repository?
examples/byzantine.rs (line 23)
3fn main() {
4 // f=2: can tolerate 2 byzantine nodes, needs 7 total
5 let config = Config::new(2);
6 let mut engine = ConsensusEngine::new(config);
7
8 for i in 1..=7 {
9 let mut node = Node::new(i, 1000).expect("failed to create node");
10 // Mark first 2 nodes as byzantine (they vote against valid blocks)
11 if i <= 2 {
12 node.set_byzantine(true);
13 }
14 engine.add_node(node);
15 }
16
17 println!("Byzantine fault tolerance demo");
18 println!(" Nodes: 7 (5 honest, 2 byzantine)");
19 println!(" f=2: system tolerates up to 2 faults");
20 println!(" Threshold: 2f+1 = {}\n", 2 * 2 + 1);
21
22 let total_rounds = 100;
23 let blocks = engine.run(total_rounds).expect("simulation failed");
24
25 println!("After {} round attempts:", engine.round());
26 println!(" Blocks committed: {}", blocks.len());
27
28 if !blocks.is_empty() {
29 println!("\n Byzantine nodes voted AGAINST every block,");
30 println!(" but consensus still passed because 5 honest");
31 println!(" nodes exceed the threshold of 5 (2f+1).\n");
32
33 // Verify ledger consistency
34 let first_len = engine.nodes()[0].ledger().len();
35 let consistent = engine.nodes().iter().all(|n| n.ledger().len() == first_len);
36 println!(
37 " Ledger consistency: {} ({} blocks per node)",
38 if consistent { "PASS" } else { "FAIL" },
39 first_len
40 );
41 } else {
42 println!("\n No blocks committed (VRF didn't elect enough validators).");
43 println!(" Try running again -- VRF selection is probabilistic.");
44 }
45}More examples
examples/simulation.rs (line 27)
4fn main() {
5 let config = Config::new(3); // f=3, needs 10 nodes
6 let mut engine = ConsensusEngine::new(config);
7
8 // 10 nodes with varying weights (simulating different stakes)
9 let weights = [500, 300, 200, 150, 100, 100, 80, 60, 40, 20];
10 for (i, &w) in weights.iter().enumerate() {
11 let node = Node::new((i + 1) as u64, w).expect("failed to create node");
12 engine.add_node(node);
13 }
14
15 let total_rounds = 100;
16 println!(
17 "Simulating {} rounds with {} nodes (f=3)\n",
18 total_rounds,
19 weights.len()
20 );
21 println!(
22 "Node weights: {:?}",
23 weights
24 );
25 println!("Total weight: {}\n", weights.iter().sum::<u64>());
26
27 let blocks = engine.run(total_rounds).expect("simulation failed");
28
29 // Proposer distribution
30 let mut proposer_count: HashMap<u64, u32> = HashMap::new();
31 for block in &blocks {
32 *proposer_count.entry(block.proposer).or_default() += 1;
33 }
34
35 println!("Results:");
36 println!(" Rounds attempted: {}", engine.round());
37 println!(" Blocks committed: {}", blocks.len());
38 println!(
39 " Success rate: {:.1}%",
40 blocks.len() as f64 / engine.round() as f64 * 100.0
41 );
42
43 println!("\nProposer distribution:");
44 let mut sorted: Vec<_> = proposer_count.iter().collect();
45 sorted.sort_by_key(|(id, _)| *id);
46 for (id, count) in sorted {
47 let w = weights[(*id - 1) as usize];
48 println!(" Node {} (weight {:>3}): {} blocks", id, w, count);
49 }
50
51 // Verify all nodes have the same ledger length
52 let ledger_lens: Vec<usize> = engine.nodes().iter().map(|n| n.ledger().len()).collect();
53 let all_same = ledger_lens.windows(2).all(|w| w[0] == w[1]);
54 println!(
55 "\nLedger consistency: {} (all nodes have {} blocks)",
56 if all_same { "PASS" } else { "FAIL" },
57 ledger_lens[0]
58 );
59}Auto Trait Implementations§
impl Freeze for ConsensusEngine
impl RefUnwindSafe for ConsensusEngine
impl Send for ConsensusEngine
impl Sync for ConsensusEngine
impl Unpin for ConsensusEngine
impl UnsafeUnpin for ConsensusEngine
impl UnwindSafe for ConsensusEngine
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more