Skip to content

Conversation

@lukaszrzasik
Copy link
Contributor

Closes #<ISSUE_NUMBER>

This PR:

Adds vote participation API.
The API can be used to show each's node vote participation in the current and previous epoch.

This PR does not:

Key places to review:

@gemini-code-assist
Copy link

Summary of Changes

Hello @lukaszrzasik, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive system for tracking and exposing node vote participation within the HotShot consensus mechanism. It adds new data structures and logic to the core consensus engine to monitor voting activity across epochs and provides new API endpoints for external systems to query this participation data. The changes aim to improve visibility into validator behavior and overall network health.

Highlights

  • New Vote Participation API: Introduced a new API to track and expose node vote participation rates for the current and previous epochs, enhancing transparency in the consensus process.
  • Core Consensus Logic: Added a new VoteParticipation struct within hotshot-types/src/consensus.rs to manage vote counts, views, stake tables, and success thresholds. This struct includes methods for updating participation based on Quorum Certificates (QCs) and transitioning between epochs.
  • Integration with Consensus Tasks: Modified hotshot-task-impls/src/consensus/mod.rs to update vote participation upon new epoch transitions and hotshot-task-impls/src/quorum_vote/handlers.rs to update participation when quorum proposals are validated.
  • API Endpoints: Exposed new API endpoints /current-vote-participation and /previous-vote-participation in the sequencer to allow external querying of the newly tracked vote participation data.
  • Signature Key Refactoring: Refactored the QuorumCertificateScheme and SignatureKey traits to introduce a signers method, allowing retrieval of signers from a QC without requiring full signature verification, which is used for vote participation tracking.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a vote participation API, which is a valuable addition for monitoring node health. The implementation is well-structured, primarily through the new VoteParticipation struct within the Consensus state, and the API endpoints are correctly defined. I've noted several opportunities to refactor duplicated code, which would enhance the maintainability of the new functionality. Overall, this is a strong contribution.

Comment on lines +230 to +246
let (stake_table, success_threshold) = if let Ok(epoch_membership) = self
.membership_coordinator
.stake_table_for_epoch(Some(next_epoch))
.await
{
(
epoch_membership.stake_table().await,
epoch_membership.success_threshold().await,
)
} else {
tracing::warn!(
"Failed to get stake table for epoch {} while updating vote \
participation",
next_epoch
);
(HSStakeTable::default(), U256::MAX)
};

Choose a reason for hiding this comment

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

medium

This logic for fetching the stake table and success threshold is also present in crates/hotshot/hotshot/src/lib.rs. To improve maintainability and reduce code duplication, consider adding a helper function to EpochMembershipCoordinator that encapsulates this logic.

Comment on lines +559 to +575
fn current_vote_participation(
&self,
) -> HashMap<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType, f64> {
self.current_epoch_participation
.iter()
.map(|(key, votes)| {
(
key.clone(),
if self.current_epoch_num_views == 0 {
0.0
} else {
*votes as f64 / self.current_epoch_num_views as f64
},
)
})
.collect()
}

Choose a reason for hiding this comment

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

medium

The logic for calculating the vote ratio is repeated in get_participation, current_vote_participation, and previous_vote_participation. To improve code clarity and reduce duplication, consider extracting this logic into a private helper function.

For example:

fn calculate_ratio(num_votes: &u64, total_views: u64) -> f64 {
    if total_views == 0 {
        0.0
    } else {
        *num_votes as f64 / total_views as f64
    }
}

You can then use this helper in current_vote_participation and other methods.

Comment on lines +179 to +196
fn signers(
&self,
stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
threshold: U256,
) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
if self.view_number == TYPES::View::genesis() {
return Ok(vec![]);
}
let real_qc_pp =
<TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);

<TYPES::SignatureKey as SignatureKey>::signers(
&real_qc_pp,
self.signatures.as_ref().unwrap(),
)
.wrap()
.context(|e| warn!("Tracing signers: {e}"))
}

Choose a reason for hiding this comment

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

medium

The signers method implementation is duplicated across three Certificate trait implementations in this file (DaData, DaData2, and QuorumMarker). To avoid code duplication, you could move this logic into a method on SimpleCertificate itself, and then have the trait implementations call this shared method.

Comment on lines +490 to +500
/// Get the current vote participation.
async fn current_vote_participation(&self) -> HashMap<PubKey, f64> {
self.consensus()
.await
.read()
.await
.consensus()
.read()
.await
.current_vote_participation()
}

Choose a reason for hiding this comment

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

medium

The pattern for accessing the consensus state is repeated for current_proposal_participation, previous_proposal_participation, and the new current_vote_participation and previous_vote_participation methods. To reduce boilerplate and improve readability, consider adding a helper method to ApiState that encapsulates this access pattern.

For example:

async fn get_consensus_read_lock(&self) -> async_lock::RwLockReadGuard<'_, hotshot_types::consensus::Consensus<SeqTypes>> {
    self.consensus()
        .await
        .read()
        .await
        .consensus()
        .read()
        .await
}

Then you can simplify this method and others like it:

async fn current_vote_participation(&self) -> HashMap<PubKey, f64> {
    self.get_consensus_read_lock().await.current_vote_participation()
}

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.

2 participants