Skip to content

Commit

Permalink
refactor: remove approval verification functions from epoch manager (n…
Browse files Browse the repository at this point in the history
…ear#12417)

The approval verification functions are not needed in the epoch manager
and each one is only used once.

The MR moves them to a separate file and exposes the
`get_heuristic_block_approvers_ordered` function needed by the
validation functions.
  • Loading branch information
stedfn authored Nov 13, 2024
1 parent 72c0aa9 commit 8d180da
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 215 deletions.
96 changes: 96 additions & 0 deletions chain/chain/src/approval_verification.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use near_chain_primitives::error::Error;
use near_crypto::Signature;
use near_primitives::{
block_header::{Approval, ApprovalInner},
epoch_info::EpochInfo,
errors::EpochError,
hash::CryptoHash,
types::{AccountId, ApprovalStake, Balance, BlockHeight},
};
use std::{collections::HashSet, sync::Arc};

pub fn verify_approval_with_approvers_info(
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<near_crypto::Signature>>],
info: Vec<(ApprovalStake, bool)>,
) -> Result<bool, Error> {
if approvals.len() > info.len() {
return Ok(false);
}

let message_to_sign = Approval::get_data_for_sig(
&if prev_block_height + 1 == block_height {
ApprovalInner::Endorsement(*prev_block_hash)
} else {
ApprovalInner::Skip(prev_block_height)
},
block_height,
);

for ((validator, is_slashed), may_be_signature) in info.into_iter().zip(approvals.iter()) {
if let Some(signature) = may_be_signature {
if is_slashed || !signature.verify(message_to_sign.as_ref(), &validator.public_key) {
return Ok(false);
}
}
}
Ok(true)
}

/// Verify approvals and check threshold, but ignore next epoch approvals and slashing
pub fn verify_approvals_and_threshold_orphan(
can_approved_block_be_produced: &dyn Fn(
&[Option<Box<Signature>>],
&[(Balance, Balance, bool)],
) -> bool,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
epoch_info: Arc<EpochInfo>,
) -> Result<(), Error> {
let block_approvers = get_heuristic_block_approvers_ordered(&epoch_info)?;
let message_to_sign = Approval::get_data_for_sig(
&if prev_block_height + 1 == block_height {
ApprovalInner::Endorsement(*prev_block_hash)
} else {
ApprovalInner::Skip(prev_block_height)
},
block_height,
);

for (validator, may_be_signature) in block_approvers.iter().zip(approvals.iter()) {
if let Some(signature) = may_be_signature {
if !signature.verify(message_to_sign.as_ref(), &validator.public_key) {
return Err(Error::InvalidApprovals);
}
}
}
let stakes = block_approvers
.iter()
.map(|stake| (stake.stake_this_epoch, stake.stake_next_epoch, false))
.collect::<Vec<_>>();
if !can_approved_block_be_produced(approvals, &stakes) {
Err(Error::NotEnoughApprovals)
} else {
Ok(())
}
}

fn get_heuristic_block_approvers_ordered(
epoch_info: &EpochInfo,
) -> Result<Vec<ApprovalStake>, EpochError> {
let mut result = vec![];
let mut validators: HashSet<AccountId> = HashSet::new();
for validator_id in epoch_info.block_producers_settlement().into_iter() {
let validator_stake = epoch_info.get_validator(*validator_id);
let account_id = validator_stake.account_id();
if validators.insert(account_id.clone()) {
result.push(validator_stake.get_approval_stake(false));
}
}

Ok(result)
}
5 changes: 4 additions & 1 deletion chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::approval_verification::verify_approval_with_approvers_info;
use crate::block_processing_utils::{
ApplyChunksDoneWaiter, ApplyChunksStillApplying, BlockPreprocessInfo, BlockProcessingArtifact,
BlocksInProcessing,
Expand Down Expand Up @@ -1061,11 +1062,13 @@ impl Chain {
// producer, confirmation signatures and finality info.
if *provenance != Provenance::PRODUCED {
// first verify aggregated signature
if !self.epoch_manager.verify_approval(
let info = self.epoch_manager.get_epoch_block_approvers_ordered(prev_header.hash())?;
if !verify_approval_with_approvers_info(
prev_header.hash(),
prev_header.height(),
header.height(),
header.approvals(),
info,
)? {
return Err(Error::InvalidApprovals);
};
Expand Down
7 changes: 4 additions & 3 deletions chain/chain/src/chain_update.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::approval_verification::verify_approvals_and_threshold_orphan;
use crate::block_processing_utils::BlockPreprocessInfo;
use crate::chain::collect_receipts_from_response;
use crate::metrics::{SHARD_LAYOUT_NUM_SHARDS, SHARD_LAYOUT_VERSION};
use crate::store::{ChainStore, ChainStoreAccess, ChainStoreUpdate};

use crate::types::{
ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, RuntimeAdapter,
RuntimeStorageConfig,
Expand Down Expand Up @@ -346,8 +346,8 @@ impl<'a> ChainUpdate<'a> {
let height = header.height();
let epoch_id = header.epoch_id();
let approvals = header.approvals();
self.epoch_manager.verify_approvals_and_threshold_orphan(
epoch_id,
let epoch_info = self.epoch_manager.get_epoch_info(epoch_id)?;
verify_approvals_and_threshold_orphan(
&|approvals, stakes| {
Doomslug::can_approved_block_be_produced(
self.doomslug_threshold_mode,
Expand All @@ -359,6 +359,7 @@ impl<'a> ChainUpdate<'a> {
prev_height,
height,
approvals,
epoch_info,
)
}

Expand Down
1 change: 1 addition & 0 deletions chain/chain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub use store::{
pub use store_validator::{ErrorMessage, StoreValidator};
pub use types::{Block, BlockHeader, BlockStatus, ChainGenesis, LatestKnown, Provenance};

mod approval_verification;
mod block_processing_utils;
pub mod blocks_delay_tracker;
pub mod chain;
Expand Down
59 changes: 0 additions & 59 deletions chain/chain/src/test_utils/kv_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use near_primitives::account::{AccessKey, Account};
use near_primitives::apply::ApplyChunkReason;
use near_primitives::bandwidth_scheduler::BandwidthRequests;
use near_primitives::block::Tip;
use near_primitives::block_header::{Approval, ApprovalInner};
use near_primitives::congestion_info::{CongestionInfo, ExtendedCongestionInfo};
use near_primitives::epoch_block_info::BlockInfo;
use near_primitives::epoch_info::EpochInfo;
Expand Down Expand Up @@ -936,64 +935,6 @@ impl EpochManagerAdapter for MockEpochManager {
Ok(true)
}

fn verify_approval(
&self,
_prev_block_hash: &CryptoHash,
_prev_block_height: BlockHeight,
_block_height: BlockHeight,
_approvals: &[Option<Box<Signature>>],
) -> Result<bool, Error> {
Ok(true)
}

fn verify_approval_with_approvers_info(
&self,
_prev_block_hash: &CryptoHash,
_prev_block_height: BlockHeight,
_block_height: BlockHeight,
_approvals: &[Option<Box<Signature>>],
_info: Vec<(ApprovalStake, bool)>,
) -> Result<bool, Error> {
Ok(true)
}

fn verify_approvals_and_threshold_orphan(
&self,
epoch_id: &EpochId,
can_approved_block_be_produced: &dyn Fn(
&[Option<Box<Signature>>],
&[(Balance, Balance, bool)],
) -> bool,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
) -> Result<(), Error> {
let validators = self.get_block_producers(self.get_valset_for_epoch(epoch_id)?);
let message_to_sign = Approval::get_data_for_sig(
&if prev_block_height + 1 == block_height {
ApprovalInner::Endorsement(*prev_block_hash)
} else {
ApprovalInner::Skip(prev_block_height)
},
block_height,
);

for (validator, may_be_signature) in validators.iter().zip(approvals.iter()) {
if let Some(signature) = may_be_signature {
if !signature.verify(message_to_sign.as_ref(), validator.public_key()) {
return Err(Error::InvalidApprovals);
}
}
}
let stakes = validators.iter().map(|stake| (stake.stake(), 0, false)).collect::<Vec<_>>();
if !can_approved_block_be_produced(approvals, &stakes) {
Err(Error::NotEnoughApprovals)
} else {
Ok(())
}
}

fn verify_chunk_endorsement_signature(
&self,
_endorsement: &ChunkEndorsement,
Expand Down
132 changes: 1 addition & 131 deletions chain/epoch-manager/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::EpochManagerHandle;
use near_chain_primitives::Error;
use near_crypto::Signature;
use near_primitives::block::Tip;
use near_primitives::block_header::{Approval, ApprovalInner, BlockHeader};
use near_primitives::block_header::BlockHeader;
use near_primitives::epoch_block_info::BlockInfo;
use near_primitives::epoch_info::EpochInfo;
use near_primitives::epoch_manager::{EpochConfig, ShardConfig};
Expand Down Expand Up @@ -391,40 +391,6 @@ pub trait EpochManagerAdapter: Send + Sync {
shard_id: ShardId,
) -> Result<bool, Error>;

/// Verify aggregated bls signature
fn verify_approval(
&self,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
) -> Result<bool, Error>;

/// Verify aggregated bls signature given block approvers info
fn verify_approval_with_approvers_info(
&self,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
info: Vec<(ApprovalStake, bool)>,
) -> Result<bool, Error>;

/// Verify approvals and check threshold, but ignore next epoch approvals and slashing
fn verify_approvals_and_threshold_orphan(
&self,
epoch_id: &EpochId,
can_approved_block_be_produced: &dyn Fn(
&[Option<Box<Signature>>],
// (stake this in epoch, stake in next epoch, is_slashed)
&[(Balance, Balance, bool)],
) -> bool,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
) -> Result<(), Error>;

fn verify_chunk_endorsement_signature(
&self,
endorsement: &ChunkEndorsement,
Expand Down Expand Up @@ -967,102 +933,6 @@ impl EpochManagerAdapter for EpochManagerHandle {
Ok(signature.verify(chunk_hash.as_ref(), chunk_producer.public_key()))
}

fn verify_approval(
&self,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
) -> Result<bool, Error> {
let info = {
let epoch_manager = self.read();
epoch_manager.get_all_block_approvers_ordered(prev_block_hash)?
};
self.verify_approval_with_approvers_info(
prev_block_hash,
prev_block_height,
block_height,
approvals,
info,
)
}

fn verify_approval_with_approvers_info(
&self,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
info: Vec<(ApprovalStake, bool)>,
) -> Result<bool, Error> {
if approvals.len() > info.len() {
return Ok(false);
}

let message_to_sign = Approval::get_data_for_sig(
&if prev_block_height + 1 == block_height {
ApprovalInner::Endorsement(*prev_block_hash)
} else {
ApprovalInner::Skip(prev_block_height)
},
block_height,
);

for ((validator, is_slashed), may_be_signature) in info.into_iter().zip(approvals.iter()) {
if let Some(signature) = may_be_signature {
if is_slashed || !signature.verify(message_to_sign.as_ref(), &validator.public_key)
{
return Ok(false);
}
}
}
Ok(true)
}

fn verify_approvals_and_threshold_orphan(
&self,
epoch_id: &EpochId,
can_approved_block_be_produced: &dyn Fn(
&[Option<Box<Signature>>],
&[(Balance, Balance, bool)],
) -> bool,
prev_block_hash: &CryptoHash,
prev_block_height: BlockHeight,
block_height: BlockHeight,
approvals: &[Option<Box<Signature>>],
) -> Result<(), Error> {
let info = {
let epoch_manager = self.read();
epoch_manager.get_heuristic_block_approvers_ordered(epoch_id)?
};

let message_to_sign = Approval::get_data_for_sig(
&if prev_block_height + 1 == block_height {
ApprovalInner::Endorsement(*prev_block_hash)
} else {
ApprovalInner::Skip(prev_block_height)
},
block_height,
);

for (validator, may_be_signature) in info.iter().zip(approvals.iter()) {
if let Some(signature) = may_be_signature {
if !signature.verify(message_to_sign.as_ref(), &validator.public_key) {
return Err(Error::InvalidApprovals);
}
}
}
let stakes = info
.iter()
.map(|stake| (stake.stake_this_epoch, stake.stake_next_epoch, false))
.collect::<Vec<_>>();
if !can_approved_block_be_produced(approvals, &stakes) {
Err(Error::NotEnoughApprovals)
} else {
Ok(())
}
}

fn verify_chunk_endorsement_signature(
&self,
endorsement: &ChunkEndorsement,
Expand Down
Loading

0 comments on commit 8d180da

Please sign in to comment.