reth_stateless/
recover_block.rs

1use crate::validation::StatelessValidationError;
2use alloc::vec::Vec;
3use alloy_consensus::BlockHeader;
4use alloy_primitives::{Address, Signature, B256};
5use core::ops::Deref;
6use reth_chainspec::EthereumHardforks;
7use reth_ethereum_primitives::{Block, TransactionSigned};
8use reth_primitives_traits::{Block as _, RecoveredBlock};
9use serde::{Deserialize, Serialize};
10use serde_with::{serde_as, Bytes};
11
12#[cfg(all(feature = "k256", feature = "secp256k1"))]
13use k256 as _;
14
15/// Serialized uncompressed public key
16#[serde_as]
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct UncompressedPublicKey(#[serde_as(as = "Bytes")] pub [u8; 65]);
19
20impl Deref for UncompressedPublicKey {
21    type Target = [u8];
22
23    fn deref(&self) -> &Self::Target {
24        &self.0
25    }
26}
27
28/// Verifies all transactions in a block against a list of public keys and signatures.
29///
30/// Returns a `RecoveredBlock`
31pub(crate) fn recover_block_with_public_keys<ChainSpec>(
32    block: Block,
33    public_keys: Vec<UncompressedPublicKey>,
34    chain_spec: &ChainSpec,
35) -> Result<RecoveredBlock<Block>, StatelessValidationError>
36where
37    ChainSpec: EthereumHardforks,
38{
39    if block.body().transactions.len() != public_keys.len() {
40        return Err(StatelessValidationError::Custom(
41            "Number of public keys must match number of transactions",
42        ));
43    }
44
45    // Determine if we're in the Homestead fork for signature validation
46    let is_homestead = chain_spec.is_homestead_active_at_block(block.header().number());
47
48    // Verify each transaction signature against its corresponding public key
49    let senders = public_keys
50        .iter()
51        .zip(block.body().transactions())
52        .map(|(vk, tx)| verify_and_compute_sender(vk, tx, is_homestead))
53        .collect::<Result<Vec<_>, _>>()?;
54
55    // Create RecoveredBlock with verified senders
56    let block_hash = block.hash_slow();
57    Ok(RecoveredBlock::new(block, senders, block_hash))
58}
59
60/// Verifies a transaction using its signature and the given public key.
61///
62/// Note: If the signature or the public key is incorrect, then this method
63/// will return an error.
64///
65/// Returns the address derived from the public key.
66fn verify_and_compute_sender(
67    vk: &UncompressedPublicKey,
68    tx: &TransactionSigned,
69    is_homestead: bool,
70) -> Result<Address, StatelessValidationError> {
71    let sig = tx.signature();
72
73    // non-normalized signatures are only valid pre-homestead
74    let sig_is_normalized = sig.normalize_s().is_none();
75    if is_homestead && !sig_is_normalized {
76        return Err(StatelessValidationError::HomesteadSignatureNotNormalized);
77    }
78    let sig_hash = tx.signature_hash();
79    #[cfg(all(feature = "k256", feature = "secp256k1"))]
80    {
81        let _ = verify_and_compute_sender_unchecked_k256;
82    }
83    #[cfg(feature = "secp256k1")]
84    {
85        verify_and_compute_sender_unchecked_secp256k1(vk, sig, sig_hash)
86    }
87    #[cfg(all(feature = "k256", not(feature = "secp256k1")))]
88    {
89        verify_and_compute_sender_unchecked_k256(vk, sig, sig_hash)
90    }
91    #[cfg(not(any(feature = "secp256k1", feature = "k256")))]
92    {
93        let _ = vk;
94        let _ = tx;
95        let _: B256 = sig_hash;
96        let _: &Signature = sig;
97
98        unimplemented!("Must choose either k256 or secp256k1 feature")
99    }
100}
101#[cfg(feature = "k256")]
102fn verify_and_compute_sender_unchecked_k256(
103    vk: &UncompressedPublicKey,
104    sig: &Signature,
105    sig_hash: B256,
106) -> Result<Address, StatelessValidationError> {
107    use k256::ecdsa::{signature::hazmat::PrehashVerifier, VerifyingKey};
108
109    let vk =
110        VerifyingKey::from_sec1_bytes(vk).map_err(|_| StatelessValidationError::SignerRecovery)?;
111
112    sig.to_k256()
113        .and_then(|sig| vk.verify_prehash(sig_hash.as_slice(), &sig))
114        .map_err(|_| StatelessValidationError::SignerRecovery)?;
115
116    Ok(Address::from_public_key(&vk))
117}
118
119#[cfg(feature = "secp256k1")]
120fn verify_and_compute_sender_unchecked_secp256k1(
121    vk: &UncompressedPublicKey,
122    sig: &Signature,
123    sig_hash: B256,
124) -> Result<Address, StatelessValidationError> {
125    use secp256k1::{ecdsa::Signature as SecpSignature, Message, PublicKey, SECP256K1};
126
127    let public_key =
128        PublicKey::from_slice(vk).map_err(|_| StatelessValidationError::SignerRecovery)?;
129
130    let mut sig_bytes = [0u8; 64];
131    sig_bytes[0..32].copy_from_slice(&sig.r().to_be_bytes::<32>());
132    sig_bytes[32..64].copy_from_slice(&sig.s().to_be_bytes::<32>());
133
134    let signature = SecpSignature::from_compact(&sig_bytes)
135        .map_err(|_| StatelessValidationError::SignerRecovery)?;
136
137    let message = Message::from_digest(sig_hash.0);
138    SECP256K1
139        .verify_ecdsa(&message, &signature, &public_key)
140        .map_err(|_| StatelessValidationError::SignerRecovery)?;
141
142    Ok(Address::from_raw_public_key(&vk[1..]))
143}