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, collections::HashMap, sync::Arc};
11
12use anyhow::Result;
13use serde::{Deserialize, Serialize};
14
15use diem_crypto::{
16    hash::{TransactionAccumulatorHasher, SPARSE_MERKLE_PLACEHOLDER_HASH},
17    HashValue,
18};
19use diem_types::{
20    account_state_blob::AccountStateBlob,
21    block_info::PivotBlockDecision,
22    contract_event::ContractEvent,
23    epoch_state::EpochState,
24    ledger_info::LedgerInfoWithSignatures,
25    proof::{accumulator::InMemoryAccumulator, AccumulatorExtensionProof},
26    term_state::PosState,
27    transaction::{
28        Transaction, TransactionInfo, TransactionListWithProof,
29        TransactionStatus, Version,
30    },
31    validator_config::ConsensusSignature,
32};
33pub use error::Error;
34use scratchpad::ProofRead;
35use storage_interface::TreeState;
36
37pub use self::processed_vm_output::{ProcessedVMOutput, TransactionData};
38
39mod error;
40mod processed_vm_output;
41
42type SparseMerkleProof = diem_types::proof::SparseMerkleProof<AccountStateBlob>;
43type SparseMerkleTree = scratchpad::SparseMerkleTree<AccountStateBlob>;
44
45pub trait ChunkExecutor: Send {
46    /// Verifies the transactions based on the provided proofs and ledger info.
47    /// If the transactions are valid, executes them and commits immediately
48    /// if execution results match the proofs. Returns a vector of
49    /// reconfiguration events in the chunk
50    fn execute_and_commit_chunk(
51        &self,
52        txn_list_with_proof: TransactionListWithProof,
53        // Target LI that has been verified independently: the proofs are
54        // relative to this version.
55        verified_target_li: LedgerInfoWithSignatures,
56        // An optional end of epoch LedgerInfo. We do not allow chunks that end
57        // epoch without carrying any epoch change LI.
58        epoch_change_li: Option<LedgerInfoWithSignatures>,
59    ) -> Result<Vec<ContractEvent>>;
60}
61
62pub trait BlockExecutor: Send {
63    /// Get the latest committed block id
64    fn committed_block_id(&self) -> Result<HashValue, Error>;
65
66    /// Executes a block.
67    fn execute_block(
68        &self, block: (HashValue, Vec<Transaction>),
69        parent_block_id: HashValue, catch_up_mode: bool,
70    ) -> Result<StateComputeResult, Error>;
71
72    /// Saves eligible blocks to persistent storage.
73    /// If we have multiple blocks and not all of them have signatures, we may
74    /// send them to storage in a few batches. For example, if we have
75    /// ```text
76    /// A <- B <- C <- D <- E
77    /// ```
78    /// and only `C` and `E` have signatures, we will send `A`, `B` and `C` in
79    /// the first batch, then `D` and `E` later in the another batch.
80    /// Commits a block and all its ancestors in a batch manner.
81    ///
82    /// Returns `Ok(Result<Vec<Transaction>, Vec<ContractEvents>)` if
83    /// successful, where `Vec<Transaction>` is a vector of transactions that
84    /// were kept from the submitted blocks, and `Vec<ContractEvents>` is a
85    /// vector of reconfiguration events in the submitted blocks
86    fn commit_blocks(
87        &self, block_ids: Vec<HashValue>,
88        ledger_info_with_sigs: LedgerInfoWithSignatures,
89    ) -> Result<(Vec<Transaction>, Vec<ContractEvent>), Error>;
90}
91
92pub trait TransactionReplayer: Send {
93    fn replay_chunk(
94        &self, first_version: Version, txns: Vec<Transaction>,
95        txn_infos: Vec<TransactionInfo>,
96    ) -> Result<()>;
97
98    fn expecting_version(&self) -> Version;
99}
100
101/// A structure that summarizes the result of the execution needed for consensus
102/// to agree on. The execution is responsible for generating the ID of the new
103/// state, which is returned in the result.
104///
105/// Not every transaction in the payload succeeds: the returned vector keeps the
106/// boolean status of success / failure of the transactions.
107/// Note that the specific details of compute_status are opaque to
108/// StateMachineReplication, which is going to simply pass the results between
109/// StateComputer and TxnManager.
110#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
111pub struct StateComputeResult {
112    /// transaction accumulator root hash is identified as `state_id` in
113    /// Consensus.
114    root_hash: HashValue,
115    /// Represents the roots of all the full subtrees from left to right in
116    /// this accumulator after the execution.
117    frozen_subtree_roots: Vec<HashValue>,
118
119    /// The frozen subtrees roots of the parent block,
120    parent_frozen_subtree_roots: Vec<HashValue>,
121
122    /// The number of leaves of the transaction accumulator after executing a
123    /// proposed block. This state must be persisted to ensure that on
124    /// restart that the version is calculated correctly.
125    num_leaves: u64,
126
127    /// The number of leaves after executing the parent block,
128    parent_num_leaves: u64,
129
130    /// If set, this is the new epoch info that should be changed to if this
131    /// block is committed.
132    epoch_state: Option<EpochState>,
133    /// The compute status (success/failure) of the given payload. The specific
134    /// details are opaque for StateMachineReplication, which is merely
135    /// passing it between StateComputer and TxnManager.
136    compute_status: Vec<TransactionStatus>,
137
138    /// The transaction info hashes of all success txns.
139    transaction_info_hashes: Vec<HashValue>,
140
141    /// The signature of the VoteProposal corresponding to this block.
142    signature: Option<ConsensusSignature>,
143
144    /// Tracks the last pivot selection of a proposed block
145    pivot_decision: Option<PivotBlockDecision>,
146}
147
148impl StateComputeResult {
149    pub fn new(
150        root_hash: HashValue, frozen_subtree_roots: Vec<HashValue>,
151        num_leaves: u64, parent_frozen_subtree_roots: Vec<HashValue>,
152        parent_num_leaves: u64, epoch_state: Option<EpochState>,
153        compute_status: Vec<TransactionStatus>,
154        transaction_info_hashes: Vec<HashValue>,
155        pivot_decision: Option<PivotBlockDecision>,
156    ) -> Self {
157        Self {
158            root_hash,
159            frozen_subtree_roots,
160            num_leaves,
161            parent_frozen_subtree_roots,
162            parent_num_leaves,
163            epoch_state,
164            compute_status,
165            transaction_info_hashes,
166            signature: None,
167            pivot_decision,
168        }
169    }
170}
171
172impl StateComputeResult {
173    pub fn version(&self) -> Version {
174        max(self.num_leaves, 1)
175            .checked_sub(1)
176            .expect("Integer overflow occurred")
177    }
178
179    pub fn root_hash(&self) -> HashValue { self.root_hash }
180
181    pub fn compute_status(&self) -> &Vec<TransactionStatus> {
182        &self.compute_status
183    }
184
185    pub fn epoch_state(&self) -> &Option<EpochState> { &self.epoch_state }
186
187    pub fn extension_proof(
188        &self,
189    ) -> AccumulatorExtensionProof<TransactionAccumulatorHasher> {
190        AccumulatorExtensionProof::<TransactionAccumulatorHasher>::new(
191            self.parent_frozen_subtree_roots.clone(),
192            self.parent_num_leaves(),
193            self.transaction_info_hashes().clone(),
194        )
195    }
196
197    pub fn transaction_info_hashes(&self) -> &Vec<HashValue> {
198        &self.transaction_info_hashes
199    }
200
201    pub fn num_leaves(&self) -> u64 { self.num_leaves }
202
203    pub fn frozen_subtree_roots(&self) -> &Vec<HashValue> {
204        &self.frozen_subtree_roots
205    }
206
207    pub fn parent_num_leaves(&self) -> u64 { self.parent_num_leaves }
208
209    pub fn parent_frozen_subtree_roots(&self) -> &Vec<HashValue> {
210        &self.parent_frozen_subtree_roots
211    }
212
213    pub fn pivot_decision(&self) -> &Option<PivotBlockDecision> {
214        &self.pivot_decision
215    }
216
217    pub fn has_reconfiguration(&self) -> bool { self.epoch_state.is_some() }
218
219    pub fn signature(&self) -> &Option<ConsensusSignature> { &self.signature }
220
221    pub fn set_signature(&mut self, sig: ConsensusSignature) {
222        self.signature = Some(sig);
223    }
224}
225
226/// A wrapper of the in-memory state sparse merkle tree and the transaction
227/// accumulator that represent a specific state collectively. Usually it is a
228/// state after executing a block.
229#[derive(Clone, Debug)]
230pub struct ExecutedTrees {
231    /// The in-memory Sparse Merkle Tree representing a specific state after
232    /// execution. If this tree is presenting the latest committed state, it
233    /// will have a single Subtree node (or Empty node) whose hash equals
234    /// the root hash of the newest Sparse Merkle Tree in storage.
235    state_tree: Arc<SparseMerkleTree>,
236
237    /// The in-memory Merkle Accumulator representing a blockchain state
238    /// consistent with the `state_tree`.
239    transaction_accumulator:
240        Arc<InMemoryAccumulator<TransactionAccumulatorHasher>>,
241
242    pos_state: PosState,
243}
244
245impl From<TreeState> for ExecutedTrees {
246    fn from(tree_state: TreeState) -> Self {
247        ExecutedTrees::new(
248            tree_state.account_state_root_hash,
249            tree_state.ledger_frozen_subtree_hashes,
250            tree_state.num_transactions,
251            // TODO(lpl): Ensure this is not used.
252            PosState::new_empty(),
253        )
254    }
255}
256
257impl ExecutedTrees {
258    pub fn new_with_pos_state(
259        tree_state: TreeState, pos_state: PosState,
260    ) -> Self {
261        ExecutedTrees::new(
262            tree_state.account_state_root_hash,
263            tree_state.ledger_frozen_subtree_hashes,
264            tree_state.num_transactions,
265            pos_state,
266        )
267    }
268
269    pub fn new_copy(
270        state_tree: Arc<SparseMerkleTree>,
271        transaction_accumulator: Arc<
272            InMemoryAccumulator<TransactionAccumulatorHasher>,
273        >,
274        pos_state: PosState,
275    ) -> Self {
276        Self {
277            state_tree,
278            transaction_accumulator,
279            pos_state,
280        }
281    }
282
283    pub fn state_tree(&self) -> &Arc<SparseMerkleTree> { &self.state_tree }
284
285    pub fn pos_state(&self) -> &PosState { &self.pos_state }
286
287    pub fn txn_accumulator(
288        &self,
289    ) -> &Arc<InMemoryAccumulator<TransactionAccumulatorHasher>> {
290        &self.transaction_accumulator
291    }
292
293    pub fn version(&self) -> Option<Version> {
294        let num_elements = self.txn_accumulator().num_leaves() as u64;
295        num_elements.checked_sub(1)
296    }
297
298    pub fn state_id(&self) -> HashValue { self.txn_accumulator().root_hash() }
299
300    pub fn state_root(&self) -> HashValue { self.state_tree().root_hash() }
301
302    pub fn new(
303        state_root_hash: HashValue,
304        frozen_subtrees_in_accumulator: Vec<HashValue>,
305        num_leaves_in_accumulator: u64, pos_state: PosState,
306    ) -> ExecutedTrees {
307        ExecutedTrees {
308            state_tree: Arc::new(SparseMerkleTree::new(state_root_hash)),
309            transaction_accumulator: Arc::new(
310                InMemoryAccumulator::new(
311                    frozen_subtrees_in_accumulator,
312                    num_leaves_in_accumulator,
313                )
314                .expect("The startup info read from storage should be valid."),
315            ),
316            pos_state,
317        }
318    }
319
320    pub fn new_empty() -> ExecutedTrees {
321        Self::new(
322            *SPARSE_MERKLE_PLACEHOLDER_HASH,
323            vec![],
324            0,
325            PosState::new_empty(),
326        )
327    }
328
329    pub fn set_pos_state_skipped(&mut self, skipped: bool) {
330        self.pos_state.set_skipped(skipped)
331    }
332}
333
334pub struct ProofReader {
335    account_to_proof: HashMap<HashValue, SparseMerkleProof>,
336}
337
338impl ProofReader {
339    pub fn new(
340        account_to_proof: HashMap<HashValue, SparseMerkleProof>,
341    ) -> Self {
342        ProofReader { account_to_proof }
343    }
344}
345
346impl ProofRead<AccountStateBlob> for ProofReader {
347    fn get_proof(&self, key: HashValue) -> Option<&SparseMerkleProof> {
348        self.account_to_proof.get(&key)
349    }
350}