stratus/eth/primitives/
block.rs

1use alloy_primitives::B256;
2use alloy_rpc_types_eth::BlockTransactions;
3use alloy_trie::root::ordered_trie_root;
4use display_json::DebugAsJson;
5use itertools::Itertools;
6
7use super::ExternalBlock;
8use super::Index;
9use super::PendingBlock;
10use super::Size;
11use super::TransactionExecution;
12use crate::alias::AlloyBlockAlloyTransaction;
13use crate::alias::AlloyBlockB256;
14use crate::alias::AlloyTransaction;
15use crate::alias::JsonValue;
16use crate::eth::primitives::BlockHeader;
17use crate::eth::primitives::BlockNumber;
18use crate::eth::primitives::Hash;
19use crate::eth::primitives::LogMessage;
20use crate::eth::primitives::TransactionMined;
21use crate::eth::primitives::UnixTime;
22use crate::ext::to_json_value;
23
24#[derive(DebugAsJson, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
25#[cfg_attr(test, derive(fake::Dummy))]
26pub struct Block {
27    pub header: BlockHeader,
28    pub transactions: Vec<TransactionMined>,
29}
30
31impl Block {
32    /// Creates a new block with the given number and timestamp.
33    pub fn new(number: BlockNumber, timestamp: UnixTime) -> Self {
34        Self {
35            header: BlockHeader::new(number, timestamp),
36            transactions: Vec::new(),
37        }
38    }
39
40    /// Constructs an empty genesis block.
41    pub fn genesis() -> Block {
42        Block::new(BlockNumber::ZERO, UnixTime::from(1702568764))
43    }
44
45    /// Calculates block size label by the number of transactions.
46    pub fn label_size_by_transactions(&self) -> &'static str {
47        match self.transactions.len() {
48            0 => "0",
49            1..=5 => "1-5",
50            6..=10 => "6-10",
51            11..=15 => "11-15",
52            16..=20 => "16-20",
53            _ => "20+",
54        }
55    }
56
57    /// Calculates block size label by consumed gas.
58    pub fn label_size_by_gas(&self) -> &'static str {
59        match self.header.gas_used.as_u64() {
60            0 => "0",
61            1..=1_000_000 => "0-1M",
62            1_000_001..=2_000_000 => "1M-2M",
63            2_000_001..=3_000_000 => "2M-3M",
64            3_000_001..=4_000_000 => "3M-4M",
65            4_000_001..=5_000_000 => "4M-5M",
66            5_000_001..=6_000_000 => "5M-6M",
67            6_000_001..=7_000_000 => "6M-7M",
68            7_000_001..=8_000_000 => "7M-8M",
69            8_000_001..=9_000_000 => "8M-9M",
70            9_000_001..=10_000_000 => "9M-10M",
71            _ => "10M+",
72        }
73    }
74
75    /// Serializes itself to JSON-RPC block format with full transactions included.
76    pub fn to_json_rpc_with_full_transactions(self) -> JsonValue {
77        let alloy_block: AlloyBlockAlloyTransaction = self.into();
78        to_json_value(alloy_block)
79    }
80
81    /// Serializes itself to JSON-RPC block format with only transactions hashes included.
82    pub fn to_json_rpc_with_transactions_hashes(self) -> JsonValue {
83        let alloy_block: AlloyBlockB256 = self.into();
84        to_json_value(alloy_block)
85    }
86
87    /// Returns the block number.
88    pub fn number(&self) -> BlockNumber {
89        self.header.number
90    }
91
92    /// Returns the block hash.
93    pub fn hash(&self) -> Hash {
94        self.header.hash
95    }
96
97    pub fn create_log_messages(&self) -> Vec<LogMessage> {
98        let mut log_messages = vec![];
99        for (transaction_index, tx) in self.transactions.iter().enumerate() {
100            for (idx, log) in tx.logs().iter().enumerate() {
101                log_messages.push(LogMessage {
102                    log: log.clone(),
103                    transaction_hash: tx.info.hash,
104                    transaction_index: (transaction_index as u64).into(),
105                    block_hash: self.hash(),
106                    block_number: self.number(),
107                    index: tx.mined_data.first_log_index + Index(idx as u64),
108                });
109            }
110        }
111        log_messages
112    }
113
114    fn calculate_transaction_root(&mut self) {
115        if !self.transactions.is_empty() {
116            let transactions_hashes: Vec<B256> = self.transactions.iter().map(|x| x.info.hash).map(B256::from).collect();
117            self.header.transactions_root = ordered_trie_root(&transactions_hashes).into();
118        }
119    }
120
121    pub fn apply_external(&mut self, external_block: &ExternalBlock) {
122        self.header.hash = external_block.hash();
123        assert!(*self.header.timestamp == external_block.header.timestamp);
124        for transaction in self.transactions.iter_mut() {
125            assert!(transaction.evm_input.block_timestamp == self.header.timestamp);
126            transaction.mined_data.block_hash = external_block.hash();
127        }
128    }
129}
130
131impl From<PendingBlock> for Block {
132    fn from(value: PendingBlock) -> Self {
133        let mut block = Block::new(value.header.number, *value.header.timestamp);
134        let txs: Vec<TransactionExecution> = value.transactions.into_values().collect();
135        block.transactions.reserve(txs.len());
136        block.header.size = Size::from(txs.len() as u64);
137
138        let mut log_index = Index::ZERO;
139        for (tx_idx, execution) in txs.into_iter().enumerate() {
140            let log_count = execution.result.execution.logs.len() as u64;
141            let transaction_mined = TransactionMined::from_execution(execution, block.hash(), (tx_idx as u64).into(), log_index);
142            block.transactions.push(transaction_mined);
143            log_index += Index(log_count);
144        }
145
146        Self::calculate_transaction_root(&mut block);
147
148        block
149    }
150}
151
152// -----------------------------------------------------------------------------
153// Conversions: Self -> Other
154// -----------------------------------------------------------------------------
155impl From<Block> for AlloyBlockAlloyTransaction {
156    fn from(block: Block) -> Self {
157        let alloy_block: AlloyBlockAlloyTransaction = block.header.into();
158        let transactions: Vec<AlloyTransaction> = block.transactions.into_iter().map_into().collect();
159
160        Self {
161            transactions: BlockTransactions::Full(transactions),
162            ..alloy_block
163        }
164    }
165}
166
167impl From<Block> for AlloyBlockB256 {
168    fn from(block: Block) -> Self {
169        let alloy_block: AlloyBlockB256 = block.header.into();
170        let transaction_hashes: Vec<B256> = block.transactions.into_iter().map(|x| x.info.hash).map(B256::from).collect();
171
172        Self {
173            transactions: BlockTransactions::Hashes(transaction_hashes),
174            ..alloy_block
175        }
176    }
177}