Skip to content

feat: add dkg/exchanger #350

Open
varex83 wants to merge 32 commits intomainfrom
bohdan/dkg-exchanger
Open

feat: add dkg/exchanger #350
varex83 wants to merge 32 commits intomainfrom
bohdan/dkg-exchanger

Conversation

@varex83
Copy link
Copy Markdown
Collaborator

@varex83 varex83 commented Apr 23, 2026

No description provided.

@emlautarom1 emlautarom1 self-requested a review April 23, 2026 11:58
Copy link
Copy Markdown
Collaborator

@emlautarom1 emlautarom1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, some small comments on some improvements to the tests.

Comment thread crates/dkg/src/exchanger.rs Outdated
Comment on lines +169 to +170
let sig_types = sig_types.clone();
let st = st.clone();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This matches Charon but I don't understand why we need the same data as Vec and HashSet (we do a contains in both cases).

Comment thread crates/dkg/src/exchanger.rs Outdated
.await
.subscribe_internal(sub)
.await
.expect("subscribe_internal is infallible");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we make subscribe_internal return a Result if it's infallible? Can we update its definition?

Comment thread crates/dkg/src/exchanger.rs Outdated
Comment on lines +515 to +539
// Run concurrent exchanges: for each (node, sig_type) pair, spawn a task
let (result_tx, mut result_rx) =
mpsc::unbounded_channel::<(SigType, HashMap<PubKey, Vec<ParSignedData>>)>();

let mut join_set = tokio::task::JoinSet::new();
for (node_idx, ex) in exchangers.iter().enumerate() {
for &sig_type in &sig_types {
let ex = Arc::clone(ex);
let set = data_to_be_sent[node_idx].clone();
let tx = result_tx.clone();
join_set.spawn(async move {
let data = ex.exchange(sig_type, set).await.expect("exchange failed");
let _ = tx.send((sig_type, data));
});
}
}
drop(result_tx);

// Collect results into actual: one entry per sig_type (last writer wins,
// all nodes return equivalent data for each sig_type)
let mut actual: SigTypeStore = HashMap::new();
while let Some((sig_type, data)) = result_rx.recv().await {
actual.insert(sig_type, data);
}
while join_set.join_next().await.is_some() {}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified by using the JoinSet values instead of using extra channels

Suggested change
// Run concurrent exchanges: for each (node, sig_type) pair, spawn a task
let (result_tx, mut result_rx) =
mpsc::unbounded_channel::<(SigType, HashMap<PubKey, Vec<ParSignedData>>)>();
let mut join_set = tokio::task::JoinSet::new();
for (node_idx, ex) in exchangers.iter().enumerate() {
for &sig_type in &sig_types {
let ex = Arc::clone(ex);
let set = data_to_be_sent[node_idx].clone();
let tx = result_tx.clone();
join_set.spawn(async move {
let data = ex.exchange(sig_type, set).await.expect("exchange failed");
let _ = tx.send((sig_type, data));
});
}
}
drop(result_tx);
// Collect results into actual: one entry per sig_type (last writer wins,
// all nodes return equivalent data for each sig_type)
let mut actual: SigTypeStore = HashMap::new();
while let Some((sig_type, data)) = result_rx.recv().await {
actual.insert(sig_type, data);
}
while join_set.join_next().await.is_some() {}
// Run concurrent exchanges: for each (node, sig_type) pair, spawn a task
let mut join_set = tokio::task::JoinSet::new();
for (node_idx, ex) in exchangers.iter().enumerate() {
for &sig_type in &sig_types {
let ex = Arc::clone(ex);
let set = data_to_be_sent[node_idx].clone();
join_set.spawn(async move {
let data = ex.exchange(sig_type, set).await.expect("exchange failed");
(sig_type, data)
});
}
}
// Collect results into actual: one entry per sig_type (last writer wins,
// all nodes return equivalent data for each sig_type)
let actual: SigTypeStore = join_set.join_all().await.into_iter().collect();

Comment thread crates/dkg/src/exchanger.rs Outdated

for stop_tx in stop_txs {
let _ = stop_tx.send(());
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having multiple stop_txs we could have a single CancellationToken shared across all swarms.

We could also ignore cancellation entirely since the spawned tasks will get dropped when the function completes. The current design is not waiting for them to finish (no join on the handles), so it would not make a difference.

@emlautarom1 emlautarom1 linked an issue Apr 23, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement dkg/exchanger

3 participants