-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(protocol): Refactor Block Info Txs (#303)
### Description Refactors the `block_info.rs` module into a new `info/` module that makes the `L1BlockInfoTx` more extensible. Closes #302
- Loading branch information
Showing
7 changed files
with
318 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
//! Contains bedrock-specific L1 block info types. | ||
use alloc::{format, string::ToString, vec::Vec}; | ||
use alloy_primitives::{Address, Bytes, B256, U256}; | ||
|
||
use crate::DecodeError; | ||
|
||
/// Represents the fields within a Bedrock L1 block info transaction. | ||
/// | ||
/// Bedrock Binary Format | ||
// +---------+--------------------------+ | ||
// | Bytes | Field | | ||
// +---------+--------------------------+ | ||
// | 4 | Function signature | | ||
// | 32 | Number | | ||
// | 32 | Time | | ||
// | 32 | BaseFee | | ||
// | 32 | BlockHash | | ||
// | 32 | SequenceNumber | | ||
// | 32 | BatcherHash | | ||
// | 32 | L1FeeOverhead | | ||
// | 32 | L1FeeScalar | | ||
// +---------+--------------------------+ | ||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)] | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
pub struct L1BlockInfoBedrock { | ||
/// The current L1 origin block number | ||
pub number: u64, | ||
/// The current L1 origin block's timestamp | ||
pub time: u64, | ||
/// The current L1 origin block's basefee | ||
pub base_fee: u64, | ||
/// The current L1 origin block's hash | ||
pub block_hash: B256, | ||
/// The current sequence number | ||
pub sequence_number: u64, | ||
/// The address of the batch submitter | ||
pub batcher_address: Address, | ||
/// The fee overhead for L1 data | ||
pub l1_fee_overhead: U256, | ||
/// The fee scalar for L1 data | ||
pub l1_fee_scalar: U256, | ||
} | ||
|
||
impl L1BlockInfoBedrock { | ||
/// The length of an L1 info transaction in Bedrock. | ||
pub const L1_INFO_TX_LEN: usize = 4 + 32 * 8; | ||
|
||
/// The 4 byte selector of the | ||
/// "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" function | ||
pub const L1_INFO_TX_SELECTOR: [u8; 4] = [0x01, 0x5d, 0x8e, 0xb9]; | ||
|
||
/// Encodes the [L1BlockInfoBedrock] object into Ethereum transaction calldata. | ||
pub fn encode_calldata(&self) -> Bytes { | ||
let mut buf = Vec::with_capacity(Self::L1_INFO_TX_LEN); | ||
buf.extend_from_slice(Self::L1_INFO_TX_SELECTOR.as_ref()); | ||
buf.extend_from_slice(U256::from(self.number).to_be_bytes::<32>().as_slice()); | ||
buf.extend_from_slice(U256::from(self.time).to_be_bytes::<32>().as_slice()); | ||
buf.extend_from_slice(U256::from(self.base_fee).to_be_bytes::<32>().as_slice()); | ||
buf.extend_from_slice(self.block_hash.as_slice()); | ||
buf.extend_from_slice(U256::from(self.sequence_number).to_be_bytes::<32>().as_slice()); | ||
buf.extend_from_slice(self.batcher_address.into_word().as_slice()); | ||
buf.extend_from_slice(self.l1_fee_overhead.to_be_bytes::<32>().as_slice()); | ||
buf.extend_from_slice(self.l1_fee_scalar.to_be_bytes::<32>().as_slice()); | ||
buf.into() | ||
} | ||
|
||
/// Decodes the [L1BlockInfoBedrock] object from ethereum transaction calldata. | ||
pub fn decode_calldata(r: &[u8]) -> Result<Self, DecodeError> { | ||
if r.len() != Self::L1_INFO_TX_LEN { | ||
return Err(DecodeError::InvalidLength(format!( | ||
"Invalid calldata length for Bedrock L1 info transaction, expected {}, got {}", | ||
Self::L1_INFO_TX_LEN, | ||
r.len() | ||
))); | ||
} | ||
|
||
let number = u64::from_be_bytes( | ||
r[28..36] | ||
.try_into() | ||
.map_err(|_| DecodeError::ParseError("Conversion error for number".to_string()))?, | ||
); | ||
let time = u64::from_be_bytes( | ||
r[60..68] | ||
.try_into() | ||
.map_err(|_| DecodeError::ParseError("Conversion error for time".to_string()))?, | ||
); | ||
let base_fee = | ||
u64::from_be_bytes(r[92..100].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for base fee".to_string()) | ||
})?); | ||
let block_hash = B256::from_slice(r[100..132].as_ref()); | ||
let sequence_number = u64::from_be_bytes(r[156..164].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for sequence number".to_string()) | ||
})?); | ||
let batcher_address = Address::from_slice(r[176..196].as_ref()); | ||
let l1_fee_overhead = U256::from_be_slice(r[196..228].as_ref()); | ||
let l1_fee_scalar = U256::from_be_slice(r[228..260].as_ref()); | ||
|
||
Ok(Self { | ||
number, | ||
time, | ||
base_fee, | ||
block_hash, | ||
sequence_number, | ||
batcher_address, | ||
l1_fee_overhead, | ||
l1_fee_scalar, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
//! Contains ecotone-specific L1 block info types. | ||
use alloc::{format, string::ToString, vec::Vec}; | ||
use alloy_primitives::{Address, Bytes, B256, U256}; | ||
|
||
use crate::DecodeError; | ||
|
||
/// Represents the fields within an Ecotone L1 block info transaction. | ||
/// | ||
/// Ecotone Binary Format | ||
/// +---------+--------------------------+ | ||
/// | Bytes | Field | | ||
/// +---------+--------------------------+ | ||
/// | 4 | Function signature | | ||
/// | 4 | BaseFeeScalar | | ||
/// | 4 | BlobBaseFeeScalar | | ||
/// | 8 | SequenceNumber | | ||
/// | 8 | Timestamp | | ||
/// | 8 | L1BlockNumber | | ||
/// | 32 | BaseFee | | ||
/// | 32 | BlobBaseFee | | ||
/// | 32 | BlockHash | | ||
/// | 32 | BatcherHash | | ||
/// +---------+--------------------------+ | ||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)] | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
pub struct L1BlockInfoEcotone { | ||
/// The current L1 origin block number | ||
pub number: u64, | ||
/// The current L1 origin block's timestamp | ||
pub time: u64, | ||
/// The current L1 origin block's basefee | ||
pub base_fee: u64, | ||
/// The current L1 origin block's hash | ||
pub block_hash: B256, | ||
/// The current sequence number | ||
pub sequence_number: u64, | ||
/// The address of the batch submitter | ||
pub batcher_address: Address, | ||
/// The current blob base fee on L1 | ||
pub blob_base_fee: u128, | ||
/// The fee scalar for L1 blobspace data | ||
pub blob_base_fee_scalar: u32, | ||
/// The fee scalar for L1 data | ||
pub base_fee_scalar: u32, | ||
} | ||
|
||
impl L1BlockInfoEcotone { | ||
/// The type byte identifier for the L1 scalar format in Ecotone. | ||
pub const L1_SCALAR: u8 = 1; | ||
|
||
/// The length of an L1 info transaction in Ecotone. | ||
pub const L1_INFO_TX_LEN: usize = 4 + 32 * 5; | ||
|
||
/// The 4 byte selector of "setL1BlockValuesEcotone()" | ||
pub const L1_INFO_TX_SELECTOR: [u8; 4] = [0x44, 0x0a, 0x5e, 0x20]; | ||
|
||
/// Encodes the [L1BlockInfoEcotone] object into Ethereum transaction calldata. | ||
pub fn encode_calldata(&self) -> Bytes { | ||
let mut buf = Vec::with_capacity(Self::L1_INFO_TX_LEN); | ||
buf.extend_from_slice(Self::L1_INFO_TX_SELECTOR.as_ref()); | ||
buf.extend_from_slice(self.base_fee_scalar.to_be_bytes().as_ref()); | ||
buf.extend_from_slice(self.blob_base_fee_scalar.to_be_bytes().as_ref()); | ||
buf.extend_from_slice(self.sequence_number.to_be_bytes().as_ref()); | ||
buf.extend_from_slice(self.time.to_be_bytes().as_ref()); | ||
buf.extend_from_slice(self.number.to_be_bytes().as_ref()); | ||
buf.extend_from_slice(U256::from(self.base_fee).to_be_bytes::<32>().as_ref()); | ||
buf.extend_from_slice(U256::from(self.blob_base_fee).to_be_bytes::<32>().as_ref()); | ||
buf.extend_from_slice(self.block_hash.as_ref()); | ||
buf.extend_from_slice(self.batcher_address.into_word().as_ref()); | ||
buf.into() | ||
} | ||
|
||
/// Decodes the [L1BlockInfoEcotone] object from ethereum transaction calldata. | ||
pub fn decode_calldata(r: &[u8]) -> Result<Self, DecodeError> { | ||
if r.len() != Self::L1_INFO_TX_LEN { | ||
return Err(DecodeError::InvalidLength(format!( | ||
"Invalid calldata length for Ecotone L1 info transaction, expected {}, got {}", | ||
Self::L1_INFO_TX_LEN, | ||
r.len() | ||
))); | ||
} | ||
let base_fee_scalar = u32::from_be_bytes(r[4..8].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for base fee scalar".to_string()) | ||
})?); | ||
let blob_base_fee_scalar = u32::from_be_bytes(r[8..12].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for blob base fee scalar".to_string()) | ||
})?); | ||
let sequence_number = u64::from_be_bytes(r[12..20].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for sequence number".to_string()) | ||
})?); | ||
let timestamp = | ||
u64::from_be_bytes(r[20..28].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for timestamp".to_string()) | ||
})?); | ||
let l1_block_number = u64::from_be_bytes(r[28..36].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for L1 block number".to_string()) | ||
})?); | ||
let base_fee = | ||
u64::from_be_bytes(r[60..68].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for base fee".to_string()) | ||
})?); | ||
let blob_base_fee = u128::from_be_bytes(r[84..100].try_into().map_err(|_| { | ||
DecodeError::ParseError("Conversion error for blob base fee".to_string()) | ||
})?); | ||
let block_hash = B256::from_slice(r[100..132].as_ref()); | ||
let batcher_address = Address::from_slice(r[144..164].as_ref()); | ||
|
||
Ok(Self { | ||
number: l1_block_number, | ||
time: timestamp, | ||
base_fee, | ||
block_hash, | ||
sequence_number, | ||
batcher_address, | ||
blob_base_fee, | ||
blob_base_fee_scalar, | ||
base_fee_scalar, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
//! Contains error types specific to the L1 block info transaction. | ||
use alloc::string::String; | ||
|
||
/// An error type for parsing L1 block info transactions. | ||
#[derive(Debug, thiserror::Error, Copy, Clone)] | ||
pub enum BlockInfoError { | ||
/// Failed to parse the L1 blob base fee scalar. | ||
L1BlobBaseFeeScalar, | ||
/// Failed to parse the base fee scalar. | ||
BaseFeeScalar, | ||
/// Failed to parse the EIP-1559 denominator. | ||
Eip1559Denominator, | ||
/// Failed to parse the EIP-1559 elasticity parameter. | ||
Eip1559Elasticity, | ||
} | ||
|
||
impl core::fmt::Display for BlockInfoError { | ||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
match self { | ||
Self::L1BlobBaseFeeScalar => { | ||
write!(f, "Failed to parse the L1 blob base fee scalar") | ||
} | ||
Self::BaseFeeScalar => write!(f, "Failed to parse the base fee scalar"), | ||
Self::Eip1559Denominator => { | ||
write!(f, "Failed to parse the EIP-1559 denominator") | ||
} | ||
Self::Eip1559Elasticity => { | ||
write!(f, "Failed to parse the EIP-1559 elasticity parameter") | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// An error decoding an L1 block info transaction. | ||
#[derive(Debug, thiserror::Error)] | ||
pub enum DecodeError { | ||
/// Invalid selector for the L1 info transaction | ||
InvalidSelector, | ||
/// Parse error for the L1 info transaction | ||
ParseError(String), | ||
/// Invalid length for the L1 info transaction | ||
InvalidLength(String), | ||
} | ||
|
||
impl core::fmt::Display for DecodeError { | ||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
match self { | ||
Self::InvalidSelector => write!(f, "Invalid L1 info transaction selector"), | ||
Self::ParseError(msg) => write!(f, "Parse error: {}", msg), | ||
Self::InvalidLength(msg) => write!(f, "Invalid data length: {}", msg), /* Handle display for length errors */ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
//! Module containing L1 Attributes types (aka the L1 block info transaction). | ||
mod variant; | ||
pub use variant::L1BlockInfoTx; | ||
|
||
mod bedrock; | ||
pub use bedrock::L1BlockInfoBedrock; | ||
|
||
mod ecotone; | ||
pub use ecotone::L1BlockInfoEcotone; | ||
|
||
mod errors; | ||
pub use errors::{BlockInfoError, DecodeError}; |
Oops, something went wrong.