Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 43 additions & 12 deletions src/consensus/parlia/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ use super::{
};
use crate::consensus::parlia::go_rng::{RngSource, Shuffle};
use tracing::{trace, debug, warn};
use crate::consensus::parlia::constants::K_ANCESTOR_GENERATION_DEPTH;
use crate::consensus::parlia::provider::SnapshotProvider;

const RECOVERED_PROPOSER_CACHE_NUM: usize = 4096;
const ADDRESS_LENGTH: usize = 20; // Ethereum address length in bytes
Expand Down Expand Up @@ -533,7 +535,7 @@ where ChainSpec: EthChainSpec + BscHardforks + 'static,
let mut time_for_mining_ms = period_ms / 2;
let last_block_in_turn = snap.last_block_in_one_turn(header.number);
if !last_block_in_turn {
time_for_mining_ms = period_ms * 4 / 5;
time_for_mining_ms = period_ms;
}
if delay_ms > time_for_mining_ms {
delay_ms = time_for_mining_ms;
Expand Down Expand Up @@ -630,27 +632,56 @@ where ChainSpec: EthChainSpec + BscHardforks + 'static,
SYSTEM_TXS_GAS_HARD_LIMIT
}

pub fn assemble_vote_attestation(&self, parent_snap: &Snapshot, parent_header: &Header, current_header: &mut Header) -> Result<(), ParliaConsensusError> {
if !self.spec.is_luban_active_at_block(current_header.number()) || current_header.number() < 2 {
pub fn assemble_vote_attestation(&self, parent_snap: &Snapshot, parent_header: &Header, current_header: &mut Header, snapshot_provider: &Arc<dyn SnapshotProvider + Send + Sync>) -> Result<(), ParliaConsensusError> {
if !self.spec.is_luban_active_at_block(current_header.number()) || current_header.number() < 3 {
return Ok(());
}

let votes = fetch_vote_by_block_hash(current_header.parent_hash());
if votes.len() < usize::div_ceil(parent_snap.validators.len() * 2, 3) {
// get justified number and hash from parent snapshot
let (justified_number, justified_hash) = (parent_snap.vote_data.target_number, parent_snap.vote_data.target_hash);
let mut times = 1;
if self.spec.is_fermi_active_at_timestamp(current_header.number(), current_header.timestamp()) {
times = K_ANCESTOR_GENERATION_DEPTH;
}
let mut votes = Vec::new();
let mut target_header = parent_header.clone();
let mut target_header_parent_snap = None;
for _ in 0..times {
let snap = snapshot_provider.snapshot_by_hash(&target_header.parent_hash()).
ok_or(ParliaConsensusError::SnapshotNotFound {
block_hash: target_header.parent_hash(),
})?;
votes = fetch_vote_by_block_hash(target_header.hash_slow());
let quorum = usize::div_ceil(snap.validators.len() * 2, 3);
if votes.len() >= quorum {
target_header_parent_snap = Some(snap);
break;
}
tracing::debug!(target: "parlia::consensus", "vote count is less than 2/3 of validators, skip assemble vote attestation, number={}, parent ={:?}, vote count={}, validators count={}",
current_header.number(), current_header.parent_hash(), votes.len(), parent_snap.validators.len());
target_header.number(), target_header.hash_slow(), votes.len(), parent_snap.validators.len());
let block_hash = target_header.parent_hash();
if let Some(header) = crate::shared::get_canonical_header_by_hash_from_provider(&block_hash) {
target_header = header;
} else {
return Err(ParliaConsensusError::HeaderNotFound {
block_hash,
});
}
if target_header.number() <= justified_number {
break;
}
}
if target_header_parent_snap.is_none() {
tracing::warn!(target: "parlia::consensus", "cannot collect enough votes, current_block={}, target_header_number={}, justified_number={}",
current_header.number(), target_header.number(), justified_number);
return Ok(());
}

tracing::debug!(target: "parlia::consensus", "assemble vote attestation, number={}, parent ={:?}, vote count={}, validators count={}",
current_header.number(), current_header.parent_hash(), votes.len(), parent_snap.validators.len());
// get justified number and hash from parent snapshot
let (justified_number, justified_hash) = (parent_snap.vote_data.target_number, parent_snap.vote_data.target_hash);
let mut attestation = VoteAttestation::new_with_vote_data(VoteData {
source_number: justified_number,
source_hash: justified_hash,
target_number: parent_header.number,
target_hash: parent_header.hash_slow(),
target_number: target_header.number(),
target_hash: target_header.hash_slow(),
});
// Check vote data from votes
for vote in votes.iter() {
Expand Down
3 changes: 2 additions & 1 deletion src/consensus/parlia/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ pub const SYSTEM_TXS_GAS_SOFT_LIMIT: u64 = 1_000_000; // Maximum gas reserved fo
// Ramanujan HF constants
pub const FIXED_BACKOFF_TIME_BEFORE_FORK_MILLIS: u64 = 200; // 200 ms
pub const WIGGLE_TIME_BEFORE_FORK_MILLIS: u64 = 500; // 500 ms
pub const MILLISECONDS_UNIT: u64 = 250; // 250 ms
pub const MILLISECONDS_UNIT: u64 = 50; // 50 ms
pub const K_ANCESTOR_GENERATION_DEPTH: u64 = 3;

// miner config default values
pub const DEFAULT_MIN_GAS_TIP: u128 = 50_000_000; // 0.05 Gwei
Expand Down
15 changes: 15 additions & 0 deletions src/consensus/parlia/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use alloy_primitives::{BlockHash, BlockNumber};

use crate::consensus::parlia::VoteAddress;


/// Parlia consensus error.
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
pub enum ParliaConsensusError {
Expand Down Expand Up @@ -33,6 +34,20 @@ pub enum ParliaConsensusError {
#[error("invalid header extra")]
InvalidHeaderExtra,

/// Error for invalid header extra
#[error("header not found: {block_hash}")]
HeaderNotFound {
/// The block hash
block_hash: BlockHash,
},

/// Error when the snapshot is not found
#[error("snapshot not found: {block_hash}")]
SnapshotNotFound {
/// The block hash
block_hash: BlockHash,
},

/// Error when the header is not in epoch
#[error("{block_number} is not in epoch")]
NotInEpoch {
Expand Down
2 changes: 1 addition & 1 deletion src/hardforks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ pub trait BscHardforks: EthereumHardforks {
self.bsc_fork_activation(BscHardfork::Maxwell).active_at_timestamp(timestamp)
}

/// Convenience method to check if [`BscHardfork::Maxwell`] is active at a given timestamp.
/// Convenience method to check if [`BscHardfork::Fermi`] is active at a given timestamp.
fn is_fermi_active_at_timestamp(&self, block_number: u64, timestamp: u64) -> bool {
self.is_london_active_at_block(block_number) &&
self.bsc_fork_activation(BscHardfork::Fermi).active_at_timestamp(timestamp)
Expand Down
6 changes: 4 additions & 2 deletions src/node/evm/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ where
self.parlia.clone(),
&parent_snap,
&parent_header,
&mut header
&mut header,
&snapshot_provider,
).map_err(|e| BlockExecutionError::msg(format!("Failed to finalize header: {}", e)))?;

let header_hash = keccak256(alloy_rlp::encode(&header));
Expand Down Expand Up @@ -291,7 +292,8 @@ where
self.parlia.clone(),
&parent_snap,
&parent_header,
&mut header
&mut header,
&snapshot_provider,
).map_err(|e| BlockExecutionError::msg(format!("Failed to finalize header: {}", e)))?;

let header_hash = keccak256(alloy_rlp::encode(&header));
Expand Down
22 changes: 9 additions & 13 deletions src/node/evm/pre_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ use blst::{
BLST_ERROR,
};
use bit_set::BitSet;
use crate::consensus::parlia::constants::K_ANCESTOR_GENERATION_DEPTH;

const BLST_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";

const K_ANCESTOR_GENERATION_DEPTH: u64 = 3;

type ValidatorCache = LruMap<BlockHash, (Vec<Address>, Vec<VoteAddress>), ByLength>;
type TurnLengthCache = LruMap<BlockHash, u8, ByLength>;

Expand Down Expand Up @@ -278,7 +277,12 @@ where
let target_hash = attestation.data.target_hash;
let mut is_match = false;
let mut ancestor = parent.clone();
for _ in 0..self.get_ancestor_generation_depth(header) {
let depth = if self.spec.is_fermi_active_at_timestamp(header.number(), header.timestamp) {
K_ANCESTOR_GENERATION_DEPTH
} else {
1
};
for _ in 0..depth {
if ancestor.number() == target_block && ancestor.hash_slow() == target_hash {
is_match = true;
break;
Expand All @@ -294,8 +298,8 @@ where
if !is_match {
return Err(BscBlockExecutionError::Validation(
BscBlockValidationError::InvalidAttestationTarget {
block_number: GotExpected { got: target_block, expected: parent.number() },
block_hash: GotExpected { got: target_hash, expected: parent.hash_slow() }
block_number: GotExpected { got: target_block, expected: ancestor.number() },
block_hash: GotExpected { got: target_hash, expected: ancestor.hash_slow() }
.into(),
}
).into());
Expand Down Expand Up @@ -386,14 +390,6 @@ where

Ok(())
}

fn get_ancestor_generation_depth(&self, header: &Header) -> u64 {
if self.spec.is_fermi_active_at_timestamp(header.number(),header.timestamp) {
return K_ANCESTOR_GENERATION_DEPTH;
}
1
}


fn verify_seal(
&self,
Expand Down
19 changes: 7 additions & 12 deletions src/node/miner/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use reth::payload::EthPayloadBuilderAttributes;
use crate::hardforks::BscHardforks;
use reth_chainspec::EthChainSpec;
use crate::node::evm::pre_execution::VALIDATOR_CACHE;
use crate::node::miner::signer::{seal_header_with_global_signer, SignerError};
use crate::node::miner::signer::{SignerError, seal_header_with_global_signer};
use crate::node::miner::bsc_miner::MiningContext;
use crate::consensus::parlia::provider::SnapshotProvider;

pub fn prepare_new_attributes(ctx: &mut MiningContext, parlia: Arc<Parlia<BscChainSpec>>, parent_header: &Header, signer: Address) -> EthPayloadBuilderAttributes {
let mut new_header = prepare_new_header(parlia.clone(), parent_header, signer);
Expand Down Expand Up @@ -61,7 +62,9 @@ pub fn finalize_new_header<ChainSpec>(
parlia: Arc<Parlia<ChainSpec>>,
parent_snap: &Snapshot,
parent_header: &Header,
new_header: &mut Header) -> Result<(), crate::node::miner::signer::SignerError>
new_header: &mut Header,
snapshot_provider: &Arc<dyn SnapshotProvider + Send + Sync>,
) -> Result<(), crate::node::miner::signer::SignerError>
where
ChainSpec: EthChainSpec + crate::hardforks::BscHardforks + 'static,
{
Expand Down Expand Up @@ -97,16 +100,8 @@ where
parlia.prepare_turn_length(parent_snap, new_header).
map_err(|e| SignerError::SigningFailed(format!("Failed to prepare turn length: {}", e)))?;

// TODO: add BEP-590 changes in fermi hardfork later, it changes the assemble and verify logic.
if let Err(e) = parlia.assemble_vote_attestation(parent_snap, parent_header, new_header) {
tracing::warn!(
target: "parlia::assemble_vote_attestation",
block_number = new_header.number,
parent_hash = ?new_header.parent_hash,
error = ?e,
"Failed to assemble vote attestation, skipping"
);
}
parlia.assemble_vote_attestation(parent_snap, parent_header, new_header, snapshot_provider).
map_err(|e| SignerError::SigningFailed(format!("Failed to assemble vote attestation: {}", e)))?;

{ // seal header
let mut extra_data = new_header.extra_data.to_vec();
Expand Down
10 changes: 8 additions & 2 deletions src/node/vote_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::hardforks::BscHardforks;
use crate::consensus::parlia::util::calculate_millisecond_timestamp;
use crate::node::evm::util::get_cannonical_header_from_cache;
use crate::node::vote_journal;
use crate::consensus::parlia::constants::K_ANCESTOR_GENERATION_DEPTH;

/// Number of blocks to wait after mining becomes enabled before producing votes.
/// This mirrors geth's VoteManager warm-up (blocksNumberSinceMining = 40) to avoid
Expand Down Expand Up @@ -159,7 +160,12 @@ pub fn maybe_produce_and_broadcast_for_head(

// Too-late-to-vote guard: ensure we have time to broadcast before next block assembly
let cur_ms = calculate_millisecond_timestamp(head);
let vote_assemble_ms = cur_ms.saturating_add(snap.block_interval);
let count = if chain_spec.is_fermi_active_at_timestamp(head.number(), head.timestamp()) {
K_ANCESTOR_GENERATION_DEPTH
} else {
1
};
let vote_assemble_ms = cur_ms.saturating_add(snap.block_interval * count);
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
Expand Down Expand Up @@ -219,4 +225,4 @@ pub fn maybe_produce_and_broadcast_for_head(
tracing::warn!(target: "bsc::vote", error=%e, "Failed to sign vote");
}
}
}
}
Loading