Consensus Mechanism

Learn about consensus mechanisms in liquid staking protocols.

Last updated: 2024-03-21
Edit on GitHub

Overview

Understanding and properly implementing consensus mechanisms is crucial for your liquid staking protocol's security and performance. This guide covers stake delegation, vote accounts, Tower BFT consensus, and slashing protection.

Consensus Mechanism Flow

Stake Delegation

Implement secure stake delegation mechanisms to distribute stake across validators.

Stake Delegation Implementation

1pub struct StakeDelegation {
2    /// Vote account to delegate to
3    pub vote_account: Pubkey,
4    
5    /// Stake authority
6    pub stake_authority: Pubkey,
7    
8    /// Withdraw authority
9    pub withdraw_authority: Pubkey,
10    
11    /// Stake amount in lamports
12    pub stake_amount: u64,
13    
14    /// Lockup period
15    pub lockup: Option<Lockup>,
16}
17
18impl StakeDelegation {
19    pub fn create_stake_account(
20        &self,
21        payer: &Keypair,
22        stake_account: &Keypair,
23    ) -> Result<Transaction, ProgramError> {
24        let rent = Rent::get()?.minimum_balance(std::mem::size_of::<StakeState>());
25        
26        let mut transaction = Transaction::new_with_payer(
27            &[
28                // Create stake account
29                system_instruction::create_account(
30                    &payer.pubkey(),
31                    &stake_account.pubkey(),
32                    rent + self.stake_amount,
33                    std::mem::size_of::<StakeState>() as u64,
34                    &stake::program::id(),
35                ),
36                // Initialize stake account
37                stake::instruction::initialize(
38                    &stake_account.pubkey(),
39                    &stake::state::Authorized {
40                        staker: self.stake_authority,
41                        withdrawer: self.withdraw_authority,
42                    },
43                    &self.lockup.unwrap_or_default(),
44                ),
45                // Delegate stake
46                stake::instruction::delegate_stake(
47                    &stake_account.pubkey(),
48                    &self.stake_authority,
49                    &self.vote_account,
50                ),
51            ],
52            Some(&payer.pubkey()),
53        );
54        
55        Ok(transaction)
56    }
57    
58    pub fn deactivate_stake(
59        &self,
60        stake_account: &Pubkey,
61    ) -> Result<Transaction, ProgramError> {
62        let transaction = Transaction::new_with_payer(
63            &[stake::instruction::deactivate_stake(
64                stake_account,
65                &self.stake_authority,
66            )],
67            Some(&self.stake_authority),
68        );
69        
70        Ok(transaction)
71    }
72}

Vote Account Management

Implement vote account management and monitoring.

Vote Account Implementation

1pub struct VoteAccountManager {
2    /// RPC connection
3    pub connection: Arc<RpcClient>,
4    
5    /// Vote account
6    pub vote_account: Pubkey,
7    
8    /// Vote authority
9    pub vote_authority: Keypair,
10    
11    /// Withdraw authority
12    pub withdraw_authority: Keypair,
13}
14
15impl VoteAccountManager {
16    pub async fn create_vote_account(
17        &self,
18        node_pubkey: &Pubkey,
19        commission: u8,
20    ) -> Result<Transaction, ProgramError> {
21        let rent = self.connection
22            .get_minimum_balance_for_rent_exemption(VoteState::size_of())
23            .await?;
24            
25        let mut transaction = Transaction::new_with_payer(
26            &[
27                // Create account
28                system_instruction::create_account(
29                    &self.vote_authority.pubkey(),
30                    &self.vote_account,
31                    rent,
32                    VoteState::size_of() as u64,
33                    &solana_vote_program::id(),
34                ),
35                // Initialize vote account
36                vote_instruction::initialize_account(
37                    &self.vote_account,
38                    node_pubkey,
39                    &self.vote_authority.pubkey(),
40                    &self.withdraw_authority.pubkey(),
41                    commission,
42                ),
43            ],
44            Some(&self.vote_authority.pubkey()),
45        );
46        
47        Ok(transaction)
48    }
49    
50    pub async fn update_commission(
51        &self,
52        new_commission: u8,
53    ) -> Result<Transaction, ProgramError> {
54        let transaction = Transaction::new_with_payer(
55            &[vote_instruction::update_commission(
56                &self.vote_account,
57                &self.withdraw_authority.pubkey(),
58                new_commission,
59            )],
60            Some(&self.withdraw_authority.pubkey()),
61        );
62        
63        Ok(transaction)
64    }
65    
66    pub async fn withdraw_rewards(
67        &self,
68        rewards_account: &Pubkey,
69    ) -> Result<Transaction, ProgramError> {
70        let balance = self.connection
71            .get_balance(&self.vote_account)
72            .await?;
73            
74        let transaction = Transaction::new_with_payer(
75            &[vote_instruction::withdraw(
76                &self.vote_account,
77                &self.withdraw_authority.pubkey(),
78                balance,
79                rewards_account,
80            )],
81            Some(&self.withdraw_authority.pubkey()),
82        );
83        
84        Ok(transaction)
85    }
86}

Tower BFT Implementation

Understand and interact with Solana's Tower BFT consensus protocol.

Tower BFT Interaction

1pub struct TowerBFT {
2    /// Vote state
3    pub vote_state: VoteState,
4    
5    /// Root slot
6    pub root_slot: Slot,
7    
8    /// Locked out slots
9    pub lockouts: Vec<Lockout>,
10    
11    /// Last voted slot
12    pub last_voted_slot: Option<Slot>,
13}
14
15impl TowerBFT {
16    pub fn process_vote(
17        &mut self,
18        slot: Slot,
19        hash: Hash,
20    ) -> Result<Vote, ProgramError> {
21        // Check if slot is locked out
22        if self.is_locked_out(slot) {
23            return Err(ProgramError::InvalidArgument);
24        }
25        
26        // Create vote
27        let vote = Vote::new(vec![slot], hash);
28        
29        // Update lockouts
30        self.lockouts.push(Lockout {
31            slot,
32            confirmation_count: 0,
33        });
34        
35        // Update last voted slot
36        self.last_voted_slot = Some(slot);
37        
38        Ok(vote)
39    }
40    
41    pub fn is_locked_out(&self, slot: Slot) -> bool {
42        self.lockouts.iter().any(|lockout| {
43            lockout.slot >= slot
44                && lockout.confirmation_count > MINIMUM_CONFIRMATIONS
45        })
46    }
47    
48    pub fn process_new_root(
49        &mut self,
50        root: Slot,
51    ) -> Result<(), ProgramError> {
52        // Update root
53        self.root_slot = root;
54        
55        // Prune lockouts
56        self.lockouts.retain(|lockout| lockout.slot > root);
57        
58        Ok(())
59    }
60}

Slashing Protection

Implement robust slashing protection mechanisms.

Slashing Protection Implementation

1pub struct SlashingProtector {
2    /// Validator vote account
3    pub vote_account: Pubkey,
4    
5    /// Stake account
6    pub stake_account: Pubkey,
7    
8    /// Monitoring thresholds
9    pub thresholds: SlashingThresholds,
10    
11    /// Historical performance
12    pub history: VoteHistory,
13}
14
15impl SlashingProtector {
16    pub async fn check_validator_behavior(
17        &self,
18        connection: &RpcClient,
19    ) -> Result<ValidatorStatus, ProgramError> {
20        // Get vote account status
21        let vote_account = connection
22            .get_vote_account(&self.vote_account)
23            .await?;
24            
25        // Check for double voting
26        if self.detect_double_voting(&vote_account)? {
27            return Ok(ValidatorStatus::DoubleVoting);
28        }
29        
30        // Check for skip rate
31        let skip_rate = self.calculate_skip_rate(&vote_account)?;
32        if skip_rate > self.thresholds.max_skip_rate {
33            return Ok(ValidatorStatus::ExcessiveSkips);
34        }
35        
36        // Check for downtime
37        let uptime = self.calculate_uptime(&vote_account)?;
38        if uptime < self.thresholds.min_uptime {
39            return Ok(ValidatorStatus::Downtime);
40        }
41        
42        Ok(ValidatorStatus::Healthy)
43    }
44    
45    pub async fn handle_slashing_event(
46        &self,
47        connection: &RpcClient,
48        status: ValidatorStatus,
49    ) -> Result<Transaction, ProgramError> {
50        match status {
51            ValidatorStatus::DoubleVoting => {
52                // Emergency unstake all funds
53                self.emergency_unstake(connection).await
54            }
55            ValidatorStatus::ExcessiveSkips | ValidatorStatus::Downtime => {
56                // Partial unstake based on severity
57                let unstake_amount = self.calculate_unstake_amount(status)?;
58                self.partial_unstake(connection, unstake_amount).await
59            }
60            ValidatorStatus::Healthy => Ok(Transaction::default()),
61        }
62    }
63    
64    async fn emergency_unstake(
65        &self,
66        connection: &RpcClient,
67    ) -> Result<Transaction, ProgramError> {
68        // Create emergency unstake instruction
69        let instruction = stake::instruction::deactivate_stake(
70            &self.stake_account,
71            &self.stake_authority,
72        );
73        
74        // Build and return transaction
75        let transaction = Transaction::new_with_payer(
76            &[instruction],
77            Some(&self.stake_authority),
78        );
79        
80        Ok(transaction)
81    }
82    
83    fn detect_double_voting(
84        &self,
85        vote_account: &VoteAccountStatus,
86    ) -> Result<bool, ProgramError> {
87        // Check for conflicting votes in history
88        for votes in vote_account.votes.windows(2) {
89            if votes[0].slot >= votes[1].slot {
90                return Ok(true);
91            }
92        }
93        
94        Ok(false)
95    }
96}

Performance Monitoring

Implement comprehensive validator performance monitoring.

Performance Monitoring Implementation

1pub struct ValidatorMonitor {
2    /// RPC connection
3    pub connection: Arc<RpcClient>,
4    
5    /// Metrics client
6    pub metrics: Arc<MetricsClient>,
7    
8    /// Alert system
9    pub alerts: Arc<AlertSystem>,
10    
11    /// Monitoring thresholds
12    pub thresholds: MonitoringThresholds,
13}
14
15impl ValidatorMonitor {
16    pub async fn monitor_validator_performance(
17        &self,
18        vote_account: &Pubkey,
19    ) -> Result<ValidatorMetrics, ProgramError> {
20        // Get vote account status
21        let status = self.connection
22            .get_vote_account(vote_account)
23            .await?;
24            
25        // Calculate metrics
26        let metrics = ValidatorMetrics {
27            epoch_credits: status.epoch_credits,
28            skip_rate: self.calculate_skip_rate(&status)?,
29            uptime: self.calculate_uptime(&status)?,
30            commission: status.commission,
31            last_vote: status.last_vote,
32        };
33        
34        // Check thresholds
35        self.check_thresholds(&metrics).await?;
36        
37        // Record metrics
38        self.record_metrics(vote_account, &metrics).await?;
39        
40        Ok(metrics)
41    }
42    
43    async fn check_thresholds(
44        &self,
45        metrics: &ValidatorMetrics,
46    ) -> Result<(), ProgramError> {
47        // Check skip rate
48        if metrics.skip_rate > self.thresholds.max_skip_rate {
49            self.alerts.send_alert(
50                AlertType::HighSkipRate {
51                    skip_rate: metrics.skip_rate,
52                    threshold: self.thresholds.max_skip_rate,
53                },
54            ).await?;
55        }
56        
57        // Check uptime
58        if metrics.uptime < self.thresholds.min_uptime {
59            self.alerts.send_alert(
60                AlertType::LowUptime {
61                    uptime: metrics.uptime,
62                    threshold: self.thresholds.min_uptime,
63                },
64            ).await?;
65        }
66        
67        Ok(())
68    }
69    
70    async fn record_metrics(
71        &self,
72        vote_account: &Pubkey,
73        metrics: &ValidatorMetrics,
74    ) -> Result<(), ProgramError> {
75        self.metrics.record_metrics(
76            vote_account,
77            &[
78                ("skip_rate", metrics.skip_rate),
79                ("uptime", metrics.uptime),
80                ("epoch_credits", metrics.epoch_credits as f64),
81                ("commission", metrics.commission as f64),
82            ],
83        ).await
84    }
85}
Consensus Considerations
  • Implement proper stake delegation with lockup periods
  • Monitor validator performance and vote history
  • Implement robust slashing protection mechanisms
  • Set up comprehensive performance monitoring
  • Handle emergency situations with proper procedures
  • Maintain detailed metrics and alerts