casper-node 2.0.3

The Casper blockchain node
Documentation
use std::fmt::{self, Display, Formatter};
use tracing::{debug, warn};

use casper_types::{Block, BlockHash, DeployHash, Digest, EraId, PublicKey, TransactionId};

use crate::{
    components::block_synchronizer::{
        need_next::NeedNext, peer_list::PeerList, signature_acquisition::SignatureAcquisition,
        BlockAcquisitionError, ExecutionResultsAcquisition, ExecutionResultsChecksum,
    },
    types::{BlockExecutionResultsOrChunkId, EraValidatorWeights, ExecutableBlock, NodeId},
    NodeRng,
};

use super::block_acquisition::signatures_from_missing_validators;

#[derive(Debug, PartialEq)]
pub(crate) struct BlockAcquisitionAction {
    peers_to_ask: Vec<NodeId>,
    need_next: NeedNext,
}

impl Display for BlockAcquisitionAction {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{} from {} peers",
            self.need_next,
            self.peers_to_ask.len()
        )
    }
}

impl BlockAcquisitionAction {
    pub(super) fn need_next(&self) -> NeedNext {
        self.need_next.clone()
    }

    pub(super) fn peers_to_ask(&self) -> Vec<NodeId> {
        self.peers_to_ask.to_vec()
    }

    pub(super) fn need_nothing(block_hash: BlockHash) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::Nothing(block_hash),
        }
    }

    pub(super) fn peers(block_hash: BlockHash) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::Peers(block_hash),
        }
    }

    pub(super) fn execution_results_checksum(
        block_hash: BlockHash,
        global_state_root_hash: Digest,
    ) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::ExecutionResultsChecksum(block_hash, global_state_root_hash),
        }
    }

    pub(super) fn execution_results(
        block_hash: BlockHash,
        peer_list: &PeerList,
        rng: &mut NodeRng,
        next: BlockExecutionResultsOrChunkId,
        checksum: ExecutionResultsChecksum,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::ExecutionResults(block_hash, next, checksum),
        }
    }

    pub(super) fn approvals_hashes(block: &Block, peer_list: &PeerList, rng: &mut NodeRng) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::ApprovalsHashes(*block.hash(), Box::new(block.clone())),
        }
    }

    pub(super) fn legacy_deploy_by_hash(
        block_hash: BlockHash,
        deploy_hash: DeployHash,
        peer_list: &PeerList,
        rng: &mut NodeRng,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::DeployByHash(block_hash, deploy_hash),
        }
    }

    pub(super) fn transaction_by_id(
        block_hash: BlockHash,
        txn_id: TransactionId,
        peer_list: &PeerList,
        rng: &mut NodeRng,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::TransactionById(block_hash, txn_id),
        }
    }

    pub(super) fn global_state(
        peer_list: &PeerList,
        rng: &mut NodeRng,
        block_hash: BlockHash,
        root_hash: Digest,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::GlobalState(block_hash, root_hash),
        }
    }

    pub(super) fn finality_signatures(
        peer_list: &PeerList,
        rng: &mut NodeRng,
        era_id: EraId,
        block_hash: BlockHash,
        missing_signatures: Vec<PublicKey>,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);

        debug!(
            %era_id,
            missing_signatures = missing_signatures.len(),
            peers_to_ask = peers_to_ask.len(),
            "BlockSynchronizer: requesting finality signatures");

        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::FinalitySignatures(block_hash, era_id, missing_signatures),
        }
    }

    pub(super) fn block_body(
        peer_list: &PeerList,
        rng: &mut NodeRng,
        block_hash: BlockHash,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::BlockBody(block_hash),
        }
    }

    pub(super) fn switch_to_have_sufficient_finality(
        block_hash: BlockHash,
        block_height: u64,
    ) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::SwitchToHaveStrictFinality(block_hash, block_height),
        }
    }

    pub(super) fn block_marked_complete(block_hash: BlockHash, block_height: u64) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::BlockMarkedComplete(block_hash, block_height),
        }
    }

    pub(super) fn make_executable_block(block_hash: BlockHash, block_height: u64) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::MakeExecutableBlock(block_hash, block_height),
        }
    }

    pub(super) fn enqueue_block_for_execution(
        block_hash: &BlockHash,
        executable_block: Box<ExecutableBlock>,
    ) -> Self {
        BlockAcquisitionAction {
            peers_to_ask: vec![],
            need_next: NeedNext::EnqueueForExecution(
                *block_hash,
                executable_block.height,
                executable_block,
            ),
        }
    }

    pub(super) fn block_header(
        peer_list: &PeerList,
        rng: &mut NodeRng,
        block_hash: BlockHash,
    ) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::BlockHeader(block_hash),
        }
    }

    pub(super) fn era_validators(peer_list: &PeerList, rng: &mut NodeRng, era_id: EraId) -> Self {
        let peers_to_ask = peer_list.qualified_peers(rng);
        BlockAcquisitionAction {
            peers_to_ask,
            need_next: NeedNext::EraValidators(era_id),
        }
    }

    pub(super) fn maybe_execution_results(
        block: &Block,
        peer_list: &PeerList,
        rng: &mut NodeRng,
        exec_results: &mut ExecutionResultsAcquisition,
    ) -> Result<Self, BlockAcquisitionError> {
        match exec_results {
            ExecutionResultsAcquisition::Needed { .. } => {
                Ok(BlockAcquisitionAction::execution_results_checksum(
                    *block.hash(),
                    *block.state_root_hash(),
                ))
            }
            acq @ ExecutionResultsAcquisition::Pending { .. }
            | acq @ ExecutionResultsAcquisition::Acquiring { .. } => {
                match acq.needs_value_or_chunk() {
                    None => {
                        warn!(
                            block_hash = %block.hash(),
                            "execution_results_acquisition.needs_value_or_chunk() should never be \
                            None for these variants"
                        );
                        Err(BlockAcquisitionError::InvalidAttemptToAcquireExecutionResults)
                    }
                    Some((next, checksum)) => Ok(BlockAcquisitionAction::execution_results(
                        *block.hash(),
                        peer_list,
                        rng,
                        next,
                        checksum,
                    )),
                }
            }
            ExecutionResultsAcquisition::Complete { .. } => Ok(
                BlockAcquisitionAction::approvals_hashes(block, peer_list, rng),
            ),
        }
    }

    #[allow(clippy::too_many_arguments)]
    pub(super) fn next_action_after_deploy_acquisition(
        block_hash: BlockHash,
        block_height: u64,
        era_id: EraId,
        peer_list: &PeerList,
        rng: &mut NodeRng,
        validator_weights: &EraValidatorWeights,
        signatures: &mut SignatureAcquisition,
        is_historical: bool,
        max_simultaneous_peers: u8,
    ) -> Self {
        if signatures.has_sufficient_finality(is_historical, true) {
            BlockAcquisitionAction::switch_to_have_sufficient_finality(block_hash, block_height)
        } else {
            signatures_from_missing_validators(
                validator_weights,
                signatures,
                max_simultaneous_peers,
                peer_list,
                rng,
                era_id,
                block_hash,
            )
        }
    }
}