alloy_eips/
eip7685.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! [EIP-7685]: General purpose execution layer requests
//!
//! [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685

use alloc::vec::Vec;
use alloy_primitives::{b256, Bytes, B256};
use derive_more::{Deref, DerefMut, From, IntoIterator};

/// The empty requests hash.
///
/// This is equivalent to `sha256(sha256(0) ++ sha256(1) ++ sha256(2))`
pub const EMPTY_REQUESTS_HASH: B256 =
    b256!("6036c41849da9c076ed79654d434017387a88fb833c2856b32e18218b3341c5f");

/// A container of EIP-7685 requests.
///
/// The container only holds the `requests_data` as defined by their respective EIPs. The request
/// type is prepended to `requests_data` in [`Requests::requests_hash`] to calculate the requests
/// hash as definned in EIP-7685.
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, Deref, DerefMut, From, IntoIterator)]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Requests(Vec<Bytes>);

impl Requests {
    /// Construct a new [`Requests`] container.
    pub const fn new(requests: Vec<Bytes>) -> Self {
        Self(requests)
    }

    /// Add a new request into the container.
    pub fn push_request(&mut self, request: Bytes) {
        self.0.push(request);
    }

    /// Consumes [`Requests`] and returns the inner raw opaque requests.
    ///
    /// # Note
    ///
    /// These requests are only the `requests_data` without the `request_type`.
    pub fn take(self) -> Vec<Bytes> {
        self.0
    }

    /// Get an iterator over the requests.
    pub fn iter(&self) -> core::slice::Iter<'_, Bytes> {
        self.0.iter()
    }

    /// Calculate the requests hash as defined in EIP-7685 for the requests.
    ///
    /// The requests hash is defined as
    ///
    /// ```text
    /// sha256(sha256(requests_0) ++ sha256(requests_1) ++ ...)
    /// ```
    ///
    /// The request type for each requests is prepended to the `requests_data` inside of this
    /// container. The request type for the first request in the container will be `0x00`, the
    /// second request will have type `0x01`, and so on.
    #[cfg(feature = "sha2")]
    pub fn requests_hash(&self) -> B256 {
        use sha2::{Digest, Sha256};
        let mut hash = Sha256::new();
        for (ty, req) in self.0.iter().enumerate() {
            let mut req_hash = Sha256::new();
            req_hash.update([ty as u8]);
            req_hash.update(req);
            hash.update(req_hash.finalize());
        }
        B256::from(hash.finalize().as_ref())
    }

    /// Extend this container with requests from another container.
    pub fn extend(&mut self, other: Self) {
        self.0.extend(other.take());
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_extend() {
        // Test extending a Requests container with another Requests container
        let mut reqs1 = Requests::new(vec![Bytes::from(vec![0x01, 0x02])]);
        let reqs2 =
            Requests::new(vec![Bytes::from(vec![0x03, 0x04]), Bytes::from(vec![0x05, 0x06])]);

        // Extend reqs1 with reqs2
        reqs1.extend(reqs2);

        // Ensure the requests are correctly combined
        assert_eq!(reqs1.0.len(), 3);
        assert_eq!(
            reqs1.0,
            vec![
                Bytes::from(vec![0x01, 0x02]),
                Bytes::from(vec![0x03, 0x04]),
                Bytes::from(vec![0x05, 0x06])
            ]
        );
    }

    #[test]
    #[cfg(feature = "sha2")]
    fn test_consistent_requests_hash() {
        // We test that the empty requests hash is consistent with the EIP-7685 definition.
        assert_eq!(
            Requests(vec![Bytes::from(vec![]), Bytes::from(vec![]), Bytes::from(vec![])])
                .requests_hash(),
            EMPTY_REQUESTS_HASH,
        );

        // Test to hash a non-empty vector of requests.
        assert_eq!(
            Requests(vec![
                Bytes::from(vec![0x0a, 0x0b, 0x0c]),
                Bytes::from(vec![0x0d, 0x0e, 0x0f])
            ])
            .requests_hash(),
            b256!("be3a57667b9bb9e0275019c0faf0f415fdc8385a408fd03e13a5c50615e3530c"),
        );
    }
}