#![forbid(unsafe_code)]
use std::{cmp::max, collections::HashMap, sync::Arc};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use diem_crypto::{
    hash::{TransactionAccumulatorHasher, SPARSE_MERKLE_PLACEHOLDER_HASH},
    HashValue,
};
use diem_types::{
    account_state_blob::AccountStateBlob,
    block_info::PivotBlockDecision,
    contract_event::ContractEvent,
    epoch_state::EpochState,
    ledger_info::LedgerInfoWithSignatures,
    proof::{accumulator::InMemoryAccumulator, AccumulatorExtensionProof},
    term_state::PosState,
    transaction::{
        Transaction, TransactionInfo, TransactionListWithProof,
        TransactionStatus, Version,
    },
    validator_config::ConsensusSignature,
};
pub use error::Error;
use scratchpad::ProofRead;
use storage_interface::TreeState;
pub use self::processed_vm_output::{ProcessedVMOutput, TransactionData};
mod error;
mod processed_vm_output;
type SparseMerkleProof = diem_types::proof::SparseMerkleProof<AccountStateBlob>;
type SparseMerkleTree = scratchpad::SparseMerkleTree<AccountStateBlob>;
pub trait ChunkExecutor: Send {
    fn execute_and_commit_chunk(
        &self,
        txn_list_with_proof: TransactionListWithProof,
        verified_target_li: LedgerInfoWithSignatures,
        epoch_change_li: Option<LedgerInfoWithSignatures>,
    ) -> Result<Vec<ContractEvent>>;
}
pub trait BlockExecutor: Send {
    fn committed_block_id(&self) -> Result<HashValue, Error>;
    fn execute_block(
        &self, block: (HashValue, Vec<Transaction>),
        parent_block_id: HashValue, catch_up_mode: bool,
    ) -> Result<StateComputeResult, Error>;
    fn commit_blocks(
        &self, block_ids: Vec<HashValue>,
        ledger_info_with_sigs: LedgerInfoWithSignatures,
    ) -> Result<(Vec<Transaction>, Vec<ContractEvent>), Error>;
}
pub trait TransactionReplayer: Send {
    fn replay_chunk(
        &self, first_version: Version, txns: Vec<Transaction>,
        txn_infos: Vec<TransactionInfo>,
    ) -> Result<()>;
    fn expecting_version(&self) -> Version;
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct StateComputeResult {
    root_hash: HashValue,
    frozen_subtree_roots: Vec<HashValue>,
    parent_frozen_subtree_roots: Vec<HashValue>,
    num_leaves: u64,
    parent_num_leaves: u64,
    epoch_state: Option<EpochState>,
    compute_status: Vec<TransactionStatus>,
    transaction_info_hashes: Vec<HashValue>,
    signature: Option<ConsensusSignature>,
    pivot_decision: Option<PivotBlockDecision>,
}
impl StateComputeResult {
    pub fn new(
        root_hash: HashValue, frozen_subtree_roots: Vec<HashValue>,
        num_leaves: u64, parent_frozen_subtree_roots: Vec<HashValue>,
        parent_num_leaves: u64, epoch_state: Option<EpochState>,
        compute_status: Vec<TransactionStatus>,
        transaction_info_hashes: Vec<HashValue>,
        pivot_decision: Option<PivotBlockDecision>,
    ) -> Self {
        Self {
            root_hash,
            frozen_subtree_roots,
            num_leaves,
            parent_frozen_subtree_roots,
            parent_num_leaves,
            epoch_state,
            compute_status,
            transaction_info_hashes,
            signature: None,
            pivot_decision,
        }
    }
}
impl StateComputeResult {
    pub fn version(&self) -> Version {
        max(self.num_leaves, 1)
            .checked_sub(1)
            .expect("Integer overflow occurred")
    }
    pub fn root_hash(&self) -> HashValue { self.root_hash }
    pub fn compute_status(&self) -> &Vec<TransactionStatus> {
        &self.compute_status
    }
    pub fn epoch_state(&self) -> &Option<EpochState> { &self.epoch_state }
    pub fn extension_proof(
        &self,
    ) -> AccumulatorExtensionProof<TransactionAccumulatorHasher> {
        AccumulatorExtensionProof::<TransactionAccumulatorHasher>::new(
            self.parent_frozen_subtree_roots.clone(),
            self.parent_num_leaves(),
            self.transaction_info_hashes().clone(),
        )
    }
    pub fn transaction_info_hashes(&self) -> &Vec<HashValue> {
        &self.transaction_info_hashes
    }
    pub fn num_leaves(&self) -> u64 { self.num_leaves }
    pub fn frozen_subtree_roots(&self) -> &Vec<HashValue> {
        &self.frozen_subtree_roots
    }
    pub fn parent_num_leaves(&self) -> u64 { self.parent_num_leaves }
    pub fn parent_frozen_subtree_roots(&self) -> &Vec<HashValue> {
        &self.parent_frozen_subtree_roots
    }
    pub fn pivot_decision(&self) -> &Option<PivotBlockDecision> {
        &self.pivot_decision
    }
    pub fn has_reconfiguration(&self) -> bool { self.epoch_state.is_some() }
    pub fn signature(&self) -> &Option<ConsensusSignature> { &self.signature }
    pub fn set_signature(&mut self, sig: ConsensusSignature) {
        self.signature = Some(sig);
    }
}
#[derive(Clone, Debug)]
pub struct ExecutedTrees {
    state_tree: Arc<SparseMerkleTree>,
    transaction_accumulator:
        Arc<InMemoryAccumulator<TransactionAccumulatorHasher>>,
    pos_state: PosState,
}
impl From<TreeState> for ExecutedTrees {
    fn from(tree_state: TreeState) -> Self {
        ExecutedTrees::new(
            tree_state.account_state_root_hash,
            tree_state.ledger_frozen_subtree_hashes,
            tree_state.num_transactions,
            PosState::new_empty(),
        )
    }
}
impl ExecutedTrees {
    pub fn new_with_pos_state(
        tree_state: TreeState, pos_state: PosState,
    ) -> Self {
        ExecutedTrees::new(
            tree_state.account_state_root_hash,
            tree_state.ledger_frozen_subtree_hashes,
            tree_state.num_transactions,
            pos_state,
        )
    }
    pub fn new_copy(
        state_tree: Arc<SparseMerkleTree>,
        transaction_accumulator: Arc<
            InMemoryAccumulator<TransactionAccumulatorHasher>,
        >,
        pos_state: PosState,
    ) -> Self {
        Self {
            state_tree,
            transaction_accumulator,
            pos_state,
        }
    }
    pub fn state_tree(&self) -> &Arc<SparseMerkleTree> { &self.state_tree }
    pub fn pos_state(&self) -> &PosState { &self.pos_state }
    pub fn txn_accumulator(
        &self,
    ) -> &Arc<InMemoryAccumulator<TransactionAccumulatorHasher>> {
        &self.transaction_accumulator
    }
    pub fn version(&self) -> Option<Version> {
        let num_elements = self.txn_accumulator().num_leaves() as u64;
        num_elements.checked_sub(1)
    }
    pub fn state_id(&self) -> HashValue { self.txn_accumulator().root_hash() }
    pub fn state_root(&self) -> HashValue { self.state_tree().root_hash() }
    pub fn new(
        state_root_hash: HashValue,
        frozen_subtrees_in_accumulator: Vec<HashValue>,
        num_leaves_in_accumulator: u64, pos_state: PosState,
    ) -> ExecutedTrees {
        ExecutedTrees {
            state_tree: Arc::new(SparseMerkleTree::new(state_root_hash)),
            transaction_accumulator: Arc::new(
                InMemoryAccumulator::new(
                    frozen_subtrees_in_accumulator,
                    num_leaves_in_accumulator,
                )
                .expect("The startup info read from storage should be valid."),
            ),
            pos_state,
        }
    }
    pub fn new_empty() -> ExecutedTrees {
        Self::new(
            *SPARSE_MERKLE_PLACEHOLDER_HASH,
            vec![],
            0,
            PosState::new_empty(),
        )
    }
    pub fn set_pos_state_skipped(&mut self, skipped: bool) {
        self.pos_state.set_skipped(skipped)
    }
}
pub struct ProofReader {
    account_to_proof: HashMap<HashValue, SparseMerkleProof>,
}
impl ProofReader {
    pub fn new(
        account_to_proof: HashMap<HashValue, SparseMerkleProof>,
    ) -> Self {
        ProofReader { account_to_proof }
    }
}
impl ProofRead<AccountStateBlob> for ProofReader {
    fn get_proof(&self, key: HashValue) -> Option<&SparseMerkleProof> {
        self.account_to_proof.get(&key)
    }
}