kona_protocol/info/
bedrock.rs

1//! Contains bedrock-specific L1 block info types.
2
3use alloc::vec::Vec;
4use alloy_primitives::{Address, B256, Bytes, U256};
5
6use crate::DecodeError;
7
8/// Represents the fields within a Bedrock L1 block info transaction.
9///
10/// Bedrock Binary Format
11// +---------+--------------------------+
12// | Bytes   | Field                    |
13// +---------+--------------------------+
14// | 4       | Function signature       |
15// | 32      | Number                   |
16// | 32      | Time                     |
17// | 32      | BaseFee                  |
18// | 32      | BlockHash                |
19// | 32      | SequenceNumber           |
20// | 32      | BatcherHash              |
21// | 32      | L1FeeOverhead            |
22// | 32      | L1FeeScalar              |
23// +---------+--------------------------+
24#[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct L1BlockInfoBedrock {
27    /// The current L1 origin block number
28    pub number: u64,
29    /// The current L1 origin block's timestamp
30    pub time: u64,
31    /// The current L1 origin block's basefee
32    pub base_fee: u64,
33    /// The current L1 origin block's hash
34    pub block_hash: B256,
35    /// The current sequence number
36    pub sequence_number: u64,
37    /// The address of the batch submitter
38    pub batcher_address: Address,
39    /// The fee overhead for L1 data
40    pub l1_fee_overhead: U256,
41    /// The fee scalar for L1 data
42    pub l1_fee_scalar: U256,
43}
44
45impl L1BlockInfoBedrock {
46    /// The length of an L1 info transaction in Bedrock.
47    pub const L1_INFO_TX_LEN: usize = 4 + 32 * 8;
48
49    /// The 4 byte selector of the
50    /// "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" function
51    pub const L1_INFO_TX_SELECTOR: [u8; 4] = [0x01, 0x5d, 0x8e, 0xb9];
52
53    /// Encodes the [`L1BlockInfoBedrock`] object into Ethereum transaction calldata.
54    pub fn encode_calldata(&self) -> Bytes {
55        let mut buf = Vec::with_capacity(Self::L1_INFO_TX_LEN);
56        buf.extend_from_slice(Self::L1_INFO_TX_SELECTOR.as_ref());
57        buf.extend_from_slice(U256::from(self.number).to_be_bytes::<32>().as_slice());
58        buf.extend_from_slice(U256::from(self.time).to_be_bytes::<32>().as_slice());
59        buf.extend_from_slice(U256::from(self.base_fee).to_be_bytes::<32>().as_slice());
60        buf.extend_from_slice(self.block_hash.as_slice());
61        buf.extend_from_slice(U256::from(self.sequence_number).to_be_bytes::<32>().as_slice());
62        buf.extend_from_slice(self.batcher_address.into_word().as_slice());
63        buf.extend_from_slice(self.l1_fee_overhead.to_be_bytes::<32>().as_slice());
64        buf.extend_from_slice(self.l1_fee_scalar.to_be_bytes::<32>().as_slice());
65        buf.into()
66    }
67
68    /// Decodes the [`L1BlockInfoBedrock`] object from ethereum transaction calldata.
69    pub fn decode_calldata(r: &[u8]) -> Result<Self, DecodeError> {
70        if r.len() != Self::L1_INFO_TX_LEN {
71            return Err(DecodeError::InvalidBedrockLength(Self::L1_INFO_TX_LEN, r.len()));
72        }
73
74        // SAFETY: For all below slice operations, the full
75        //         length is validated above to be `260`.
76
77        // SAFETY: 8 bytes are copied directly into the array
78        let mut number = [0u8; 8];
79        number.copy_from_slice(&r[28..36]);
80        let number = u64::from_be_bytes(number);
81
82        // SAFETY: 8 bytes are copied directly into the array
83        let mut time = [0u8; 8];
84        time.copy_from_slice(&r[60..68]);
85        let time = u64::from_be_bytes(time);
86
87        // SAFETY: 8 bytes are copied directly into the array
88        let mut base_fee = [0u8; 8];
89        base_fee.copy_from_slice(&r[92..100]);
90        let base_fee = u64::from_be_bytes(base_fee);
91
92        let block_hash = B256::from_slice(r[100..132].as_ref());
93
94        // SAFETY: 8 bytes are copied directly into the array
95        let mut sequence_number = [0u8; 8];
96        sequence_number.copy_from_slice(&r[156..164]);
97        let sequence_number = u64::from_be_bytes(sequence_number);
98
99        let batcher_address = Address::from_slice(r[176..196].as_ref());
100        let l1_fee_overhead = U256::from_be_slice(r[196..228].as_ref());
101        let l1_fee_scalar = U256::from_be_slice(r[228..260].as_ref());
102
103        Ok(Self {
104            number,
105            time,
106            base_fee,
107            block_hash,
108            sequence_number,
109            batcher_address,
110            l1_fee_overhead,
111            l1_fee_scalar,
112        })
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use alloc::vec;
120
121    #[test]
122    fn test_decode_calldata_bedrock_invalid_length() {
123        let r = vec![0u8; 1];
124        assert_eq!(
125            L1BlockInfoBedrock::decode_calldata(&r),
126            Err(DecodeError::InvalidBedrockLength(L1BlockInfoBedrock::L1_INFO_TX_LEN, r.len(),))
127        );
128    }
129
130    #[test]
131    fn test_l1_block_info_bedrock_roundtrip_calldata_encoding() {
132        let info = L1BlockInfoBedrock {
133            number: 1,
134            time: 2,
135            base_fee: 3,
136            block_hash: B256::from([4u8; 32]),
137            sequence_number: 5,
138            batcher_address: Address::from([6u8; 20]),
139            l1_fee_overhead: U256::from(7),
140            l1_fee_scalar: U256::from(8),
141        };
142
143        let calldata = info.encode_calldata();
144        let decoded_info = L1BlockInfoBedrock::decode_calldata(&calldata).unwrap();
145        assert_eq!(info, decoded_info);
146    }
147}