Skip to content

[Bug] Node always assigns itself hardcoded voting power of 100 #91

@qj0r9j0vc2

Description

@qj0r9j0vc2

Problem

In crates/node/src/node.rs:run(), the node always assigns itself a hardcoded voting power of 100, ignoring the actual stake-based voting power calculated during genesis bootstrap.

Location

crates/node/src/node.rs:254-259

Code Analysis

// Start with ourselves
let mut consensus_validators = vec![ConsensusValidator::new(
    self.validator_id,
    ed25519_pubkey,
    100, // HARDCODED! Ignores actual stake-based voting power
)];

// Add all other validators from the known validator set
for (validator_id, info) in &self.validators {
    if *validator_id != self.validator_id {
        consensus_validators.push(ConsensusValidator::new(
            *validator_id,
            info.ed25519_public_key.clone(),
            info.voting_power,  // Other validators use correct power
        ));
    }
}

Meanwhile, bootstrap_validators_from_genesis() correctly calculates voting power:

let voting_power = if total_stake.is_zero() {
    100u64
} else {
    let stake_u128: u128 = validator.staked_amount.try_into().unwrap_or(u128::MAX);
    let total_u128: u128 = total_stake.try_into().unwrap_or(u128::MAX);
    let power = (stake_u128.saturating_mul(10000)) / total_u128.max(1);
    power.max(1) as u64
};

Impact

  • Proposer selection is wrong: Round-robin proposer selection weighted by voting power will be incorrect
  • Vote threshold miscalculation: 2f+1 quorum calculations will be wrong if validators have different stake amounts
  • Potential liveness issues: If the node's actual stake differs significantly from 100, consensus rounds may not reach quorum correctly

Example

If genesis has:

  • Validator A (us): 1000 ETH staked → should have voting power 5000
  • Validator B: 500 ETH staked → voting power 2500
  • Validator C: 500 ETH staked → voting power 2500

Actual result:

  • Validator A: voting power 100 (hardcoded)
  • Validator B: voting power 2500 (correct)
  • Validator C: voting power 2500 (correct)

Suggested Fix

// Get our own voting power from the validator set
let our_voting_power = self.validators
    .get(&self.validator_id)
    .map(|info| info.voting_power)
    .unwrap_or(100); // Fallback only if not bootstrapped

let mut consensus_validators = vec![ConsensusValidator::new(
    self.validator_id,
    ed25519_pubkey,
    our_voting_power,
)];

Severity

High - Affects consensus correctness when validators have different stake amounts.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions