Skip to content

Commit

Permalink
Use serde for loading genesis from TOML file
Browse files Browse the repository at this point in the history
A new `FromStringOrInteger` helper in `sequencer_utils` allows us
to hook the custom parsing logic for `FeeAmount`, `ChainId`, and
`BlockSize` into deserialization, so that all the types that include
these (`ChainConfig`, `Genesis`), we can use vanilla deserialization
without custom TOML parsing code.
  • Loading branch information
jbearer committed May 15, 2024
1 parent 0c2ea92 commit 5d5dec3
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 181 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion builder/src/non_permissioned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ impl BuilderConfig {
maximize_txns_count_timeout_duration,
instance_state
.chain_config
.base_fee()
.base_fee
.as_u64()
.context("the base fee exceeds the maximum amount that a builder can pay (defined by u64::MAX)")?,
Arc::new(instance_state),
Expand Down
2 changes: 1 addition & 1 deletion builder/src/permissioned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ impl<N: network::Type, P: SequencerPersistence, Ver: StaticVersionType + 'static
maximize_txns_count_timeout_duration,
instance_state
.chain_config
.base_fee()
.base_fee
.as_u64()
.context("the base fee exceeds the maximum amount that a builder can pay (defined by u64::MAX)")?,
Arc::new(instance_state),
Expand Down
4 changes: 2 additions & 2 deletions sequencer/src/block/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl<TableWord: TableWordTraits> Payload<TableWord> {
block_size += size_of::<TxTableEntry>() as u64;
}

if block_size > chain_config.max_block_size {
if block_size > *chain_config.max_block_size {
break;
}

Expand Down Expand Up @@ -373,7 +373,7 @@ mod test {

let n_txs = target_payload_total as u64 / tx_size;
let chain_config = ChainConfig {
max_block_size,
max_block_size: max_block_size.into(),
..Default::default()
};

Expand Down
138 changes: 67 additions & 71 deletions sequencer/src/chain_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,86 @@ use crate::{
options::parse_size,
state::{FeeAccount, FeeAmount},
};
use anyhow::{bail, Context};
use committable::{Commitment, Committable};
use derive_more::{From, Into};
use derive_more::{Deref, Display, From, Into};
use ethers::types::{Address, U256};
use itertools::Either;
use sequencer_utils::impl_to_fixed_bytes;
use sequencer_utils::{
impl_serde_from_string_or_integer, impl_to_fixed_bytes, serde::FromStringOrInteger,
};
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Default, Hash, Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, From, Into)]
#[derive(Default, Hash, Copy, Clone, Debug, Display, PartialEq, Eq, From, Into)]
#[display(fmt = "{_0}")]
pub struct ChainId(U256);

impl_serde_from_string_or_integer!(ChainId);
impl_to_fixed_bytes!(ChainId, U256);

impl FromStringOrInteger for ChainId {
type Binary = U256;
type Integer = u64;

fn from_binary(b: Self::Binary) -> anyhow::Result<Self> {
Ok(Self(b))
}

fn from_integer(i: Self::Integer) -> anyhow::Result<Self> {
Ok(i.into())
}

fn from_string(s: String) -> anyhow::Result<Self> {
if s.starts_with("0x") {
Ok(Self(U256::from_str(&s)?))
} else {
Ok(Self(U256::from_dec_str(&s)?))
}
}

fn to_binary(&self) -> anyhow::Result<Self::Binary> {
Ok(self.0)
}

fn to_string(&self) -> anyhow::Result<String> {
Ok(format!("{self}"))
}
}

impl From<u64> for ChainId {
fn from(id: u64) -> Self {
Self(id.into())
}
}

impl ChainId {
pub fn from_toml(toml: &toml::Value) -> anyhow::Result<Self> {
if let Some(s) = toml.as_str() {
if s.starts_with("0x") {
Ok(Self(U256::from_str(s)?))
} else {
Ok(Self(U256::from_dec_str(s)?))
}
} else if let Some(n) = toml.as_integer() {
Ok(u64::try_from(n)
.context("must be an unsigned integer")?
.into())
} else {
bail!("must be an integer or an integral string");
}
#[derive(Hash, Copy, Clone, Debug, Default, Display, PartialEq, Eq, From, Into, Deref)]
#[display(fmt = "{_0}")]
pub struct BlockSize(u64);

impl_serde_from_string_or_integer!(BlockSize);

impl FromStringOrInteger for BlockSize {
type Binary = u64;
type Integer = u64;

fn from_binary(b: Self::Binary) -> anyhow::Result<Self> {
Ok(Self(b))
}

fn from_integer(i: Self::Integer) -> anyhow::Result<Self> {
Ok(Self(i))
}

fn from_string(s: String) -> anyhow::Result<Self> {
Ok(parse_size(&s)?.into())
}

fn to_binary(&self) -> anyhow::Result<Self::Binary> {
Ok(self.0)
}

fn to_string(&self) -> anyhow::Result<String> {
Ok(format!("{self}"))
}
}

Expand All @@ -47,7 +92,7 @@ pub struct ChainConfig {
pub chain_id: ChainId,

/// Maximum size in bytes of a block
pub max_block_size: u64,
pub max_block_size: BlockSize,

/// Minimum fee in WEI per byte of payload
pub base_fee: FeeAmount,
Expand All @@ -71,63 +116,14 @@ impl Default for ChainConfig {
fn default() -> Self {
Self {
chain_id: U256::from(35353).into(), // arbitrarily chosen chain ID
max_block_size: 10240,
max_block_size: 10240.into(),
base_fee: 0.into(),
fee_contract: None,
fee_recipient: Default::default(),
}
}
}

impl ChainConfig {
pub fn from_toml(toml: &toml::Value) -> anyhow::Result<Self> {
let cfg = toml.as_table().context("must be table")?;
let chain_id = ChainId::from_toml(cfg.get("chain_id").context("missing chain_id")?)
.context("invalid chain ID")?;
let max_block_size = match cfg
.get("max_block_size")
.context("missing max_block_size")?
{
toml::Value::String(s) => parse_size(s).context("invalid max block size")?,
toml::Value::Integer(n) => (*n)
.try_into()
.context("max_block_size must be an unsigned integer")?,
_ => bail!("max_block_size must be an integer or an integral string"),
};
let base_fee = FeeAmount::from_toml(cfg.get("base_fee").context("missing base_fee")?)
.context("invalid base fee")?;
let fee_contract = match cfg.get("fee_contract") {
Some(toml::Value::String(s)) => {
Some(s.parse().context("invalid fee_contract address")?)
}
Some(_) => bail!("fee_contract must be an address string"),
None => None,
};
let fee_recipient = cfg
.get("fee_recipient")
.context("missing fee_recipient")?
.as_str()
.context("fee_recipient must be an address string")?
.parse()
.context("invalid fee_recipient")?;
Ok(Self {
chain_id,
max_block_size,
base_fee,
fee_contract,
fee_recipient,
})
}

pub fn max_block_size(&self) -> u64 {
self.max_block_size
}

pub fn base_fee(&self) -> FeeAmount {
self.base_fee
}
}

impl Committable for ChainConfig {
fn tag() -> String {
"CHAIN_CONFIG".to_string()
Expand All @@ -136,7 +132,7 @@ impl Committable for ChainConfig {
fn commit(&self) -> Commitment<Self> {
let comm = committable::RawCommitmentBuilder::new(&Self::tag())
.fixed_size_field("chain_id", &self.chain_id.to_fixed_bytes())
.u64_field("max_block_size", self.max_block_size)
.u64_field("max_block_size", *self.max_block_size)
.fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes())
.fixed_size_field("fee_recipient", &self.fee_recipient.to_fixed_bytes());
let comm = if let Some(addr) = self.fee_contract {
Expand Down
70 changes: 13 additions & 57 deletions sequencer/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::Path, str::FromStr};
use std::{collections::HashMap, path::Path};

/// Initial configuration of an Espresso stake table.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
Expand All @@ -17,62 +17,18 @@ pub struct StakeTableConfig {
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Genesis {
pub chain_config: ChainConfig,
pub stake_table: StakeTableConfig,
#[serde(default)]
pub accounts: HashMap<FeeAccount, FeeAmount>,
pub l1_finalized: Option<L1BlockInfo>,
pub stake_table: StakeTableConfig,
}

impl Genesis {
pub fn from_file(path: impl AsRef<Path>) -> anyhow::Result<Self> {
let path = path.as_ref();
let bytes = std::fs::read(path).context(format!("genesis file {}", path.display()))?;
let text = std::str::from_utf8(&bytes).context("genesis file must be UTF-8")?;
let toml: toml::Value = toml::from_str(text).context("malformed genesis file")?;
Self::from_toml(&toml).context("malformed genesis file")
}

pub fn from_toml(toml: &toml::Value) -> anyhow::Result<Self> {
let genesis = toml.as_table().context("must be a TOML table")?;
let chain_config = ChainConfig::from_toml(
genesis
.get("chain_config")
.context("missing chain_config section")?,
)
.context("invalid chain config section")?;
let accounts = match toml.get("accounts") {
Some(accounts) => {
let accounts = accounts
.as_table()
.context("accounts section must be a table")?;
accounts
.iter()
.map(|(account, value)| {
Ok((
FeeAccount::from_str(account)
.context(format!("invalid account {account}"))?,
FeeAmount::from_toml(value)
.context(format!("invalid value for account {account}"))?,
))
})
.collect::<anyhow::Result<_>>()?
}
None => Default::default(),
};
let l1_finalized = toml
.get("l1_finalized")
.map(|toml| L1BlockInfo::from_toml(toml).context("ivnalid L1 finalized block"))
.transpose()?;
let stake_table = toml::from_str(&toml::to_string(
toml.get("stake_table").context("missing stake_table")?,
)?)
.context("invalid stake table")?;

Ok(Self {
chain_config,
accounts,
l1_finalized,
stake_table,
})
toml::from_str(text).context("malformed genesis file")
}
}

Expand Down Expand Up @@ -104,15 +60,15 @@ mod test {
timestamp = "0x123def"
hash = "0x80f5dd11f2bdda2814cb1ad94ef30a47de02cf28ad68c89e104c00c4e51bb7a5"
}
.into();
.to_string();

let genesis = Genesis::from_toml(&toml).unwrap();
let genesis: Genesis = toml::from_str(&toml).unwrap();
assert_eq!(genesis.stake_table, StakeTableConfig { capacity: 10 });
assert_eq!(
genesis.chain_config,
ChainConfig {
chain_id: 12345.into(),
max_block_size: 30000,
max_block_size: 30000.into(),
base_fee: 1.into(),
fee_recipient: FeeAccount::default(),
fee_contract: Some(Address::default())
Expand Down Expand Up @@ -159,15 +115,15 @@ mod test {
base_fee = 1
fee_recipient = "0x0000000000000000000000000000000000000000"
}
.into();
.to_string();

let genesis = Genesis::from_toml(&toml).unwrap();
let genesis: Genesis = toml::from_str(&toml).unwrap();
assert_eq!(genesis.stake_table, StakeTableConfig { capacity: 10 });
assert_eq!(
genesis.chain_config,
ChainConfig {
chain_id: 12345.into(),
max_block_size: 30000,
max_block_size: 30000.into(),
base_fee: 1.into(),
fee_recipient: FeeAccount::default(),
fee_contract: None,
Expand All @@ -189,11 +145,11 @@ mod test {
base_fee = "1 gwei"
fee_recipient = "0x0000000000000000000000000000000000000000"
}
.into();
.to_string();

let genesis = Genesis::from_toml(&toml).unwrap();
let genesis: Genesis = toml::from_str(&toml).unwrap();
assert_eq!(genesis.stake_table, StakeTableConfig { capacity: 10 });
assert_eq!(genesis.chain_config.max_block_size, 30000000);
assert_eq!(*genesis.chain_config.max_block_size, 30000000);
assert_eq!(genesis.chain_config.base_fee, 1_000_000_000.into());
}
}
6 changes: 0 additions & 6 deletions sequencer/src/l1_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ pub struct L1BlockInfo {
pub hash: H256,
}

impl L1BlockInfo {
pub fn from_toml(toml: &toml::Value) -> anyhow::Result<Self> {
Ok(toml::from_str(&toml::to_string(toml)?)?)
}
}

impl PartialOrd for L1BlockInfo {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
Expand Down
Loading

0 comments on commit 5d5dec3

Please sign in to comment.