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