reth_rpc_eth_types/
utils.rs

1//! Commonly used code snippets
2
3use super::{EthApiError, EthResult};
4use reth_primitives_traits::{Recovered, SignedTransaction};
5use std::future::Future;
6
7/// Recovers a [`SignedTransaction`] from an enveloped encoded byte stream.
8///
9/// This is a helper function that returns the appropriate RPC-specific error if the input data is
10/// malformed.
11///
12/// This function uses [`alloy_eips::eip2718::Decodable2718::decode_2718_exact`] to ensure
13/// that the entire input buffer is consumed and no trailing bytes are allowed.
14///
15/// See [`alloy_eips::eip2718::Decodable2718::decode_2718_exact`]
16pub fn recover_raw_transaction<T: SignedTransaction>(data: &[u8]) -> EthResult<Recovered<T>> {
17    if data.is_empty() {
18        return Err(EthApiError::EmptyRawTransactionData)
19    }
20
21    let transaction =
22        T::decode_2718_exact(data).map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?;
23
24    SignedTransaction::try_into_recovered(transaction)
25        .or(Err(EthApiError::InvalidTransactionSignature))
26}
27
28/// Performs a binary search within a given block range to find the desired block number.
29///
30/// The binary search is performed by calling the provided asynchronous `check` closure on the
31/// blocks of the range. The closure should return a future representing the result of performing
32/// the desired logic at a given block. The future resolves to an `bool` where:
33/// - `true` indicates that the condition has been matched, but we can try to find a lower block to
34///   make the condition more matchable.
35/// - `false` indicates that the condition not matched, so the target is not present in the current
36///   block and should continue searching in a higher range.
37///
38/// Args:
39/// - `low`: The lower bound of the block range (inclusive).
40/// - `high`: The upper bound of the block range (inclusive).
41/// - `check`: A closure that performs the desired logic at a given block.
42pub async fn binary_search<F, Fut, E>(low: u64, high: u64, check: F) -> Result<u64, E>
43where
44    F: Fn(u64) -> Fut,
45    Fut: Future<Output = Result<bool, E>>,
46{
47    let mut low = low;
48    let mut high = high;
49    let mut num = high;
50
51    while low <= high {
52        let mid = (low + high) / 2;
53        if check(mid).await? {
54            high = mid - 1;
55            num = mid;
56        } else {
57            low = mid + 1
58        }
59    }
60
61    Ok(num)
62}
63
64/// Calculates the blob gas used ratio for a block, accounting for the case where
65/// `max_blob_gas_per_block` is zero.
66///
67/// Returns `0.0` if `blob_gas_used` is `0`, otherwise returns the ratio
68/// `blob_gas_used/max_blob_gas_per_block`.
69pub fn checked_blob_gas_used_ratio(blob_gas_used: u64, max_blob_gas_per_block: u64) -> f64 {
70    if blob_gas_used == 0 {
71        0.0
72    } else {
73        blob_gas_used as f64 / max_blob_gas_per_block as f64
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[tokio::test]
82    async fn test_binary_search() {
83        // in the middle
84        let num: Result<_, ()> =
85            binary_search(1, 10, |mid| Box::pin(async move { Ok(mid >= 5) })).await;
86        assert_eq!(num, Ok(5));
87
88        // in the upper
89        let num: Result<_, ()> =
90            binary_search(1, 10, |mid| Box::pin(async move { Ok(mid >= 7) })).await;
91        assert_eq!(num, Ok(7));
92
93        // in the lower
94        let num: Result<_, ()> =
95            binary_search(1, 10, |mid| Box::pin(async move { Ok(mid >= 1) })).await;
96        assert_eq!(num, Ok(1));
97
98        // higher than the upper
99        let num: Result<_, ()> =
100            binary_search(1, 10, |mid| Box::pin(async move { Ok(mid >= 11) })).await;
101        assert_eq!(num, Ok(10));
102    }
103
104    #[test]
105    fn test_checked_blob_gas_used_ratio() {
106        // No blob gas used, max blob gas per block is 0
107        assert_eq!(checked_blob_gas_used_ratio(0, 0), 0.0);
108        // Blob gas used is zero, max blob gas per block is non-zero
109        assert_eq!(checked_blob_gas_used_ratio(0, 100), 0.0);
110        // Blob gas used is non-zero, max blob gas per block is non-zero
111        assert_eq!(checked_blob_gas_used_ratio(50, 100), 0.5);
112        // Blob gas used is non-zero and equal to max blob gas per block
113        assert_eq!(checked_blob_gas_used_ratio(100, 100), 1.0);
114    }
115}