kona_protocol/info/
isthmus.rs

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