narwhal_e2e.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphaos library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 #[allow(dead_code)] 20 mod common; 21 22 use crate::common::primary::{TestNetwork, TestNetworkConfig}; 23 use alphaos_node_bft::MAX_FETCH_TIMEOUT_IN_MS; 24 25 use std::time::Duration; 26 27 use deadline::deadline; 28 use tokio::time::sleep; 29 30 #[tokio::test(flavor = "multi_thread")] 31 #[ignore = "long-running e2e test"] 32 async fn test_state_coherence() { 33 const N: u16 = 4; 34 const TRANSMISSION_INTERVAL_MS: u64 = 10; 35 36 let mut network = tokio::task::spawn_blocking(|| { 37 TestNetwork::new(TestNetworkConfig { 38 num_nodes: N, 39 bft: false, 40 connect_all: true, 41 fire_transmissions: Some(TRANSMISSION_INTERVAL_MS), 42 // Set this to Some(0..=4) to see the logs. 43 log_level: Some(0), 44 log_connections: true, 45 }) 46 }) 47 .await 48 .unwrap(); 49 50 network.start().await; 51 52 // TODO(nkls): the easiest would be to assert on the anchor or bullshark's output, once 53 // implemented. 54 55 std::future::pending::<()>().await; 56 } 57 58 #[tokio::test(flavor = "multi_thread")] 59 async fn test_quorum_threshold() { 60 // Start N nodes but don't connect them. 61 const N: u16 = 4; 62 const TRANSMISSION_INTERVAL_MS: u64 = 10; 63 64 let mut network = tokio::task::spawn_blocking(|| { 65 TestNetwork::new(TestNetworkConfig { 66 num_nodes: N, 67 bft: false, 68 connect_all: false, 69 fire_transmissions: None, 70 // Set this to Some(0..=4) to see the logs. 71 log_level: None, 72 log_connections: true, 73 }) 74 }) 75 .await 76 .unwrap(); 77 network.start().await; 78 79 // Check each node is at round 1 (0 is genesis). 80 for validators in network.validators.values() { 81 assert_eq!(validators.primary.current_round(), 1); 82 } 83 84 // Start the cannons for node 0. 85 network.fire_transmissions_at(0, TRANSMISSION_INTERVAL_MS); 86 87 sleep(Duration::from_millis(MAX_FETCH_TIMEOUT_IN_MS)).await; 88 89 // Check each node is still at round 1. 90 for validator in network.validators.values() { 91 assert_eq!(validator.primary.current_round(), 1); 92 } 93 94 // Connect the first two nodes and start the cannons for node 1. 95 network.connect_validators(0, 1).await; 96 network.fire_transmissions_at(1, TRANSMISSION_INTERVAL_MS); 97 98 sleep(Duration::from_millis(MAX_FETCH_TIMEOUT_IN_MS)).await; 99 100 // Check each node is still at round 1. 101 for validator in network.validators.values() { 102 assert_eq!(validator.primary.current_round(), 1); 103 } 104 105 // Connect the third node and start the cannons for it. 106 network.connect_validators(0, 2).await; 107 network.connect_validators(1, 2).await; 108 network.fire_transmissions_at(2, TRANSMISSION_INTERVAL_MS); 109 110 // Check the nodes reach quorum and advance through the rounds. 111 const TARGET_ROUND: u64 = 4; 112 let net = network.clone(); 113 deadline!(Duration::from_secs(20), move || { net.is_round_reached(TARGET_ROUND) }); 114 } 115 116 #[tokio::test(flavor = "multi_thread")] 117 async fn test_quorum_break() { 118 // Start N nodes, connect them and start the cannons for each. 119 const N: u16 = 4; 120 const TRANSMISSION_INTERVAL_MS: u64 = 10; 121 let mut network = tokio::task::spawn_blocking(|| { 122 TestNetwork::new(TestNetworkConfig { 123 num_nodes: N, 124 bft: false, 125 connect_all: true, 126 fire_transmissions: Some(TRANSMISSION_INTERVAL_MS), 127 // Set this to Some(0..=4) to see the logs. 128 log_level: None, 129 log_connections: true, 130 }) 131 }) 132 .await 133 .unwrap(); 134 network.start().await; 135 136 // Check the nodes have started advancing through the rounds. 137 const TARGET_ROUND: u64 = 4; 138 // Note: cloning the network is fine because the primaries it wraps are `Arc`ed. 139 let network_clone = network.clone(); 140 deadline!(Duration::from_secs(20), move || { network_clone.is_round_reached(TARGET_ROUND) }); 141 142 // Break the quorum by disconnecting two nodes. 143 const NUM_NODES: u16 = 2; 144 network.disconnect(NUM_NODES).await; 145 146 // Check the nodes have stopped advancing through the rounds. 147 assert!(network.is_halted().await); 148 } 149 150 #[tokio::test(flavor = "multi_thread")] 151 async fn test_storage_coherence() { 152 // Start N nodes, connect them and start the cannons for each. 153 const N: u16 = 4; 154 const TRANSMISSION_INTERVAL_MS: u64 = 10; 155 let mut network = tokio::task::spawn_blocking(|| { 156 TestNetwork::new(TestNetworkConfig { 157 num_nodes: N, 158 bft: false, 159 connect_all: true, 160 fire_transmissions: Some(TRANSMISSION_INTERVAL_MS), 161 // Set this to Some(0..=4) to see the logs. 162 log_level: None, 163 log_connections: true, 164 }) 165 }) 166 .await 167 .unwrap(); 168 network.start().await; 169 170 // Check the nodes have started advancing through the rounds. 171 const TARGET_ROUND: u64 = 12; 172 // Note: cloning the network is fine because the primaries it wraps are `Arc`ed. 173 let network_clone = network.clone(); 174 deadline!(Duration::from_secs(40), move || { network_clone.is_round_reached(TARGET_ROUND) }); 175 176 // Check the committee is coherent across the network up to the target round. We skip the 177 // genesis round. 178 assert!(network.is_committee_coherent(1..TARGET_ROUND)); 179 180 // Check the round certificates are coherent across the network. We skip the genesis round and 181 // check only up to 3 rounds before the the target round, because the network advances when 182 // quorum is reached, not when all the nodes have completed the round and received certificates. 183 assert!(network.is_certificate_round_coherent(1..TARGET_ROUND - 2)); 184 }