reth_primitives_traits/block/
recovered.rs
1use crate::{
4 block::{error::SealedBlockRecoveryError, SealedBlock},
5 transaction::signed::{RecoveryError, SignedTransaction},
6 Block, BlockBody, InMemorySize, SealedHeader,
7};
8use alloc::vec::Vec;
9use alloy_consensus::{transaction::Recovered, BlockHeader};
10use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
11use alloy_primitives::{Address, BlockHash, BlockNumber, Bloom, Bytes, Sealed, B256, B64, U256};
12use derive_more::Deref;
13
14#[derive(Debug, Clone, Deref)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct RecoveredBlock<B: Block> {
33 #[deref]
35 #[cfg_attr(
36 feature = "serde",
37 serde(bound = "SealedBlock<B>: serde::Serialize + serde::de::DeserializeOwned")
38 )]
39 block: SealedBlock<B>,
40 senders: Vec<Address>,
42}
43
44impl<B: Block> RecoveredBlock<B> {
45 pub fn new(block: B, senders: Vec<Address>, hash: BlockHash) -> Self {
50 Self { block: SealedBlock::new_unchecked(block, hash), senders }
51 }
52
53 pub fn new_unhashed(block: B, senders: Vec<Address>) -> Self {
57 Self { block: SealedBlock::new_unhashed(block), senders }
58 }
59
60 pub fn senders(&self) -> &[Address] {
62 &self.senders
63 }
64
65 pub fn senders_iter(&self) -> impl Iterator<Item = &Address> {
67 self.senders.iter()
68 }
69
70 pub fn into_block(self) -> B {
72 self.block.into_block()
73 }
74
75 pub const fn sealed_block(&self) -> &SealedBlock<B> {
77 &self.block
78 }
79
80 pub const fn new_sealed(block: SealedBlock<B>, senders: Vec<Address>) -> Self {
83 Self { block, senders }
84 }
85
86 pub fn try_new(
91 block: B,
92 senders: Vec<Address>,
93 hash: BlockHash,
94 ) -> Result<Self, SealedBlockRecoveryError<B>> {
95 let senders = if block.body().transaction_count() == senders.len() {
96 senders
97 } else {
98 let Ok(senders) = block.body().try_recover_signers() else {
99 return Err(SealedBlockRecoveryError::new(SealedBlock::new_unchecked(block, hash)));
100 };
101 senders
102 };
103 Ok(Self::new(block, senders, hash))
104 }
105
106 pub fn try_new_unchecked(
111 block: B,
112 senders: Vec<Address>,
113 hash: BlockHash,
114 ) -> Result<Self, SealedBlockRecoveryError<B>> {
115 let senders = if block.body().transaction_count() == senders.len() {
116 senders
117 } else {
118 let Ok(senders) = block.body().try_recover_signers_unchecked() else {
119 return Err(SealedBlockRecoveryError::new(SealedBlock::new_unchecked(block, hash)));
120 };
121 senders
122 };
123 Ok(Self::new(block, senders, hash))
124 }
125
126 pub fn try_new_unhashed(block: B, senders: Vec<Address>) -> Result<Self, RecoveryError> {
131 let senders = if block.body().transaction_count() == senders.len() {
132 senders
133 } else {
134 block.body().try_recover_signers()?
135 };
136 Ok(Self::new_unhashed(block, senders))
137 }
138
139 pub fn try_new_unhashed_unchecked(
144 block: B,
145 senders: Vec<Address>,
146 ) -> Result<Self, RecoveryError> {
147 let senders = if block.body().transaction_count() == senders.len() {
148 senders
149 } else {
150 block.body().try_recover_signers_unchecked()?
151 };
152 Ok(Self::new_unhashed(block, senders))
153 }
154
155 pub fn try_recover(block: B) -> Result<Self, RecoveryError> {
160 let senders = block.body().try_recover_signers()?;
161 Ok(Self::new_unhashed(block, senders))
162 }
163
164 pub fn try_recover_unchecked(block: B) -> Result<Self, RecoveryError> {
169 let senders = block.body().try_recover_signers_unchecked()?;
170 Ok(Self::new_unhashed(block, senders))
171 }
172
173 pub fn try_recover_sealed(block: SealedBlock<B>) -> Result<Self, SealedBlockRecoveryError<B>> {
178 let Ok(senders) = block.body().try_recover_signers() else {
179 return Err(SealedBlockRecoveryError::new(block));
180 };
181 let (block, hash) = block.split();
182 Ok(Self::new(block, senders, hash))
183 }
184
185 pub fn try_recover_sealed_unchecked(
190 block: SealedBlock<B>,
191 ) -> Result<Self, SealedBlockRecoveryError<B>> {
192 let Ok(senders) = block.body().try_recover_signers_unchecked() else {
193 return Err(SealedBlockRecoveryError::new(block));
194 };
195 let (block, hash) = block.split();
196 Ok(Self::new(block, senders, hash))
197 }
198
199 pub fn try_recover_sealed_with_senders(
206 block: SealedBlock<B>,
207 senders: Vec<Address>,
208 ) -> Result<Self, SealedBlockRecoveryError<B>> {
209 let (block, hash) = block.split();
210 Self::try_new(block, senders, hash)
211 }
212
213 pub fn try_recover_sealed_with_senders_unchecked(
218 block: SealedBlock<B>,
219 senders: Vec<Address>,
220 ) -> Result<Self, SealedBlockRecoveryError<B>> {
221 let (block, hash) = block.split();
222 Self::try_new_unchecked(block, senders, hash)
223 }
224
225 pub fn hash_ref(&self) -> &BlockHash {
227 self.block.hash_ref()
228 }
229
230 pub fn hash(&self) -> BlockHash {
232 *self.hash_ref()
233 }
234
235 pub fn num_hash(&self) -> BlockNumHash {
237 BlockNumHash::new(self.header().number(), self.hash())
238 }
239
240 pub fn block_with_parent(&self) -> BlockWithParent {
242 BlockWithParent { parent: self.header().parent_hash(), block: self.num_hash() }
243 }
244
245 pub fn clone_header(&self) -> B::Header {
247 self.header().clone()
248 }
249
250 pub fn clone_sealed_header(&self) -> SealedHeader<B::Header> {
252 SealedHeader::new(self.clone_header(), self.hash())
253 }
254
255 pub fn clone_sealed_block(&self) -> SealedBlock<B> {
257 self.block.clone()
258 }
259
260 pub fn into_header(self) -> B::Header {
262 self.block.into_header()
263 }
264
265 pub fn into_body(self) -> B::Body {
267 self.block.into_body()
268 }
269
270 pub fn into_sealed_block(self) -> SealedBlock<B> {
272 self.block
273 }
274
275 pub fn split_sealed(self) -> (SealedBlock<B>, Vec<Address>) {
277 (self.block, self.senders)
278 }
279
280 #[doc(alias = "into_components")]
282 pub fn split(self) -> (B, Vec<Address>) {
283 (self.block.into_block(), self.senders)
284 }
285
286 #[inline]
288 pub fn transactions_with_sender(
289 &self,
290 ) -> impl Iterator<Item = (&Address, &<B::Body as BlockBody>::Transaction)> + '_ {
291 self.senders.iter().zip(self.block.body().transactions())
292 }
293
294 #[inline]
296 pub fn clone_transactions_recovered(
297 &self,
298 ) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> + '_ {
299 self.transactions_with_sender()
300 .map(|(sender, tx)| Recovered::new_unchecked(tx.clone(), *sender))
301 }
302
303 #[inline]
305 pub fn transactions_recovered(
306 &self,
307 ) -> impl Iterator<Item = Recovered<&'_ <B::Body as BlockBody>::Transaction>> + '_ {
308 self.transactions_with_sender().map(|(sender, tx)| Recovered::new_unchecked(tx, *sender))
309 }
310
311 #[inline]
313 pub fn into_transactions_recovered(
314 self,
315 ) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> {
316 self.block
317 .split()
318 .0
319 .into_body()
320 .into_transactions()
321 .into_iter()
322 .zip(self.senders)
323 .map(|(tx, sender)| tx.with_signer(sender))
324 }
325
326 #[inline]
328 pub fn into_transactions(self) -> Vec<<B::Body as BlockBody>::Transaction> {
329 self.block.split().0.into_body().into_transactions()
330 }
331}
332
333impl<B: Block> BlockHeader for RecoveredBlock<B> {
334 fn parent_hash(&self) -> B256 {
335 self.header().parent_hash()
336 }
337
338 fn ommers_hash(&self) -> B256 {
339 self.header().ommers_hash()
340 }
341
342 fn beneficiary(&self) -> Address {
343 self.header().beneficiary()
344 }
345
346 fn state_root(&self) -> B256 {
347 self.header().state_root()
348 }
349
350 fn transactions_root(&self) -> B256 {
351 self.header().transactions_root()
352 }
353
354 fn receipts_root(&self) -> B256 {
355 self.header().receipts_root()
356 }
357
358 fn withdrawals_root(&self) -> Option<B256> {
359 self.header().withdrawals_root()
360 }
361
362 fn logs_bloom(&self) -> Bloom {
363 self.header().logs_bloom()
364 }
365
366 fn difficulty(&self) -> U256 {
367 self.header().difficulty()
368 }
369
370 fn number(&self) -> BlockNumber {
371 self.header().number()
372 }
373
374 fn gas_limit(&self) -> u64 {
375 self.header().gas_limit()
376 }
377
378 fn gas_used(&self) -> u64 {
379 self.header().gas_used()
380 }
381
382 fn timestamp(&self) -> u64 {
383 self.header().timestamp()
384 }
385
386 fn mix_hash(&self) -> Option<B256> {
387 self.header().mix_hash()
388 }
389
390 fn nonce(&self) -> Option<B64> {
391 self.header().nonce()
392 }
393
394 fn base_fee_per_gas(&self) -> Option<u64> {
395 self.header().base_fee_per_gas()
396 }
397
398 fn blob_gas_used(&self) -> Option<u64> {
399 self.header().blob_gas_used()
400 }
401
402 fn excess_blob_gas(&self) -> Option<u64> {
403 self.header().excess_blob_gas()
404 }
405
406 fn parent_beacon_block_root(&self) -> Option<B256> {
407 self.header().parent_beacon_block_root()
408 }
409
410 fn requests_hash(&self) -> Option<B256> {
411 self.header().requests_hash()
412 }
413
414 fn extra_data(&self) -> &Bytes {
415 self.header().extra_data()
416 }
417}
418
419impl<B: Block> Eq for RecoveredBlock<B> {}
420
421impl<B: Block> PartialEq for RecoveredBlock<B> {
422 fn eq(&self, other: &Self) -> bool {
423 self.hash_ref().eq(other.hash_ref()) &&
424 self.block.eq(&other.block) &&
425 self.senders.eq(&other.senders)
426 }
427}
428
429impl<B: Block + Default> Default for RecoveredBlock<B> {
430 #[inline]
431 fn default() -> Self {
432 Self::new_unhashed(B::default(), Default::default())
433 }
434}
435
436impl<B: Block> InMemorySize for RecoveredBlock<B> {
437 #[inline]
438 fn size(&self) -> usize {
439 self.block.size() + self.senders.len() * core::mem::size_of::<Address>()
440 }
441}
442
443impl<B: Block> From<RecoveredBlock<B>> for Sealed<B> {
444 fn from(value: RecoveredBlock<B>) -> Self {
445 value.block.into()
446 }
447}
448
449#[cfg(any(test, feature = "arbitrary"))]
450impl<'a, B> arbitrary::Arbitrary<'a> for RecoveredBlock<B>
451where
452 B: Block + arbitrary::Arbitrary<'a>,
453{
454 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
455 let block = B::arbitrary(u)?;
456 Ok(Self::try_recover(block).unwrap())
457 }
458}
459
460#[cfg(any(test, feature = "test-utils"))]
461impl<B: Block> RecoveredBlock<B> {
462 pub const fn senders_mut(&mut self) -> &mut Vec<Address> {
464 &mut self.senders
465 }
466
467 pub fn push_sender(&mut self, sender: Address) {
469 self.senders.push(sender);
470 }
471}
472
473#[cfg(any(test, feature = "test-utils"))]
474impl<B> core::ops::DerefMut for RecoveredBlock<B>
475where
476 B: Block,
477{
478 fn deref_mut(&mut self) -> &mut Self::Target {
479 &mut self.block
480 }
481}
482
483#[cfg(any(test, feature = "test-utils"))]
484impl<B: crate::test_utils::TestBlock> RecoveredBlock<B> {
485 pub fn set_header(&mut self, header: B::Header) {
487 *self.header_mut() = header
488 }
489
490 pub fn set_hash(&mut self, hash: BlockHash) {
492 self.block.set_hash(hash)
493 }
494
495 pub const fn header_mut(&mut self) -> &mut B::Header {
497 self.block.header_mut()
498 }
499
500 pub const fn block_mut(&mut self) -> &mut B::Body {
502 self.block.body_mut()
503 }
504
505 pub fn set_parent_hash(&mut self, hash: BlockHash) {
507 self.block.set_parent_hash(hash);
508 }
509
510 pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
512 self.block.set_block_number(number);
513 }
514
515 pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
517 self.block.set_state_root(state_root);
518 }
519
520 pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
522 self.block.set_difficulty(difficulty);
523 }
524}
525
526#[cfg(feature = "serde-bincode-compat")]
528pub(super) mod serde_bincode_compat {
529 use crate::{
530 serde_bincode_compat::{self, SerdeBincodeCompat},
531 Block,
532 };
533 use alloc::{borrow::Cow, vec::Vec};
534 use alloy_primitives::Address;
535 use serde::{Deserialize, Deserializer, Serialize, Serializer};
536 use serde_with::{DeserializeAs, SerializeAs};
537
538 #[derive(derive_more::Debug, Serialize, Deserialize)]
558 pub struct RecoveredBlock<
559 'a,
560 T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
561 > {
562 #[serde(
563 bound = "serde_bincode_compat::SealedBlock<'a, T>: Serialize + serde::de::DeserializeOwned"
564 )]
565 block: serde_bincode_compat::SealedBlock<'a, T>,
566 #[expect(clippy::owned_cow)]
567 senders: Cow<'a, Vec<Address>>,
568 }
569
570 impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
571 From<&'a super::RecoveredBlock<T>> for RecoveredBlock<'a, T>
572 {
573 fn from(value: &'a super::RecoveredBlock<T>) -> Self {
574 Self { block: (&value.block).into(), senders: Cow::Borrowed(&value.senders) }
575 }
576 }
577
578 impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
579 From<RecoveredBlock<'a, T>> for super::RecoveredBlock<T>
580 {
581 fn from(value: RecoveredBlock<'a, T>) -> Self {
582 Self::new_sealed(value.block.into(), value.senders.into_owned())
583 }
584 }
585
586 impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
587 SerializeAs<super::RecoveredBlock<T>> for RecoveredBlock<'_, T>
588 {
589 fn serialize_as<S>(
590 source: &super::RecoveredBlock<T>,
591 serializer: S,
592 ) -> Result<S::Ok, S::Error>
593 where
594 S: Serializer,
595 {
596 RecoveredBlock::from(source).serialize(serializer)
597 }
598 }
599
600 impl<'de, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
601 DeserializeAs<'de, super::RecoveredBlock<T>> for RecoveredBlock<'de, T>
602 {
603 fn deserialize_as<D>(deserializer: D) -> Result<super::RecoveredBlock<T>, D::Error>
604 where
605 D: Deserializer<'de>,
606 {
607 RecoveredBlock::deserialize(deserializer).map(Into::into)
608 }
609 }
610
611 impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
612 SerdeBincodeCompat for super::RecoveredBlock<T>
613 {
614 type BincodeRepr<'a> = RecoveredBlock<'a, T>;
615
616 fn as_repr(&self) -> Self::BincodeRepr<'_> {
617 self.into()
618 }
619
620 fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
621 repr.into()
622 }
623 }
624}