1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! Block-related utility module
use super::{
execution::ExecState, transaction::Transaction, CopyEvent, ExecStep, ExpEvent, PrecompileEvent,
PrecompileEvents, Withdrawal,
};
use crate::{
operation::{OperationContainer, RWCounter},
Error,
};
use eth_types::{evm_unimplemented, Address, Word, H256};
use itertools::Itertools;
use std::collections::HashMap;
/// Context of a [`Block`] which can mutate in a [`Transaction`].
#[derive(Debug, Clone)]
pub struct BlockContext {
/// Used to track the global counter in every operation in the block.
/// Contains the next available value.
pub(crate) rwc: RWCounter,
/// Map call_id to (tx_index, call_index) (where tx_index is the index used
/// in Block.txs and call_index is the index used in Transaction.
/// calls).
pub(crate) call_map: HashMap<usize, (usize, usize)>,
/// Total gas used by previous transactions in this block.
pub(crate) cumulative_gas_used: u64,
}
impl Default for BlockContext {
fn default() -> Self {
Self::new()
}
}
impl BlockContext {
/// Create a new Self
pub fn new() -> Self {
Self {
rwc: RWCounter::new(),
call_map: HashMap::new(),
cumulative_gas_used: 0,
}
}
}
// TODO: Remove fields that are duplicated in`eth_block`
/// Circuit Input related to a block.
#[derive(Debug, Clone)]
pub struct Block {
/// chain id
pub chain_id: Word,
/// history hashes contains most recent 256 block hashes in history, where
/// the latest one is at history_hashes[history_hashes.len() - 1].
pub history_hashes: Vec<Word>,
/// coinbase
pub coinbase: Address,
/// gas limit
pub gas_limit: u64,
/// number
pub number: Word,
/// time
pub timestamp: Word,
/// difficulty
pub difficulty: Word,
/// base fee
pub base_fee: Word,
/// State root of the previous block
pub prev_state_root: Word,
/// Container of operations done in this block.
pub container: OperationContainer,
/// Transactions contained in the block
pub txs: Vec<Transaction>,
/// End block step
pub end_block: ExecStep,
// /// Chunk context
// pub chunk_context: ChunkContext,
/// Copy events in this block.
pub copy_events: Vec<CopyEvent>,
/// Inputs to the SHA3 opcode as well as data hashed during the EVM execution like init code
/// and address creation inputs.
pub sha3_inputs: Vec<Vec<u8>>,
/// Exponentiation events in the block.
pub exp_events: Vec<ExpEvent>,
/// IO to/from the precompiled contract calls.
pub precompile_events: PrecompileEvents,
/// Original block from geth
pub eth_block: eth_types::Block<eth_types::Transaction>,
}
impl Block {
/// Create a new block.
pub fn new(
chain_id: Word,
history_hashes: Vec<Word>,
prev_state_root: Word,
eth_block: ð_types::Block<eth_types::Transaction>,
) -> Result<Self, Error> {
if eth_block.base_fee_per_gas.is_none() {
// FIXME: resolve this once we have proper EIP-1559 support
evm_unimplemented!(
"This does not look like a EIP-1559 block - base_fee_per_gas defaults to zero"
);
}
Ok(Self {
chain_id,
history_hashes,
coinbase: eth_block
.author
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?,
gas_limit: eth_block.gas_limit.low_u64(),
number: eth_block
.number
.ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?
.low_u64()
.into(),
timestamp: eth_block.timestamp,
difficulty: if eth_block.difficulty.is_zero() {
eth_block
.mix_hash
.unwrap_or_default()
.to_fixed_bytes()
.into()
} else {
eth_block.difficulty
},
base_fee: eth_block.base_fee_per_gas.unwrap_or_default(),
prev_state_root,
container: OperationContainer::new(),
txs: Vec::new(),
end_block: ExecStep {
exec_state: ExecState::EndBlock,
..ExecStep::default()
},
copy_events: Vec::new(),
exp_events: Vec::new(),
sha3_inputs: Vec::new(),
precompile_events: PrecompileEvents { events: Vec::new() },
eth_block: eth_block.clone(),
})
}
/// Return the list of transactions of this block.
pub fn txs(&self) -> &[Transaction] {
&self.txs
}
#[cfg(test)]
pub fn txs_mut(&mut self) -> &mut Vec<Transaction> {
&mut self.txs
}
/// Return the list of withdrawals of this block.
pub fn withdrawals(&self) -> Vec<Withdrawal> {
let eth_withdrawals = self.eth_block.withdrawals.clone().unwrap();
eth_withdrawals
.iter()
.map({
|w| {
Withdrawal::new(
w.index.as_u64(),
w.validator_index.as_u64(),
w.address,
w.amount.as_u64(),
)
.unwrap()
}
})
.collect_vec()
}
/// Return root of withdrawals of this block
pub fn withdrawals_root(&self) -> H256 {
self.eth_block.withdrawals_root.unwrap_or_default()
}
/// Push a copy event to the block.
pub fn add_copy_event(&mut self, event: CopyEvent) {
self.copy_events.push(event);
}
/// Push an exponentiation event to the block.
pub fn add_exp_event(&mut self, event: ExpEvent) {
self.exp_events.push(event);
}
/// Push a precompile event to the block.
pub fn add_precompile_event(&mut self, event: PrecompileEvent) {
self.precompile_events.events.push(event);
}
}