kona_protocol/info/
interop.rs

1//! Contains interop-specific L1 block info types.
2
3use crate::DecodeError;
4use alloc::vec::Vec;
5use alloy_primitives::{Address, B256, Bytes, U256};
6
7/// Represents the fields within an Interop L1 block info transaction.
8///
9/// Interop Binary Format
10/// +---------+--------------------------+
11/// | Bytes   | Field                    |
12/// +---------+--------------------------+
13/// | 4       | Function signature       |
14/// | 4       | BaseFeeScalar            |
15/// | 4       | BlobBaseFeeScalar        |
16/// | 8       | SequenceNumber           |
17/// | 8       | Timestamp                |
18/// | 8       | L1BlockNumber            |
19/// | 32      | BaseFee                  |
20/// | 32      | BlobBaseFee              |
21/// | 32      | BlockHash                |
22/// | 32      | BatcherHash              |
23/// +---------+--------------------------+
24#[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct L1BlockInfoInterop {
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 current blob base fee on L1
40    pub blob_base_fee: u128,
41    /// The fee scalar for L1 blobspace data
42    pub blob_base_fee_scalar: u32,
43    /// The fee scalar for L1 data
44    pub base_fee_scalar: u32,
45}
46
47impl L1BlockInfoInterop {
48    /// The type byte identifier for the L1 scalar format in Interop.
49    pub const L1_SCALAR: u8 = 1;
50
51    /// The length of an L1 info transaction in Interop.
52    pub const L1_INFO_TX_LEN: usize = 4 + 32 * 5;
53
54    /// The 4 byte selector of "setL1BlockValuesInterop()"
55    pub const L1_INFO_TX_SELECTOR: [u8; 4] = [0x76, 0x0e, 0xe0, 0x4d];
56
57    /// Encodes the [L1BlockInfoInterop] object into Ethereum transaction calldata.
58    pub fn encode_calldata(&self) -> Bytes {
59        let mut buf = Vec::with_capacity(Self::L1_INFO_TX_LEN);
60        buf.extend_from_slice(Self::L1_INFO_TX_SELECTOR.as_ref());
61        buf.extend_from_slice(self.base_fee_scalar.to_be_bytes().as_ref());
62        buf.extend_from_slice(self.blob_base_fee_scalar.to_be_bytes().as_ref());
63        buf.extend_from_slice(self.sequence_number.to_be_bytes().as_ref());
64        buf.extend_from_slice(self.time.to_be_bytes().as_ref());
65        buf.extend_from_slice(self.number.to_be_bytes().as_ref());
66        buf.extend_from_slice(U256::from(self.base_fee).to_be_bytes::<32>().as_ref());
67        buf.extend_from_slice(U256::from(self.blob_base_fee).to_be_bytes::<32>().as_ref());
68        buf.extend_from_slice(self.block_hash.as_ref());
69        buf.extend_from_slice(self.batcher_address.into_word().as_ref());
70        buf.into()
71    }
72
73    /// Decodes the [L1BlockInfoInterop] object from ethereum transaction calldata.
74    pub fn decode_calldata(r: &[u8]) -> Result<Self, DecodeError> {
75        if r.len() != Self::L1_INFO_TX_LEN {
76            return Err(DecodeError::InvalidInteropLength(Self::L1_INFO_TX_LEN, r.len()));
77        }
78
79        // SAFETY: For all below slice operations, the full
80        //         length is validated above to be `164`.
81
82        // SAFETY: 4 bytes are copied directly into the array
83        let mut base_fee_scalar = [0u8; 4];
84        base_fee_scalar.copy_from_slice(&r[4..8]);
85        let base_fee_scalar = u32::from_be_bytes(base_fee_scalar);
86
87        // SAFETY: 4 bytes are copied directly into the array
88        let mut blob_base_fee_scalar = [0u8; 4];
89        blob_base_fee_scalar.copy_from_slice(&r[8..12]);
90        let blob_base_fee_scalar = u32::from_be_bytes(blob_base_fee_scalar);
91
92        // SAFETY: 8 bytes are copied directly into the array
93        let mut sequence_number = [0u8; 8];
94        sequence_number.copy_from_slice(&r[12..20]);
95        let sequence_number = u64::from_be_bytes(sequence_number);
96
97        // SAFETY: 8 bytes are copied directly into the array
98        let mut time = [0u8; 8];
99        time.copy_from_slice(&r[20..28]);
100        let time = u64::from_be_bytes(time);
101
102        // SAFETY: 8 bytes are copied directly into the array
103        let mut number = [0u8; 8];
104        number.copy_from_slice(&r[28..36]);
105        let number = u64::from_be_bytes(number);
106
107        // SAFETY: 8 bytes are copied directly into the array
108        let mut base_fee = [0u8; 8];
109        base_fee.copy_from_slice(&r[60..68]);
110        let base_fee = u64::from_be_bytes(base_fee);
111
112        // SAFETY: 16 bytes are copied directly into the array
113        let mut blob_base_fee = [0u8; 16];
114        blob_base_fee.copy_from_slice(&r[84..100]);
115        let blob_base_fee = u128::from_be_bytes(blob_base_fee);
116
117        let block_hash = B256::from_slice(r[100..132].as_ref());
118        let batcher_address = Address::from_slice(r[144..164].as_ref());
119
120        Ok(Self {
121            number,
122            time,
123            base_fee,
124            block_hash,
125            sequence_number,
126            batcher_address,
127            blob_base_fee,
128            blob_base_fee_scalar,
129            base_fee_scalar,
130        })
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use alloc::vec;
138
139    #[test]
140    fn test_decode_calldata_interop_invalid_length() {
141        let r = vec![0u8; 1];
142        assert_eq!(
143            L1BlockInfoInterop::decode_calldata(&r),
144            Err(DecodeError::InvalidInteropLength(L1BlockInfoInterop::L1_INFO_TX_LEN, r.len(),))
145        );
146    }
147
148    #[test]
149    fn test_l1_block_info_interop_roundtrip_calldata_encoding() {
150        let info = L1BlockInfoInterop {
151            number: 1,
152            time: 2,
153            base_fee: 3,
154            block_hash: B256::from([4u8; 32]),
155            sequence_number: 5,
156            batcher_address: Address::from([6u8; 20]),
157            blob_base_fee: 7,
158            blob_base_fee_scalar: 8,
159            base_fee_scalar: 9,
160        };
161
162        let calldata = info.encode_calldata();
163        let decoded_info = L1BlockInfoInterop::decode_calldata(&calldata).unwrap();
164        assert_eq!(info, decoded_info);
165    }
166}