#![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)
}
}