stratus/eth/primitives/
block.rs1use 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::LogMined;
10use super::PendingBlock;
11use super::Size;
12use super::TransactionExecution;
13use crate::alias::AlloyBlockAlloyTransaction;
14use crate::alias::AlloyBlockB256;
15use crate::alias::AlloyTransaction;
16use crate::alias::JsonValue;
17use crate::eth::primitives::BlockHeader;
18use crate::eth::primitives::BlockNumber;
19use crate::eth::primitives::Hash;
20use crate::eth::primitives::TransactionMined;
21use crate::eth::primitives::UnixTime;
22use crate::ext::to_json_value;
23
24#[derive(DebugAsJson, Clone, PartialEq, Eq, fake::Dummy, serde::Serialize, serde::Deserialize)]
25pub struct Block {
26 pub header: BlockHeader,
27 pub transactions: Vec<TransactionMined>,
28}
29
30impl Block {
31 pub fn new(number: BlockNumber, timestamp: UnixTime) -> Self {
33 Self {
34 header: BlockHeader::new(number, timestamp),
35 transactions: Vec::new(),
36 }
37 }
38
39 pub fn genesis() -> Block {
41 Block::new(BlockNumber::ZERO, UnixTime::from(1702568764))
42 }
43
44 pub fn label_size_by_transactions(&self) -> &'static str {
46 match self.transactions.len() {
47 0 => "0",
48 1..=5 => "1-5",
49 6..=10 => "6-10",
50 11..=15 => "11-15",
51 16..=20 => "16-20",
52 _ => "20+",
53 }
54 }
55
56 pub fn label_size_by_gas(&self) -> &'static str {
58 match self.header.gas_used.as_u64() {
59 0 => "0",
60 1..=1_000_000 => "0-1M",
61 1_000_001..=2_000_000 => "1M-2M",
62 2_000_001..=3_000_000 => "2M-3M",
63 3_000_001..=4_000_000 => "3M-4M",
64 4_000_001..=5_000_000 => "4M-5M",
65 5_000_001..=6_000_000 => "5M-6M",
66 6_000_001..=7_000_000 => "6M-7M",
67 7_000_001..=8_000_000 => "7M-8M",
68 8_000_001..=9_000_000 => "8M-9M",
69 9_000_001..=10_000_000 => "9M-10M",
70 _ => "10M+",
71 }
72 }
73
74 pub fn to_json_rpc_with_full_transactions(self) -> JsonValue {
76 let alloy_block: AlloyBlockAlloyTransaction = self.into();
77 to_json_value(alloy_block)
78 }
79
80 pub fn to_json_rpc_with_transactions_hashes(self) -> JsonValue {
82 let alloy_block: AlloyBlockB256 = self.into();
83 to_json_value(alloy_block)
84 }
85
86 pub fn number(&self) -> BlockNumber {
88 self.header.number
89 }
90
91 pub fn hash(&self) -> Hash {
93 self.header.hash
94 }
95
96 fn mine_transaction(&mut self, tx: TransactionExecution, transaction_index: Index, log_index: &mut Index) -> TransactionMined {
97 let mined_logs = Self::mine_logs(self, &tx, transaction_index, log_index);
98
99 TransactionMined {
100 input: tx.input,
101 block_timestamp: tx.result.execution.block_timestamp,
102 result: tx.result.execution.result,
103 output: tx.result.execution.output,
104 gas: tx.result.execution.gas,
105 deployed_contract_address: tx.result.execution.deployed_contract_address,
106 transaction_index,
107 block_number: self.header.number,
108 block_hash: self.header.hash,
109 logs: mined_logs,
110 }
111 }
112
113 fn mine_logs(&mut self, tx: &TransactionExecution, transaction_index: Index, log_index: &mut Index) -> Vec<LogMined> {
114 tx.result
115 .execution
116 .logs
117 .iter()
118 .map(|mined_log| {
119 self.header.bloom.accrue_log(mined_log);
120 let log = LogMined::mine_log(mined_log.clone(), self.number(), self.hash(), tx, *log_index, transaction_index);
121 *log_index = *log_index + Index::ONE;
122 log
123 })
124 .collect()
125 }
126
127 fn calculate_transaction_root(&mut self) {
128 if !self.transactions.is_empty() {
129 let transactions_hashes: Vec<B256> = self.transactions.iter().map(|x| x.input.hash).map(B256::from).collect();
130 self.header.transactions_root = ordered_trie_root(&transactions_hashes).into();
131 }
132 }
133
134 fn update_block_hash(&mut self) {
135 for transaction in self.transactions.iter_mut() {
136 transaction.block_hash = self.header.hash;
137 for log in transaction.logs.iter_mut() {
138 log.block_hash = self.header.hash;
139 }
140 }
141 }
142
143 pub fn apply_external(&mut self, external_block: &ExternalBlock) {
144 self.header.hash = external_block.hash();
145 self.header.timestamp = external_block.timestamp();
146 for transaction in self.transactions.iter_mut() {
147 assert!(transaction.block_timestamp == self.header.timestamp);
148 transaction.block_hash = external_block.hash();
149 for log in transaction.logs.iter_mut() {
150 log.block_hash = external_block.hash();
151 }
152 }
153 }
154}
155
156impl From<PendingBlock> for Block {
157 fn from(value: PendingBlock) -> Self {
158 let mut block = Block::new(value.header.number, *value.header.timestamp);
159 let txs: Vec<TransactionExecution> = value.transactions.into_values().collect();
160 block.transactions.reserve(txs.len());
161 block.header.size = Size::from(txs.len() as u64);
162
163 let mut log_index = Index::ZERO;
164 for (tx_idx, tx) in txs.into_iter().enumerate() {
165 let transaction_index = Index::new(tx_idx as u64);
166 let mined_transaction = Self::mine_transaction(&mut block, tx, transaction_index, &mut log_index);
167 block.transactions.push(mined_transaction);
168 }
169
170 Self::calculate_transaction_root(&mut block);
171 Self::update_block_hash(&mut block);
172
173 block
174 }
175}
176
177impl From<Block> for AlloyBlockAlloyTransaction {
181 fn from(block: Block) -> Self {
182 let alloy_block: AlloyBlockAlloyTransaction = block.header.into();
183 let transactions: Vec<AlloyTransaction> = block.transactions.into_iter().map_into().collect();
184
185 Self {
186 transactions: BlockTransactions::Full(transactions),
187 ..alloy_block
188 }
189 }
190}
191
192impl From<Block> for AlloyBlockB256 {
193 fn from(block: Block) -> Self {
194 let alloy_block: AlloyBlockB256 = block.header.into();
195 let transaction_hashes: Vec<B256> = block.transactions.into_iter().map(|x| x.input.hash).map(B256::from).collect();
196
197 Self {
198 transactions: BlockTransactions::Hashes(transaction_hashes),
199 ..alloy_block
200 }
201 }
202}