-
Notifications
You must be signed in to change notification settings - Fork 0
Closed
Labels
Description
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.