alloy_network_primitives/
block.rs1use alloy_primitives::B256;
2
3use crate::TransactionResponse;
4use alloc::{vec, vec::Vec};
5use alloy_consensus::error::ValueError;
6use alloy_eips::Encodable2718;
7use core::slice;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "serde", serde(untagged))]
14pub enum BlockTransactions<T> {
15 Full(Vec<T>),
17 Hashes(Vec<B256>),
19 Uncle,
21}
22
23impl<T> Default for BlockTransactions<T> {
24 fn default() -> Self {
25 Self::Hashes(Vec::default())
26 }
27}
28
29impl<T> BlockTransactions<T> {
30 #[inline]
32 pub const fn is_hashes(&self) -> bool {
33 matches!(self, Self::Hashes(_))
34 }
35
36 pub fn as_hashes(&self) -> Option<&[B256]> {
38 match self {
39 Self::Hashes(hashes) => Some(hashes),
40 _ => None,
41 }
42 }
43
44 pub fn first_transaction(&self) -> Option<&T> {
46 self.as_transactions().and_then(|txs| txs.first())
47 }
48
49 #[inline]
51 pub const fn is_full(&self) -> bool {
52 matches!(self, Self::Full(_))
53 }
54
55 pub fn map<U>(self, f: impl FnMut(T) -> U) -> BlockTransactions<U> {
59 match self {
60 Self::Full(txs) => BlockTransactions::Full(txs.into_iter().map(f).collect()),
61 Self::Hashes(hashes) => BlockTransactions::Hashes(hashes),
62 Self::Uncle => BlockTransactions::Uncle,
63 }
64 }
65
66 pub fn try_map<U, E>(
70 self,
71 f: impl FnMut(T) -> Result<U, E>,
72 ) -> Result<BlockTransactions<U>, E> {
73 match self {
74 Self::Full(txs) => {
75 Ok(BlockTransactions::Full(txs.into_iter().map(f).collect::<Result<_, _>>()?))
76 }
77 Self::Hashes(hashes) => Ok(BlockTransactions::Hashes(hashes)),
78 Self::Uncle => Ok(BlockTransactions::Uncle),
79 }
80 }
81
82 pub fn as_transactions(&self) -> Option<&[T]> {
86 match self {
87 Self::Full(txs) => Some(txs),
88 _ => None,
89 }
90 }
91
92 pub fn calculate_transactions_root(&self) -> Option<B256>
96 where
97 T: Encodable2718,
98 {
99 self.as_transactions().map(alloy_consensus::proofs::calculate_transaction_root)
100 }
101
102 #[inline]
104 pub const fn is_uncle(&self) -> bool {
105 matches!(self, Self::Uncle)
106 }
107
108 #[doc(alias = "transactions")]
112 pub fn txns(&self) -> impl Iterator<Item = &T> {
113 self.as_transactions().map(|txs| txs.iter()).unwrap_or_else(|| [].iter())
114 }
115
116 pub fn into_transactions(self) -> vec::IntoIter<T> {
119 match self {
120 Self::Full(txs) => txs.into_iter(),
121 _ => vec::IntoIter::default(),
122 }
123 }
124
125 pub fn into_transactions_vec(self) -> Vec<T> {
129 match self {
130 Self::Full(txs) => txs,
131 _ => vec![],
132 }
133 }
134
135 pub fn try_into_transactions(self) -> Result<Vec<T>, ValueError<Self>> {
139 match self {
140 Self::Full(txs) => Ok(txs),
141 txs @ Self::Hashes(_) => Err(ValueError::new_static(txs, "Unexpected hashes variant")),
142 txs @ Self::Uncle => Err(ValueError::new_static(txs, "Unexpected uncle variant")),
143 }
144 }
145
146 #[inline]
148 pub const fn uncle() -> Self {
149 Self::Uncle
150 }
151
152 #[inline]
154 pub const fn len(&self) -> usize {
155 match self {
156 Self::Hashes(h) => h.len(),
157 Self::Full(f) => f.len(),
158 Self::Uncle => 0,
159 }
160 }
161
162 #[inline]
164 pub const fn is_empty(&self) -> bool {
165 self.len() == 0
166 }
167}
168
169impl<T: TransactionResponse> BlockTransactions<T> {
170 pub fn new_hashes(txs: impl IntoIterator<Item = impl AsRef<T>>) -> Self {
172 Self::Hashes(txs.into_iter().map(|tx| tx.as_ref().tx_hash()).collect())
173 }
174
175 #[inline]
177 pub fn convert_to_hashes(&mut self) {
178 if !self.is_hashes() {
179 *self = Self::Hashes(self.hashes().collect());
180 }
181 }
182
183 #[inline]
185 pub fn convert_to_hashes_if(&mut self, condition: bool) {
186 if !condition {
187 return;
188 }
189 self.convert_to_hashes();
190 }
191
192 #[inline]
194 pub fn into_hashes(mut self) -> Self {
195 self.convert_to_hashes();
196 self
197 }
198
199 #[inline]
201 pub fn into_hashes_if(self, condition: bool) -> Self {
202 if !condition {
203 return self;
204 }
205 self.into_hashes()
206 }
207
208 #[inline]
210 pub fn hashes(&self) -> BlockTransactionHashes<'_, T> {
211 BlockTransactionHashes::new(self)
212 }
213
214 pub fn into_hashes_vec(self) -> Vec<B256> {
218 match self {
219 Self::Hashes(hashes) => hashes,
220 this => this.hashes().collect(),
221 }
222 }
223}
224
225impl<T> From<Vec<B256>> for BlockTransactions<T> {
226 fn from(hashes: Vec<B256>) -> Self {
227 Self::Hashes(hashes)
228 }
229}
230
231impl<T: TransactionResponse> From<Vec<T>> for BlockTransactions<T> {
232 fn from(transactions: Vec<T>) -> Self {
233 Self::Full(transactions)
234 }
235}
236
237#[derive(Clone, Debug)]
241pub struct BlockTransactionHashes<'a, T>(BlockTransactionHashesInner<'a, T>);
242
243#[derive(Clone, Debug)]
244enum BlockTransactionHashesInner<'a, T> {
245 Hashes(slice::Iter<'a, B256>),
246 Full(slice::Iter<'a, T>),
247 Uncle,
248}
249
250impl<'a, T> BlockTransactionHashes<'a, T> {
251 #[inline]
252 fn new(txs: &'a BlockTransactions<T>) -> Self {
253 Self(match txs {
254 BlockTransactions::Hashes(txs) => BlockTransactionHashesInner::Hashes(txs.iter()),
255 BlockTransactions::Full(txs) => BlockTransactionHashesInner::Full(txs.iter()),
256 BlockTransactions::Uncle => BlockTransactionHashesInner::Uncle,
257 })
258 }
259}
260
261impl<T: TransactionResponse> Iterator for BlockTransactionHashes<'_, T> {
262 type Item = B256;
263
264 #[inline]
265 fn next(&mut self) -> Option<Self::Item> {
266 match &mut self.0 {
267 BlockTransactionHashesInner::Hashes(txs) => txs.next().copied(),
268 BlockTransactionHashesInner::Full(txs) => txs.next().map(|tx| tx.tx_hash()),
269 BlockTransactionHashesInner::Uncle => None,
270 }
271 }
272
273 #[inline]
274 fn size_hint(&self) -> (usize, Option<usize>) {
275 match &self.0 {
276 BlockTransactionHashesInner::Full(txs) => txs.size_hint(),
277 BlockTransactionHashesInner::Hashes(txs) => txs.size_hint(),
278 BlockTransactionHashesInner::Uncle => (0, Some(0)),
279 }
280 }
281}
282
283impl<T: TransactionResponse> ExactSizeIterator for BlockTransactionHashes<'_, T> {
284 #[inline]
285 fn len(&self) -> usize {
286 match &self.0 {
287 BlockTransactionHashesInner::Full(txs) => txs.len(),
288 BlockTransactionHashesInner::Hashes(txs) => txs.len(),
289 BlockTransactionHashesInner::Uncle => 0,
290 }
291 }
292}
293
294impl<T: TransactionResponse> DoubleEndedIterator for BlockTransactionHashes<'_, T> {
295 #[inline]
296 fn next_back(&mut self) -> Option<Self::Item> {
297 match &mut self.0 {
298 BlockTransactionHashesInner::Full(txs) => txs.next_back().map(|tx| tx.tx_hash()),
299 BlockTransactionHashesInner::Hashes(txs) => txs.next_back().copied(),
300 BlockTransactionHashesInner::Uncle => None,
301 }
302 }
303}
304
305#[cfg(feature = "std")]
306impl<T: TransactionResponse> std::iter::FusedIterator for BlockTransactionHashes<'_, T> {}
307
308#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
313#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
314pub enum BlockTransactionsKind {
315 #[default]
317 Hashes,
318 Full,
320}
321
322impl BlockTransactionsKind {
323 pub const fn is_hashes(&self) -> bool {
325 matches!(self, Self::Hashes)
326 }
327
328 pub const fn is_full(&self) -> bool {
330 matches!(self, Self::Full)
331 }
332}
333
334impl From<bool> for BlockTransactionsKind {
335 fn from(is_full: bool) -> Self {
336 if is_full {
337 Self::Full
338 } else {
339 Self::Hashes
340 }
341 }
342}
343
344impl From<BlockTransactionsKind> for bool {
345 fn from(kind: BlockTransactionsKind) -> Self {
346 match kind {
347 BlockTransactionsKind::Full => true,
348 BlockTransactionsKind::Hashes => false,
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 fn test_full_conversion() {
359 let full = true;
360 assert_eq!(BlockTransactionsKind::Full, full.into());
361
362 let full = false;
363 assert_eq!(BlockTransactionsKind::Hashes, full.into());
364 }
365
366 #[test]
367 fn test_block_transactions_default() {
368 let default: BlockTransactions<()> = BlockTransactions::default();
369 assert!(default.is_hashes());
370 assert_eq!(default.len(), 0);
371 }
372
373 #[test]
374 fn test_block_transactions_is_methods() {
375 let hashes: BlockTransactions<()> = BlockTransactions::Hashes(vec![B256::ZERO]);
376 let full: BlockTransactions<u32> = BlockTransactions::Full(vec![42]);
377 let uncle: BlockTransactions<()> = BlockTransactions::Uncle;
378
379 assert!(hashes.is_hashes());
380 assert!(!hashes.is_full());
381 assert!(!hashes.is_uncle());
382
383 assert!(full.is_full());
384 assert!(!full.is_hashes());
385 assert!(!full.is_uncle());
386
387 assert!(uncle.is_uncle());
388 assert!(!uncle.is_full());
389 assert!(!uncle.is_hashes());
390 }
391
392 #[test]
393 fn test_as_hashes() {
394 let hashes = vec![B256::ZERO, B256::repeat_byte(1)];
395 let tx_hashes: BlockTransactions<()> = BlockTransactions::Hashes(hashes.clone());
396
397 assert_eq!(tx_hashes.as_hashes(), Some(hashes.as_slice()));
398 }
399
400 #[test]
401 fn test_as_transactions() {
402 let transactions = vec![42, 43];
403 let txs = BlockTransactions::Full(transactions.clone());
404
405 assert_eq!(txs.as_transactions(), Some(transactions.as_slice()));
406 }
407
408 #[test]
409 fn test_block_transactions_len_and_is_empty() {
410 let hashes: BlockTransactions<()> = BlockTransactions::Hashes(vec![B256::ZERO]);
411 let full = BlockTransactions::Full(vec![42]);
412 let uncle: BlockTransactions<()> = BlockTransactions::Uncle;
413
414 assert_eq!(hashes.len(), 1);
415 assert_eq!(full.len(), 1);
416 assert_eq!(uncle.len(), 0);
417
418 assert!(!hashes.is_empty());
419 assert!(!full.is_empty());
420 assert!(uncle.is_empty());
421 }
422
423 #[test]
424 fn test_block_transactions_txns_iterator() {
425 let transactions = vec![42, 43];
426 let txs = BlockTransactions::Full(transactions);
427 let mut iter = txs.txns();
428
429 assert_eq!(iter.next(), Some(&42));
430 assert_eq!(iter.next(), Some(&43));
431 assert_eq!(iter.next(), None);
432 }
433
434 #[test]
435 fn test_block_transactions_into_transactions() {
436 let transactions = vec![42, 43];
437 let txs = BlockTransactions::Full(transactions.clone());
438 let collected: Vec<_> = txs.into_transactions().collect();
439
440 assert_eq!(collected, transactions);
441 }
442
443 #[test]
444 fn test_block_transactions_kind_conversion() {
445 let full: BlockTransactionsKind = true.into();
446 assert_eq!(full, BlockTransactionsKind::Full);
447
448 let hashes: BlockTransactionsKind = false.into();
449 assert_eq!(hashes, BlockTransactionsKind::Hashes);
450
451 let bool_full: bool = BlockTransactionsKind::Full.into();
452 assert!(bool_full);
453
454 let bool_hashes: bool = BlockTransactionsKind::Hashes.into();
455 assert!(!bool_hashes);
456 }
457}