1use super::transaction::TransactionInfo;
2use alloy_consensus::{
3 BlockBody, EMPTY_OMMER_ROOT_HASH, Header, proofs::calculate_transaction_root,
4};
5use alloy_eips::eip2718::Encodable2718;
6use alloy_network::Network;
7use foundry_primitives::FoundryTxEnvelope;
8
9use crate::eth::transaction::MaybeImpersonatedTransaction;
10
11pub type Block<T = FoundryTxEnvelope> = alloy_consensus::Block<MaybeImpersonatedTransaction<T>>;
13
14#[derive(Clone, Debug)]
16pub struct BlockInfo<N: Network> {
17 pub block: Block<N::TxEnvelope>,
18 pub transactions: Vec<TransactionInfo>,
19 pub receipts: Vec<N::ReceiptEnvelope>,
20}
21
22pub fn create_block<T, Tx>(
28 mut header: Header,
29 transactions: impl IntoIterator<Item = T>,
30) -> Block<Tx>
31where
32 Tx: Encodable2718,
33 T: Into<MaybeImpersonatedTransaction<Tx>>,
34{
35 let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect();
36 let transactions_root = calculate_transaction_root(&transactions);
37
38 header.transactions_root = transactions_root;
39 header.ommers_hash = EMPTY_OMMER_ROOT_HASH;
40
41 let body = BlockBody { transactions, ommers: Vec::new(), withdrawals: None };
42 Block::new(header, body)
43}
44
45#[cfg(test)]
46mod tests {
47 use alloy_primitives::{
48 Address, B64, B256, Bloom, U256, b256,
49 hex::{self, FromHex},
50 };
51 use alloy_rlp::Decodable;
52
53 use super::*;
54 use std::str::FromStr;
55
56 #[test]
57 fn header_rlp_roundtrip() {
58 let mut header = Header {
59 parent_hash: Default::default(),
60 ommers_hash: Default::default(),
61 beneficiary: Default::default(),
62 state_root: Default::default(),
63 transactions_root: Default::default(),
64 receipts_root: Default::default(),
65 logs_bloom: Default::default(),
66 difficulty: Default::default(),
67 number: 124u64,
68 gas_limit: Default::default(),
69 gas_used: 1337u64,
70 timestamp: 0,
71 extra_data: Default::default(),
72 mix_hash: Default::default(),
73 nonce: B64::with_last_byte(99),
74 withdrawals_root: Default::default(),
75 blob_gas_used: Default::default(),
76 excess_blob_gas: Default::default(),
77 parent_beacon_block_root: Default::default(),
78 base_fee_per_gas: None,
79 requests_hash: None,
80 block_access_list_hash: None,
81 slot_number: None,
82 };
83
84 let encoded = alloy_rlp::encode(&header);
85 let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap();
86 assert_eq!(header, decoded);
87
88 header.base_fee_per_gas = Some(12345u64);
89
90 let encoded = alloy_rlp::encode(&header);
91 let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap();
92 assert_eq!(header, decoded);
93 }
94
95 #[test]
96 fn test_encode_block_header() {
97 use alloy_rlp::Encodable;
98
99 let expected = hex::decode("f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000").unwrap();
100 let mut data = vec![];
101 let header = Header {
102 parent_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
103 ommers_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
104 beneficiary: Address::from_str("0000000000000000000000000000000000000000").unwrap(),
105 state_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
106 transactions_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
107 receipts_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
108 logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(),
109 difficulty: U256::from(2222),
110 number: 0xd05u64,
111 gas_limit: 0x115cu64,
112 gas_used: 0x15b3u64,
113 timestamp: 0x1a0au64,
114 extra_data: hex::decode("7788").unwrap().into(),
115 mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
116 withdrawals_root: None,
117 blob_gas_used: None,
118 excess_blob_gas: None,
119 parent_beacon_block_root: None,
120 nonce: B64::ZERO,
121 base_fee_per_gas: None,
122 requests_hash: None,
123 block_access_list_hash: None,
124 slot_number: None,
125 };
126
127 header.encode(&mut data);
128 assert_eq!(hex::encode(&data), hex::encode(expected));
129 assert_eq!(header.length(), data.len());
130 }
131
132 #[test]
133 fn test_decode_block_header() {
135 let data = hex::decode("f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000").unwrap();
136 let expected = Header {
137 parent_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
138 ommers_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
139 beneficiary: Address::from_str("0000000000000000000000000000000000000000").unwrap(),
140 state_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
141 transactions_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
142 receipts_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
143 logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(),
144 difficulty: U256::from(2222),
145 number: 0xd05u64,
146 gas_limit: 0x115cu64,
147 gas_used: 0x15b3u64,
148 timestamp: 0x1a0au64,
149 extra_data: hex::decode("7788").unwrap().into(),
150 mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
151 nonce: B64::ZERO,
152 withdrawals_root: None,
153 blob_gas_used: None,
154 excess_blob_gas: None,
155 parent_beacon_block_root: None,
156 base_fee_per_gas: None,
157 requests_hash: None,
158 block_access_list_hash: None,
159 slot_number: None,
160 };
161 let header = Header::decode(&mut data.as_slice()).unwrap();
162 assert_eq!(header, expected);
163 }
164
165 #[test]
166 fn test_eip1559_block_header_hash() {
168 let expected_hash =
169 b256!("0x6a251c7c3c5dca7b42407a3752ff48f3bbca1fab7f9868371d9918daf1988d1f");
170 let header = Header {
171 parent_hash: B256::from_str("e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2a").unwrap(),
172 ommers_hash: B256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(),
173 beneficiary: Address::from_str("ba5e000000000000000000000000000000000000").unwrap(),
174 state_root: B256::from_str("ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7").unwrap(),
175 transactions_root: B256::from_str("50f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accf").unwrap(),
176 receipts_root: B256::from_str("29b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9").unwrap(),
177 logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(),
178 difficulty: U256::from(0x020000),
179 number: 1u64,
180 gas_limit: U256::from(0x016345785d8a0000u128).to::<u64>(),
181 gas_used: U256::from(0x015534).to::<u64>(),
182 timestamp: 0x079e,
183 extra_data: hex::decode("42").unwrap().into(),
184 mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(),
185 nonce: B64::ZERO,
186 base_fee_per_gas: Some(875),
187 withdrawals_root: None,
188 blob_gas_used: None,
189 excess_blob_gas: None,
190 parent_beacon_block_root: None,
191 requests_hash: None,
192 block_access_list_hash: None,
193 slot_number: None,
194 };
195 assert_eq!(header.hash_slow(), expected_hash);
196 }
197
198 #[test]
199 fn block_network_roundtrip() {
201 use alloy_rlp::Encodable;
202
203 let data = hex::decode("f9034df90348a0fbdbd8d2d0ac5f14bd5fa90e547fe6f1d15019c724f8e7b60972d381cd5d9cf8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c9577e7945db22e38fc060909f2278c7746b0f9ba05017cfa3b0247e35197215ae8d610265ffebc8edca8ea66d6567eb0adecda867a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018355bb7b871fffffffffffff808462bd0e1ab9014bf90148a00000000000000000000000000000000000000000000000000000000000000000f85494319fa8f1bc4e53410e92d10d918659b16540e60a945a573efb304d04c1224cd012313e827eca5dce5d94a9c831c5a268031176ebf5f3de5051e8cba0dbfe94c9577e7945db22e38fc060909f2278c7746b0f9b808400000000f8c9b841a6946f2d16f68338cbcbd8b117374ab421128ce422467088456bceba9d70c34106128e6d4564659cf6776c08a4186063c0a05f7cffd695c10cf26a6f301b67f800b8412b782100c18c35102dc0a37ece1a152544f04ad7dc1868d18a9570f744ace60870f822f53d35e89a2ea9709ccbf1f4a25ee5003944faa845d02dde0a41d5704601b841d53caebd6c8a82456e85c2806a9e08381f959a31fb94a77e58f00e38ad97b2e0355b8519ab2122662cbe022f2a4ef7ff16adc0b2d5dcd123181ec79705116db300a063746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365880000000000000000c0c0").unwrap();
204
205 let block = <Block>::decode(&mut data.as_slice()).unwrap();
206
207 let mut encoded = Vec::new();
209 block.encode(&mut encoded);
210 assert_eq!(data, encoded);
211
212 assert_eq!(block.length(), encoded.len());
214 }
215}