Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consensus Module #15

Merged
merged 17 commits into from
Dec 5, 2024
Prev Previous commit
Next Next commit
pr feedback?
  • Loading branch information
ebatsell committed Dec 5, 2024
commit 0e93dc4c22a73879bff5b65dc7f8a751f4e8a80c
4 changes: 4 additions & 0 deletions clients/js/jito_tip_router/errors/jitoTipRouter.ts
Original file line number Diff line number Diff line change
@@ -96,6 +96,8 @@ export const JITO_TIP_ROUTER_ERROR__VOTING_NOT_FINALIZED = 0x2221; // 8737
export const JITO_TIP_ROUTER_ERROR__TIE_BREAKER_NOT_IN_PRIOR_VOTES = 0x2222; // 8738
/** InvalidMerkleProof: Invalid merkle proof */
export const JITO_TIP_ROUTER_ERROR__INVALID_MERKLE_PROOF = 0x2223; // 8739
/** OperatorAdminInvalid: Operator admin needs to sign its vote */
export const JITO_TIP_ROUTER_ERROR__OPERATOR_ADMIN_INVALID = 0x2224; // 8740

export type JitoTipRouterError =
| typeof JITO_TIP_ROUTER_ERROR__ARITHMETIC_OVERFLOW
@@ -122,6 +124,7 @@ export type JitoTipRouterError =
| typeof JITO_TIP_ROUTER_ERROR__NEW_PRECISE_NUMBER_ERROR
| typeof JITO_TIP_ROUTER_ERROR__NO_MINTS_IN_TABLE
| typeof JITO_TIP_ROUTER_ERROR__NO_OPERATORS
| typeof JITO_TIP_ROUTER_ERROR__OPERATOR_ADMIN_INVALID
| typeof JITO_TIP_ROUTER_ERROR__OPERATOR_FINALIZED
| typeof JITO_TIP_ROUTER_ERROR__OPERATOR_VOTES_FULL
| typeof JITO_TIP_ROUTER_ERROR__TIE_BREAKER_ADMIN_INVALID
@@ -167,6 +170,7 @@ if (process.env.NODE_ENV !== 'production') {
[JITO_TIP_ROUTER_ERROR__NEW_PRECISE_NUMBER_ERROR]: `New precise number error`,
[JITO_TIP_ROUTER_ERROR__NO_MINTS_IN_TABLE]: `There are no mints in the table`,
[JITO_TIP_ROUTER_ERROR__NO_OPERATORS]: `No operators in ncn`,
[JITO_TIP_ROUTER_ERROR__OPERATOR_ADMIN_INVALID]: `Operator admin needs to sign its vote`,
[JITO_TIP_ROUTER_ERROR__OPERATOR_FINALIZED]: `Operator is already finalized - should not happen`,
[JITO_TIP_ROUTER_ERROR__OPERATOR_VOTES_FULL]: `Operator votes full`,
[JITO_TIP_ROUTER_ERROR__TIE_BREAKER_ADMIN_INVALID]: `Tie breaker admin invalid`,
Original file line number Diff line number Diff line change
@@ -132,6 +132,9 @@ pub enum JitoTipRouterError {
/// 8739 - Invalid merkle proof
#[error("Invalid merkle proof")]
InvalidMerkleProof = 0x2223,
/// 8740 - Operator admin needs to sign its vote
#[error("Operator admin needs to sign its vote")]
OperatorAdminInvalid = 0x2224,
}

impl solana_program::program_error::PrintProgramError for JitoTipRouterError {
43 changes: 30 additions & 13 deletions core/src/ballot_box.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,11 @@ use solana_program::{
};
use spl_math::precise_number::PreciseNumber;

use crate::{constants::precise_consensus, discriminators::Discriminators, error::TipRouterError};
use crate::{
constants::{precise_consensus, DEFAULT_CONSENSUS_REACHED_SLOT},
discriminators::Discriminators,
error::TipRouterError,
};

#[derive(Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod, ShankType)]
#[repr(C)]
@@ -208,7 +212,7 @@ impl BallotBox {
epoch: PodU64::from(epoch),
bump,
slot_created: PodU64::from(current_slot),
slot_consensus_reached: PodU64::from(0),
slot_consensus_reached: PodU64::from(DEFAULT_CONSENSUS_REACHED_SLOT),
operators_voted: PodU64::from(0),
unique_ballots: PodU64::from(0),
winning_ballot: Ballot::default(),
@@ -296,11 +300,13 @@ impl BallotBox {
}

pub fn is_consensus_reached(&self) -> bool {
self.slot_consensus_reached() > 0 || self.winning_ballot.is_valid()
self.slot_consensus_reached() != DEFAULT_CONSENSUS_REACHED_SLOT
|| self.winning_ballot.is_valid()
}

pub fn tie_breaker_set(&self) -> bool {
self.slot_consensus_reached() == 0 && self.winning_ballot.is_valid()
self.slot_consensus_reached() == DEFAULT_CONSENSUS_REACHED_SLOT
&& self.winning_ballot.is_valid()
}

pub fn get_winning_ballot(&self) -> Result<Ballot, TipRouterError> {
@@ -399,7 +405,7 @@ impl BallotBox {
total_stake_weight: u128,
current_slot: u64,
) -> Result<(), TipRouterError> {
if self.slot_consensus_reached() != 0 {
if self.slot_consensus_reached() != DEFAULT_CONSENSUS_REACHED_SLOT {
return Ok(());
}

@@ -424,7 +430,7 @@ impl BallotBox {
let consensus_reached =
ebatsell marked this conversation as resolved.
Show resolved Hide resolved
ballot_percentage_of_total.greater_than_or_equal(&target_precise_percentage);

if consensus_reached {
if consensus_reached && !self.winning_ballot.is_valid() {
self.slot_consensus_reached = PodU64::from(current_slot);

self.set_winning_ballot(max_tally.ballot());
@@ -478,13 +484,21 @@ impl BallotBox {
current_slot: u64,
valid_slots_after_consensus: u64,
) -> Result<bool, TipRouterError> {
let vote_window_valid = current_slot
<= self
.slot_consensus_reached()
.checked_add(valid_slots_after_consensus)
.ok_or(TipRouterError::ArithmeticOverflow)?;
if self.tie_breaker_set() {
return Ok(false);
}

if self.is_consensus_reached() {
let vote_window_valid = current_slot
<= self
.slot_consensus_reached()
.checked_add(valid_slots_after_consensus)
.ok_or(TipRouterError::ArithmeticOverflow)?;

Ok((!self.is_consensus_reached() || vote_window_valid) && !self.tie_breaker_set())
return Ok(vote_window_valid);
}

Ok(true)
}

pub fn verify_merkle_root(
@@ -687,7 +701,10 @@ mod tests {
.tally_votes(total_stake_weight, current_slot)
.unwrap();
assert!(!ballot_box.is_consensus_reached());
assert_eq!(ballot_box.slot_consensus_reached(), 0);
assert_eq!(
ballot_box.slot_consensus_reached(),
DEFAULT_CONSENSUS_REACHED_SLOT
);
assert!(matches!(
ballot_box.get_winning_ballot(),
Err(TipRouterError::ConsensusNotReached)
2 changes: 2 additions & 0 deletions core/src/constants.rs
Original file line number Diff line number Diff line change
@@ -16,3 +16,5 @@ pub fn precise_consensus() -> Result<PreciseNumber, TipRouterError> {
)
.ok_or(TipRouterError::DenominatorIsZero)
}

pub const DEFAULT_CONSENSUS_REACHED_SLOT: u64 = u64::MAX;
2 changes: 2 additions & 0 deletions core/src/error.rs
Original file line number Diff line number Diff line change
@@ -87,6 +87,8 @@ pub enum TipRouterError {
TieBreakerNotInPriorVotes,
#[error("Invalid merkle proof")]
InvalidMerkleProof,
#[error("Operator admin needs to sign its vote")]
OperatorAdminInvalid,
}

impl<T> DecodeError<T> for TipRouterError {
5 changes: 5 additions & 0 deletions idl/jito_tip_router.json
Original file line number Diff line number Diff line change
@@ -1682,6 +1682,11 @@
"code": 8739,
"name": "InvalidMerkleProof",
"msg": "Invalid merkle proof"
},
{
"code": 8740,
"name": "OperatorAdminInvalid",
"msg": "Operator admin needs to sign its vote"
}
],
"metadata": {
3 changes: 0 additions & 3 deletions integration_tests/tests/fixtures/tip_distribution_client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use anchor_lang::AccountDeserialize;
use jito_tip_distribution_sdk::{jito_tip_distribution, TipDistributionAccount};
// Getters for the Tip Distribution account to verify that we've set the merkle root correctly
use solana_program::{pubkey::Pubkey, system_instruction::transfer};
use solana_program_test::{BanksClient, ProgramTestBanksClientExt};
use solana_sdk::{
@@ -181,9 +180,7 @@ impl TipDistributionClient {
jito_tip_distribution_sdk::derive_config_account_address(&jito_tip_distribution::ID);
let system_program = solana_program::system_program::id();
let validator_vote_account = vote_keypair.pubkey();
println!("Checkpoint E.1");
self.airdrop(&validator_vote_account, 1.0).await?;
println!("Checkpoint E.2");
let (tip_distribution_account, account_bump) =
jito_tip_distribution_sdk::derive_tip_distribution_account_address(
&jito_tip_distribution::ID,
4 changes: 0 additions & 4 deletions integration_tests/tests/fixtures/tip_router_client.rs
Original file line number Diff line number Diff line change
@@ -515,8 +515,6 @@ impl TipRouterClient {

let restaking_config_account = self.get_restaking_config().await?;
let ncn_epoch = slot / restaking_config_account.epoch_length();
println!("Epoch length: {}", restaking_config_account.epoch_length());
println!("ncn_epoch: {}", ncn_epoch);

let config_pda = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0;
let tracked_mints =
@@ -526,7 +524,6 @@ impl TipRouterClient {

let epoch_snapshot =
EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0;
println!("epoch_snapshot: {:?}", epoch_snapshot);

let ix = InitializeEpochSnapshotBuilder::new()
.ncn_config(config_pda)
@@ -766,7 +763,6 @@ impl TipRouterClient {
ncn_epoch,
)
.0;
println!("epoch_snapshot: {:?}", epoch_snapshot);

let operator_snapshot =
jito_tip_router_core::epoch_snapshot::OperatorSnapshot::find_program_address(
7 changes: 6 additions & 1 deletion integration_tests/tests/tip_router/initialize_ballot_box.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#[cfg(test)]
mod tests {

use jito_tip_router_core::constants::DEFAULT_CONSENSUS_REACHED_SLOT;

use crate::fixtures::{test_builder::TestBuilder, TestResult};

#[tokio::test]
@@ -26,7 +28,10 @@ mod tests {
assert_eq!(ballot_box.unique_ballots(), 0);
assert_eq!(ballot_box.operators_voted(), 0);
assert!(!ballot_box.is_consensus_reached());
assert_eq!(ballot_box.slot_consensus_reached(), 0);
assert_eq!(
ballot_box.slot_consensus_reached(),
DEFAULT_CONSENSUS_REACHED_SLOT
);
assert!(ballot_box.get_winning_ballot().is_err(),);

Ok(())
7 changes: 5 additions & 2 deletions integration_tests/tests/tip_router/set_tie_breaker.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(test)]
mod tests {

use jito_tip_router_core::ballot_box::Ballot;
use jito_tip_router_core::{ballot_box::Ballot, constants::DEFAULT_CONSENSUS_REACHED_SLOT};

use crate::fixtures::{test_builder::TestBuilder, TestResult};

@@ -40,7 +40,10 @@ mod tests {

let ballot_box = tip_router_client.get_ballot_box(ncn, ncn_epoch).await?;
assert!(ballot_box.has_ballot(&Ballot::new(meta_merkle_root)));
assert_eq!(ballot_box.slot_consensus_reached(), 0);
assert_eq!(
ballot_box.slot_consensus_reached(),
DEFAULT_CONSENSUS_REACHED_SLOT
);
assert!(!ballot_box.is_consensus_reached());

// Wait a bunch of epochs for voting window to expire (TODO use the exact length)
2 changes: 0 additions & 2 deletions meta_merkle_tree/src/meta_merkle_tree.rs
Original file line number Diff line number Diff line change
@@ -189,8 +189,6 @@ impl MetaMerkleTree {
}
}

println!("Verified proof");

Ok(())
}

8 changes: 7 additions & 1 deletion program/src/cast_vote.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,6 @@ pub fn process_cast_vote(
NcnConfig::load(program_id, ncn.key, ncn_config, false)?;
Ncn::load(restaking_program.key, ncn, false)?;
Operator::load(restaking_program.key, operator, false)?;
// Check admin is operator admin

BallotBox::load(program_id, ncn.key, epoch, ballot_box, true)?;
EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, false)?;
@@ -43,6 +42,13 @@ pub fn process_cast_vote(
false,
)?;

let operator_data = operator.data.borrow();
let operator_account = Operator::try_from_slice_unchecked(&operator_data)?;

if *operator_admin.key != operator_account.admin {
return Err(TipRouterError::OperatorAdminInvalid.into());
}

let valid_slots_after_consensus = {
let ncn_config_data = ncn_config.data.borrow();
let ncn_config = NcnConfig::try_from_slice_unchecked(&ncn_config_data)?;
4 changes: 3 additions & 1 deletion program/src/initialize_ballot_box.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use jito_bytemuck::{AccountDeserialize, Discriminator};
use jito_jsm_core::{
create_account,
loader::{load_system_account, load_system_program},
loader::{load_signer, load_system_account, load_system_program},
};
use jito_tip_router_core::{ballot_box::BallotBox, ncn_config::NcnConfig};
use solana_program::{
@@ -22,6 +22,8 @@ pub fn process_initialize_ballot_box(
load_system_account(ballot_box, true)?;
load_system_program(system_program)?;

ebatsell marked this conversation as resolved.
Show resolved Hide resolved
load_signer(payer, false)?;

NcnConfig::load(program_id, ncn_account.key, ncn_config, false)?;

let (ballot_box_pda, ballot_box_bump, mut ballot_box_seeds) =
Loading