Skip to content

Commit 3bdd45f

Browse files
authored
Merge pull request #4065 from ProvableHQ/refactor/no-bft-channels
[Refactor] Remove BFT channels
2 parents 0e0a697 + 9cf87f9 commit 3bdd45f

13 files changed

Lines changed: 324 additions & 341 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,21 +99,30 @@ fn check_locktick_imports<P: AsRef<Path>>(path: P) {
9999

100100
// Modify the lock balance based on the type of the relevant import.
101101
if [ImportOfInterest::ParkingLot, ImportOfInterest::Tokio].contains(&ioi) {
102-
if line.contains("Mutex") {
103-
lock_balance += 1;
102+
lock_balance += line.matches("Mutex").count() as i8;
103+
lock_balance += line.matches("RwLock").count() as i8;
104+
105+
// A correction in case of the `use tokio::Mutex as TMutex` convention.
106+
if line.contains("TMutex") {
107+
lock_balance -= 1;
104108
}
105-
if line.contains("RwLock") {
106-
lock_balance += 1;
109+
110+
// Account for lock guards, which do not have a locktick counterpart.
111+
if line.contains("MutexGuard") {
112+
lock_balance -= 1;
107113
}
108-
} else if ioi == ImportOfInterest::Locktick {
109-
// Use `matches` instead of just `contains` here, as more than a single
110-
// lock type entry is possible in a locktick import.
111-
for _hit in line.matches("Mutex") {
114+
if line.contains("RwLockReadGuard") {
112115
lock_balance -= 1;
113116
}
114-
for _hit in line.matches("RwLock") {
117+
if line.contains("RwLockWriteGuard") {
115118
lock_balance -= 1;
116119
}
120+
} else if ioi == ImportOfInterest::Locktick {
121+
// Use `matches` instead of just `contains` here, as more than a single
122+
// lock type entry is possible in a locktick import.
123+
lock_balance -= line.matches("Mutex").count() as i8;
124+
lock_balance -= line.matches("RwLock").count() as i8;
125+
117126
// A correction in case of the `use tokio::Mutex as TMutex` convention.
118127
if line.contains("TMutex") {
119128
lock_balance += 1;

node/bft/examples/simple_node.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ pub async fn start_primary(
200200
let trusted_peers_only = false;
201201
// Initialize the primary instance.
202202
let block_sync = Arc::new(BlockSync::new(ledger.clone()));
203-
let mut primary = Primary::<CurrentNetwork>::new(
203+
let primary = Primary::<CurrentNetwork>::new(
204204
account,
205205
storage,
206206
ledger,
@@ -212,7 +212,7 @@ pub async fn start_primary(
212212
None,
213213
)?;
214214
// Run the primary instance.
215-
primary.run(None, None, sender.clone(), receiver).await?;
215+
primary.run(None, None, None, sender.clone(), receiver).await?;
216216
// Handle OS signals.
217217
handle_signals(&primary);
218218
// Return the primary instance.

node/bft/src/bft.rs

Lines changed: 54 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,9 @@
1515

1616
use crate::{
1717
MAX_LEADER_CERTIFICATE_DELAY_IN_SECS,
18-
Primary,
19-
helpers::{
20-
BFTReceiver,
21-
ConsensusSender,
22-
DAG,
23-
PrimaryReceiver,
24-
PrimarySender,
25-
Storage,
26-
fmt_id,
27-
init_bft_channels,
28-
now,
29-
},
18+
helpers::{ConsensusSender, DAG, PrimaryReceiver, PrimarySender, Storage, fmt_id, now},
19+
primary::{Primary, PrimaryCallback},
20+
sync::SyncCallback,
3021
};
3122

3223
use snarkos_account::Account;
@@ -50,15 +41,11 @@ use anyhow::Context;
5041
use colored::Colorize;
5142
use indexmap::{IndexMap, IndexSet};
5243
#[cfg(feature = "locktick")]
53-
use locktick::{
54-
parking_lot::{Mutex, RwLock},
55-
tokio::Mutex as TMutex,
56-
};
44+
use locktick::{parking_lot::RwLock, tokio::Mutex as TMutex};
5745
#[cfg(not(feature = "locktick"))]
58-
use parking_lot::{Mutex, RwLock};
46+
use parking_lot::RwLock;
5947
use std::{
6048
collections::{BTreeMap, HashSet},
61-
future::Future,
6249
net::SocketAddr,
6350
sync::{
6451
Arc,
@@ -67,10 +54,7 @@ use std::{
6754
};
6855
#[cfg(not(feature = "locktick"))]
6956
use tokio::sync::Mutex as TMutex;
70-
use tokio::{
71-
sync::{OnceCell, oneshot},
72-
task::JoinHandle,
73-
};
57+
use tokio::sync::{OnceCell, oneshot};
7458

7559
#[derive(Clone)]
7660
pub struct BFT<N: Network> {
@@ -84,8 +68,6 @@ pub struct BFT<N: Network> {
8468
leader_certificate_timer: Arc<AtomicI64>,
8569
/// The consensus sender.
8670
consensus_sender: Arc<OnceCell<ConsensusSender<N>>>,
87-
/// Handles for all spawned tasks.
88-
handles: Arc<Mutex<Vec<JoinHandle<()>>>>,
8971
/// The BFT lock.
9072
lock: Arc<TMutex<()>>,
9173
}
@@ -120,7 +102,6 @@ impl<N: Network> BFT<N> {
120102
leader_certificate: Default::default(),
121103
leader_certificate_timer: Default::default(),
122104
consensus_sender: Default::default(),
123-
handles: Default::default(),
124105
lock: Default::default(),
125106
})
126107
}
@@ -137,14 +118,16 @@ impl<N: Network> BFT<N> {
137118
primary_receiver: PrimaryReceiver<N>,
138119
) -> Result<()> {
139120
info!("Starting the BFT instance...");
140-
// Initialize the BFT channels.
141-
let (bft_sender, bft_receiver) = init_bft_channels::<N>();
142-
// First, start the BFT handlers.
143-
self.start_handlers(bft_receiver);
121+
// Set up callbacks.
122+
let primary_callback = Some(Arc::new(self.clone()) as Arc<dyn PrimaryCallback<N>>);
123+
124+
let sync_callback = Some(Arc::new(self.clone()) as Arc<dyn SyncCallback<N>>);
125+
144126
// Next, run the primary instance.
145-
self.primary.run(ping, Some(bft_sender), primary_sender, primary_receiver).await?;
127+
self.primary.run(ping, primary_callback, sync_callback, primary_sender, primary_receiver).await?;
128+
146129
// Lastly, set the consensus sender.
147-
// Note: This ensures during initial syncing, that the BFT does not advance the ledger.
130+
// Note: This ensures that the BFT does not advance the ledger during initial syncing.
148131
if let Some(consensus_sender) = consensus_sender {
149132
self.consensus_sender.set(consensus_sender).expect("Consensus sender already set");
150133
}
@@ -226,8 +209,9 @@ impl<N: Network> BFT<N> {
226209
}
227210
}
228211

229-
impl<N: Network> BFT<N> {
230-
/// Stores the certificate in the DAG, and attempts to commit one or more anchors.
212+
#[async_trait::async_trait]
213+
impl<N: Network> PrimaryCallback<N> for BFT<N> {
214+
/// Notification that a new round has started.
231215
fn update_to_next_round(&self, current_round: u64) -> bool {
232216
// Ensure the current round is at least the storage round (this is a sanity check).
233217
let storage_round = self.storage().current_round();
@@ -299,6 +283,39 @@ impl<N: Network> BFT<N> {
299283
is_ready
300284
}
301285

286+
/// Notification about a new certificated generated by `Primary` or received by the `Primary` from a peer.
287+
async fn add_new_certificate(&self, certificate: BatchCertificate<N>) -> Result<()> {
288+
// Update the DAG with the certificate.
289+
self.update_dag::<true, false>(certificate).await
290+
}
291+
}
292+
293+
#[async_trait::async_trait]
294+
impl<N: Network> SyncCallback<N> for BFT<N> {
295+
/// Syncs the BFT DAG with the given batch certificates. These batch certificates **must**
296+
/// already exist in the ledger.
297+
///
298+
/// This method commits all the certificates into the DAG.
299+
/// Note that there is no need to insert the certificates into the DAG, because these certificates
300+
/// already exist in the ledger and therefore do not need to be re-ordered into future committed subdags.
301+
async fn sync_dag_at_bootup(&self, certificates: Vec<BatchCertificate<N>>) {
302+
// Acquire the BFT write lock.
303+
let mut dag = self.dag.write();
304+
305+
// Commit all the certificates.
306+
for certificate in certificates {
307+
dag.commit(&certificate, self.storage().max_gc_rounds());
308+
}
309+
}
310+
311+
/// Notification about a new certificate detected by the `Sync` instance after fetching a new block.
312+
async fn add_new_certificate(&self, certificate: BatchCertificate<N>) -> Result<()> {
313+
// Update the DAG with the certificate.
314+
self.update_dag::<true, true>(certificate).await
315+
}
316+
}
317+
318+
impl<N: Network> BFT<N> {
302319
/// Updates the leader certificate to the current even round,
303320
/// returning `true` if the BFT is ready to update to the next round.
304321
///
@@ -901,86 +918,13 @@ impl<N: Network> BFT<N> {
901918
}
902919

903920
impl<N: Network> BFT<N> {
904-
/// Starts the BFT handlers.
905-
fn start_handlers(&self, bft_receiver: BFTReceiver<N>) {
906-
let BFTReceiver {
907-
mut rx_primary_round,
908-
mut rx_primary_certificate,
909-
mut rx_sync_bft_dag_at_bootup,
910-
mut rx_sync_bft,
911-
} = bft_receiver;
912-
913-
// Process the current round from the primary.
914-
let self_ = self.clone();
915-
self.spawn(async move {
916-
while let Some((current_round, callback)) = rx_primary_round.recv().await {
917-
callback.send(self_.update_to_next_round(current_round)).ok();
918-
}
919-
});
920-
921-
// Process the certificate from the primary.
922-
let self_ = self.clone();
923-
self.spawn(async move {
924-
while let Some((certificate, callback)) = rx_primary_certificate.recv().await {
925-
// Update the DAG with the certificate.
926-
let result = self_.update_dag::<true, false>(certificate).await;
927-
// Send the callback **after** updating the DAG.
928-
// Note: We must await the DAG update before proceeding.
929-
callback.send(result).ok();
930-
}
931-
});
932-
933-
// Process the request to sync the BFT DAG at bootup.
934-
let self_ = self.clone();
935-
self.spawn(async move {
936-
while let Some(certificates) = rx_sync_bft_dag_at_bootup.recv().await {
937-
self_.sync_bft_dag_at_bootup(certificates).await;
938-
}
939-
});
940-
941-
// Handler for new certificates that were fetched by the sync module.
942-
let self_ = self.clone();
943-
self.spawn(async move {
944-
while let Some((certificate, callback)) = rx_sync_bft.recv().await {
945-
// Update the DAG with the certificate.
946-
let result = self_.update_dag::<true, true>(certificate).await;
947-
// Send the callback **after** updating the DAG.
948-
// Note: We must await the DAG update before proceeding.
949-
callback.send(result).ok();
950-
}
951-
});
952-
}
953-
954-
/// Syncs the BFT DAG with the given batch certificates. These batch certificates **must**
955-
/// already exist in the ledger.
956-
///
957-
/// This method commits all the certificates into the DAG.
958-
/// Note that there is no need to insert the certificates into the DAG, because these certificates
959-
/// already exist in the ledger and therefore do not need to be re-ordered into future committed subdags.
960-
async fn sync_bft_dag_at_bootup(&self, certificates: Vec<BatchCertificate<N>>) {
961-
// Acquire the BFT write lock.
962-
let mut dag = self.dag.write();
963-
964-
// Commit all the certificates.
965-
for certificate in certificates {
966-
dag.commit(&certificate, self.storage().max_gc_rounds());
967-
}
968-
}
969-
970-
/// Spawns a task with the given future; it should only be used for long-running tasks.
971-
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T) {
972-
self.handles.lock().push(tokio::spawn(future));
973-
}
974-
975921
/// Shuts down the BFT.
976922
pub async fn shut_down(&self) {
977923
info!("Shutting down the BFT...");
978924
// Acquire the lock.
979925
let _lock = self.lock.lock().await;
980926
// Shut down the primary.
981927
self.primary.shut_down().await;
982-
// Abort the tasks.
983-
self.handles.lock().iter().for_each(|handle| handle.abort());
984928
}
985929
}
986930

@@ -990,6 +934,7 @@ mod tests {
990934
BFT,
991935
MAX_LEADER_CERTIFICATE_DELAY_IN_SECS,
992936
helpers::{Storage, dag::test_helpers::mock_dag_with_modified_last_committed_round},
937+
sync::SyncCallback,
993938
};
994939

995940
use snarkos_account::Account;
@@ -1587,7 +1532,7 @@ mod tests {
15871532
let bootup_bft = initialize_bft(account.clone(), storage_2, ledger)?;
15881533

15891534
// Sync the BFT DAG at bootup.
1590-
bootup_bft.sync_bft_dag_at_bootup(certificates.clone()).await;
1535+
bootup_bft.sync_dag_at_bootup(certificates.clone()).await;
15911536

15921537
// Check that the BFT starts from the same last committed round.
15931538
assert_eq!(bft.dag.read().last_committed_round(), bootup_bft.dag.read().last_committed_round());
@@ -1766,7 +1711,7 @@ mod tests {
17661711
let bootup_bft = initialize_bft(account.clone(), bootup_storage.clone(), ledger.clone())?;
17671712

17681713
// Sync the BFT DAG at bootup.
1769-
bootup_bft.sync_bft_dag_at_bootup(pre_shutdown_certificates.clone()).await;
1714+
bootup_bft.sync_dag_at_bootup(pre_shutdown_certificates.clone()).await;
17701715

17711716
// Insert the post shutdown certificates to the storage and BFT with bootup.
17721717
for certificate in post_shutdown_certificates.iter() {
@@ -1946,7 +1891,7 @@ mod tests {
19461891
// Insert a mock DAG in the BFT without bootup.
19471892
*bootup_bft.dag.write() = crate::helpers::dag::test_helpers::mock_dag_with_modified_last_committed_round(0);
19481893
// Sync the BFT DAG at bootup.
1949-
bootup_bft.sync_bft_dag_at_bootup(pre_shutdown_certificates.clone()).await;
1894+
bootup_bft.sync_dag_at_bootup(pre_shutdown_certificates.clone()).await;
19501895

19511896
// Insert the post shutdown certificates into the storage.
19521897
let mut post_shutdown_certificates: Vec<snarkvm::ledger::narwhal::BatchCertificate<CurrentNetwork>> =

node/bft/src/gateway.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,6 @@ mod prop_tests {
17461746
MAX_WORKERS,
17471747
MEMORY_POOL_PORT,
17481748
Worker,
1749-
gateway::prop_tests::GatewayAddress::{Dev, Prod},
17501749
helpers::{Storage, init_primary_channels, init_worker_channels},
17511750
};
17521751

@@ -1848,7 +1847,7 @@ mod prop_tests {
18481847
Just(account_selector.select(validators)),
18491848
0u8..,
18501849
)
1851-
.prop_map(|(a, b, c, d)| (a, b, c.private_key, Dev(d)))
1850+
.prop_map(|(a, b, c, d)| (a, b, c.private_key, GatewayAddress::Dev(d)))
18521851
})
18531852
.boxed()
18541853
}
@@ -1863,7 +1862,7 @@ mod prop_tests {
18631862
Just(account_selector.select(validators)),
18641863
any::<Option<SocketAddr>>(),
18651864
)
1866-
.prop_map(|(a, b, c, d)| (a, b, c.private_key, Prod(d)))
1865+
.prop_map(|(a, b, c, d)| (a, b, c.private_key, GatewayAddress::Prod(d)))
18671866
})
18681867
.boxed()
18691868
}

0 commit comments

Comments
 (0)