executor_types/
lib.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8#![forbid(unsafe_code)]
9
10use std::{cmp::max, sync::Arc};
11
12use anyhow::Result;
13use serde::{Deserialize, Serialize};
14
15use diem_crypto::{hash::TransactionAccumulatorHasher, HashValue};
16use diem_types::{
17    block_info::PivotBlockDecision,
18    epoch_state::EpochState,
19    ledger_info::LedgerInfoWithSignatures,
20    proof::{accumulator::InMemoryAccumulator, AccumulatorExtensionProof},
21    term_state::PosState,
22    transaction::{Transaction, TransactionStatus, Version},
23    validator_config::ConsensusSignature,
24};
25pub use error::Error;
26use storage_interface::TreeState;
27
28pub use self::processed_vm_output::{ProcessedVMOutput, TransactionData};
29
30mod error;
31mod processed_vm_output;
32
33pub trait BlockExecutor: Send {
34    /// Get the latest committed block id
35    fn committed_block_id(&self) -> Result<HashValue, Error>;
36
37    /// Executes a block.
38    fn execute_block(
39        &self, block: (HashValue, Vec<Transaction>),
40        parent_block_id: HashValue, catch_up_mode: bool,
41    ) -> Result<StateComputeResult, Error>;
42
43    /// Saves eligible blocks to persistent storage.
44    /// If we have multiple blocks and not all of them have signatures, we may
45    /// send them to storage in a few batches. For example, if we have
46    /// ```text
47    /// A <- B <- C <- D <- E
48    /// ```
49    /// and only `C` and `E` have signatures, we will send `A`, `B` and `C` in
50    /// the first batch, then `D` and `E` later in the another batch.
51    /// Commits a block and all its ancestors in a batch manner. Returns
52    /// the committed transactions so the caller can notify mempool.
53    fn commit_blocks(
54        &self, block_ids: Vec<HashValue>,
55        ledger_info_with_sigs: LedgerInfoWithSignatures,
56    ) -> Result<Vec<Transaction>, Error>;
57}
58
59/// A structure that summarizes the result of the execution needed for consensus
60/// to agree on. The execution is responsible for generating the ID of the new
61/// state, which is returned in the result.
62///
63/// Not every transaction in the payload succeeds: the returned vector keeps the
64/// boolean status of success / failure of the transactions.
65/// Note that the specific details of compute_status are opaque to
66/// StateMachineReplication, which is going to simply pass the results between
67/// StateComputer and TxnManager.
68#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
69pub struct StateComputeResult {
70    /// transaction accumulator root hash is identified as `state_id` in
71    /// Consensus.
72    root_hash: HashValue,
73    /// Represents the roots of all the full subtrees from left to right in
74    /// this accumulator after the execution.
75    frozen_subtree_roots: Vec<HashValue>,
76
77    /// The frozen subtrees roots of the parent block,
78    parent_frozen_subtree_roots: Vec<HashValue>,
79
80    /// The number of leaves of the transaction accumulator after executing a
81    /// proposed block. This state must be persisted to ensure that on
82    /// restart that the version is calculated correctly.
83    num_leaves: u64,
84
85    /// The number of leaves after executing the parent block,
86    parent_num_leaves: u64,
87
88    /// If set, this is the new epoch info that should be changed to if this
89    /// block is committed.
90    epoch_state: Option<EpochState>,
91    /// The compute status (success/failure) of the given payload. The specific
92    /// details are opaque for StateMachineReplication, which is merely
93    /// passing it between StateComputer and TxnManager.
94    compute_status: Vec<TransactionStatus>,
95
96    /// The transaction info hashes of all success txns.
97    transaction_info_hashes: Vec<HashValue>,
98
99    /// The signature of the VoteProposal corresponding to this block.
100    signature: Option<ConsensusSignature>,
101
102    /// Tracks the last pivot selection of a proposed block
103    pivot_decision: Option<PivotBlockDecision>,
104}
105
106impl StateComputeResult {
107    pub fn new(
108        root_hash: HashValue, frozen_subtree_roots: Vec<HashValue>,
109        num_leaves: u64, parent_frozen_subtree_roots: Vec<HashValue>,
110        parent_num_leaves: u64, epoch_state: Option<EpochState>,
111        compute_status: Vec<TransactionStatus>,
112        transaction_info_hashes: Vec<HashValue>,
113        pivot_decision: Option<PivotBlockDecision>,
114    ) -> Self {
115        Self {
116            root_hash,
117            frozen_subtree_roots,
118            num_leaves,
119            parent_frozen_subtree_roots,
120            parent_num_leaves,
121            epoch_state,
122            compute_status,
123            transaction_info_hashes,
124            signature: None,
125            pivot_decision,
126        }
127    }
128}
129
130impl StateComputeResult {
131    pub fn version(&self) -> Version {
132        max(self.num_leaves, 1)
133            .checked_sub(1)
134            .expect("Integer overflow occurred")
135    }
136
137    pub fn root_hash(&self) -> HashValue { self.root_hash }
138
139    pub fn compute_status(&self) -> &Vec<TransactionStatus> {
140        &self.compute_status
141    }
142
143    pub fn epoch_state(&self) -> &Option<EpochState> { &self.epoch_state }
144
145    pub fn extension_proof(
146        &self,
147    ) -> AccumulatorExtensionProof<TransactionAccumulatorHasher> {
148        AccumulatorExtensionProof::<TransactionAccumulatorHasher>::new(
149            self.parent_frozen_subtree_roots.clone(),
150            self.parent_num_leaves(),
151            self.transaction_info_hashes().clone(),
152        )
153    }
154
155    pub fn transaction_info_hashes(&self) -> &Vec<HashValue> {
156        &self.transaction_info_hashes
157    }
158
159    pub fn num_leaves(&self) -> u64 { self.num_leaves }
160
161    pub fn frozen_subtree_roots(&self) -> &Vec<HashValue> {
162        &self.frozen_subtree_roots
163    }
164
165    pub fn parent_num_leaves(&self) -> u64 { self.parent_num_leaves }
166
167    pub fn parent_frozen_subtree_roots(&self) -> &Vec<HashValue> {
168        &self.parent_frozen_subtree_roots
169    }
170
171    pub fn pivot_decision(&self) -> &Option<PivotBlockDecision> {
172        &self.pivot_decision
173    }
174
175    pub fn has_reconfiguration(&self) -> bool { self.epoch_state.is_some() }
176
177    pub fn signature(&self) -> &Option<ConsensusSignature> { &self.signature }
178
179    pub fn set_signature(&mut self, sig: ConsensusSignature) {
180        self.signature = Some(sig);
181    }
182}
183
184/// A wrapper of the transaction accumulator and PoS state that represent a
185/// specific blockchain state collectively. Usually it is a state after
186/// executing a block.
187#[derive(Clone, Debug)]
188pub struct ExecutedTrees {
189    /// The in-memory Merkle Accumulator representing the blockchain state.
190    transaction_accumulator:
191        Arc<InMemoryAccumulator<TransactionAccumulatorHasher>>,
192
193    pos_state: PosState,
194}
195
196impl From<TreeState> for ExecutedTrees {
197    fn from(tree_state: TreeState) -> Self {
198        ExecutedTrees::new(
199            tree_state.ledger_frozen_subtree_hashes,
200            tree_state.num_transactions,
201            PosState::new_empty(),
202        )
203    }
204}
205
206impl ExecutedTrees {
207    pub fn new_with_pos_state(
208        tree_state: TreeState, pos_state: PosState,
209    ) -> Self {
210        ExecutedTrees::new(
211            tree_state.ledger_frozen_subtree_hashes,
212            tree_state.num_transactions,
213            pos_state,
214        )
215    }
216
217    pub fn new_copy(
218        transaction_accumulator: Arc<
219            InMemoryAccumulator<TransactionAccumulatorHasher>,
220        >,
221        pos_state: PosState,
222    ) -> Self {
223        Self {
224            transaction_accumulator,
225            pos_state,
226        }
227    }
228
229    pub fn pos_state(&self) -> &PosState { &self.pos_state }
230
231    pub fn txn_accumulator(
232        &self,
233    ) -> &Arc<InMemoryAccumulator<TransactionAccumulatorHasher>> {
234        &self.transaction_accumulator
235    }
236
237    pub fn version(&self) -> Option<Version> {
238        let num_elements = self.txn_accumulator().num_leaves() as u64;
239        num_elements.checked_sub(1)
240    }
241
242    pub fn state_id(&self) -> HashValue { self.txn_accumulator().root_hash() }
243
244    pub fn new(
245        frozen_subtrees_in_accumulator: Vec<HashValue>,
246        num_leaves_in_accumulator: u64, pos_state: PosState,
247    ) -> ExecutedTrees {
248        ExecutedTrees {
249            transaction_accumulator: Arc::new(
250                InMemoryAccumulator::new(
251                    frozen_subtrees_in_accumulator,
252                    num_leaves_in_accumulator,
253                )
254                .expect("The startup info read from storage should be valid."),
255            ),
256            pos_state,
257        }
258    }
259
260    pub fn new_empty() -> ExecutedTrees {
261        Self::new(vec![], 0, PosState::new_empty())
262    }
263
264    pub fn set_pos_state_skipped(&mut self, skipped: bool) {
265        self.pos_state.set_skipped(skipped)
266    }
267}