Skip to content

Commit

Permalink
Save burned fees in a burner account
Browse files Browse the repository at this point in the history
This will allow the underlying asset to be conserved, and we can
decide what to do with the burnt fees at a later point.
  • Loading branch information
jbearer committed May 7, 2024
1 parent 9de1fd5 commit 56c7ee7
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 81 deletions.
2 changes: 1 addition & 1 deletion builder/src/bin/permissionless-builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ async fn main() -> anyhow::Result<()> {
chain_id: opt.chain_id.into(),
max_block_size: opt.max_block_size,
base_fee: opt.base_fee.into(),
fee_contract: None,
..Default::default()
};
let instance_state =
build_instance_state(l1_params, opt.state_peers, chain_config, sequencer_version).unwrap();
Expand Down
3 changes: 2 additions & 1 deletion data/header.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"chain_id": "0x8a19",
"max_block_size": 10240,
"base_fee": "0x0",
"fee_contract": "0xa15bb66138824a1c7167f5e85b957d04dd34e468"
"fee_contract": "0xa15bb66138824a1c7167f5e85b957d04dd34e468",
"fee_burn_account": "0x0000000000000000000000000000000000000000"
} } }
}
2 changes: 1 addition & 1 deletion sequencer/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ mod reference {
fn test_reference_header() {
reference_test::<Header, _>(
HEADER.clone(),
"BLOCK~KOFxIeSaxl85Uwwo71iutIG3EMGShjfsrUXnKWZXWXcI",
"BLOCK~5eR1V-KaEtGxKjC7adxnKnJ2hJQ3kqBqISWHLBhOYgrY",
|header| header.commit(),
);
}
Expand Down
34 changes: 30 additions & 4 deletions sequencer/src/chain_config.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use crate::state::FeeAmount;
use crate::{
options::parse_size,
state::{FeeAccount, FeeAmount},
};
use clap::Args;
use committable::{Commitment, Committable};
use derive_more::{From, Into};
use ethers::types::{Address, U256};
use itertools::Either;
use sequencer_utils::impl_to_fixed_bytes;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Default, Hash, Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, From, Into)]
pub struct ChainId(U256);
Expand All @@ -17,21 +22,40 @@ impl From<u64> for ChainId {
}
}

impl FromStr for ChainId {
type Err = <u64 as FromStr>::Err;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(u64::from_str(s)?.into())
}
}

/// Global variables for an Espresso blockchain.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Args, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ChainConfig {
/// Espresso chain ID
#[clap(long, env = "ESPRESSO_SEQUENCER_CHAIN_ID", default_value = "0")]
pub chain_id: ChainId,
/// Maximum size in bytes of a block
#[clap(long, env = "ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE", default_value = "30mb", value_parser = parse_size)]
pub max_block_size: u64,
/// Minimum fee in WEI per byte of payload
#[clap(long, env = "ESPRESSO_SEQUENCER_BASE_FEE", default_value = "0")]
pub base_fee: FeeAmount,
/// Fee contract address on L1.
///
/// This is optional so that fees can easily be toggled on/off, with no need to deploy a
/// contract when they are off. In a future release, after fees are switched on and thoroughly
/// tested, this may be made mandatory.
#[clap(long, env = "ESPRESSO_SEQUENCER_FEE_CONTRACT_PROXY_ADDRESS")]
pub fee_contract: Option<Address>,
/// Account that receives sequencing fees.
#[clap(
long,
env = "ESPRESSO_SEQUENCER_FEE_BURN_ACCOUNT",
default_value = "0x0000000000000000000000000000000000000000"
)]
pub fee_burn_account: FeeAccount,
}

impl Default for ChainConfig {
Expand All @@ -41,6 +65,7 @@ impl Default for ChainConfig {
max_block_size: 10240,
base_fee: 0.into(),
fee_contract: None,
fee_burn_account: Default::default(),
}
}
}
Expand All @@ -54,7 +79,8 @@ impl Committable for ChainConfig {
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)
.fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes());
.fixed_size_field("base_fee", &self.base_fee.to_fixed_bytes())
.fixed_size_field("fee_burn_account", &self.fee_burn_account.to_fixed_bytes());
let comm = if let Some(addr) = self.fee_contract {
comm.u64_field("fee_contract", 1).fixed_size_bytes(&addr.0)
} else {
Expand Down Expand Up @@ -117,7 +143,7 @@ mod tests {
chain_id,
max_block_size,
base_fee: 1.into(),
fee_contract: None,
..Default::default()
};
assert!(chain_config != other_config);
}
Expand Down
55 changes: 32 additions & 23 deletions sequencer/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl Header {

// Charge the builder fee.
state
.charge_fee(builder_fee)
.burn_fee(builder_fee, chain_config.fee_burn_account)
.context(format!("invalid builder fee {builder_fee:?}"))?;

let fee_merkle_tree_root = state.fee_merkle_tree.commitment();
Expand Down Expand Up @@ -299,10 +299,6 @@ impl BlockHeader<SeqTypes> for Header {
.context("invalid builder signature")?,
);

// Figure out which accounts we need to fetch, in case we are missing some state needed to
// construct the header.
let accounts = std::iter::once(builder_account);

// Fetch the latest L1 snapshot.
let l1_snapshot = instance_state.l1_client().snapshot().await;
// Fetch the new L1 deposits between parent and current finalized L1 block.
Expand All @@ -323,9 +319,14 @@ impl BlockHeader<SeqTypes> for Header {
} else {
vec![]
};
// Find missing fee state entries
let missing_accounts = parent_state
.forgotten_accounts(accounts.chain(l1_deposits.iter().map(|info| info.account())));
// Find missing fee state entries. We will need to use the builder account which is paying a
// fee and the burn account which is receiving it, plus any counts receiving deposits in
// this block.
let missing_accounts = parent_state.forgotten_accounts(
[builder_account, chain_config.fee_burn_account]
.into_iter()
.chain(l1_deposits.iter().map(|info| info.account())),
);
if !missing_accounts.is_empty() {
tracing::warn!(
height,
Expand Down Expand Up @@ -457,10 +458,7 @@ mod test_headers {
catchup::mock::MockStateCatchup,
eth_signature_key::EthKeyPair,
l1_client::L1Client,
state::{
apply_proposal, get_l1_deposits, validate_proposal, BlockMerkleTree, Delta,
FeeMerkleTree,
},
state::{validate_proposal, BlockMerkleTree, FeeMerkleTree},
NodeState,
};
use async_compatibility_layer::logging::{setup_backtrace, setup_logging};
Expand Down Expand Up @@ -741,8 +739,8 @@ mod test_headers {
}
}

#[test]
fn test_validate_proposal_error_cases() {
#[async_std::test]
async fn test_validate_proposal_error_cases() {
let genesis = GenesisForTest::default();
let vid_common = vid_scheme(1).disperse([]).unwrap().common;

Expand All @@ -760,9 +758,12 @@ mod test_headers {
parent_header.block_merkle_tree_root = block_merkle_tree_root;
let mut proposal = parent_header.clone();

let mut delta = Delta::default();
// Pass a different chain config to trigger a chain config validation error.
let state = apply_proposal(&validated_state, &mut delta, &parent_leaf, vec![]);
let state = validated_state
.apply_header(&genesis.instance_state, &parent_leaf, &proposal)
.await
.unwrap()
.0;

let result = validate_proposal(
&state,
Expand All @@ -780,7 +781,11 @@ mod test_headers {

// Advance `proposal.height` to trigger validation error.

let validated_state = apply_proposal(&validated_state, &mut delta, &parent_leaf, vec![]);
let validated_state = validated_state
.apply_header(&genesis.instance_state, &parent_leaf, &proposal)
.await
.unwrap()
.0;
let result = validate_proposal(
&validated_state,
genesis.instance_state.chain_config,
Expand All @@ -797,7 +802,11 @@ mod test_headers {
// proposed `Header` root should include parent + parent.commit
proposal.height += 1;

let validated_state = apply_proposal(&validated_state, &mut delta, &parent_leaf, vec![]);
let validated_state = validated_state
.apply_header(&genesis.instance_state, &parent_leaf, &proposal)
.await
.unwrap()
.0;

let result = validate_proposal(
&validated_state,
Expand Down Expand Up @@ -889,11 +898,11 @@ mod test_headers {
let mut block_merkle_tree = proposal_state.block_merkle_tree.clone();
block_merkle_tree.push(proposal.commit()).unwrap();

let l1_deposits = get_l1_deposits(&genesis_state, &proposal, &parent_leaf).await;

let mut delta = Delta::default();

let proposal_state = apply_proposal(&proposal_state, &mut delta, &parent_leaf, l1_deposits);
let proposal_state = proposal_state
.apply_header(&genesis_state, &parent_leaf, &proposal)
.await
.unwrap()
.0;
validate_proposal(
&proposal_state,
genesis.instance_state.chain_config,
Expand Down
12 changes: 3 additions & 9 deletions sequencer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use sequencer::{
api::{self, data_source::DataSourceOptions},
init_node,
options::{Modules, Options},
persistence, BuilderParams, ChainConfig, L1Params, NetworkParams,
persistence, BuilderParams, L1Params, NetworkParams,
};
use vbs::version::StaticVersionType;

Expand Down Expand Up @@ -50,12 +50,6 @@ where
{
let (private_staking_key, private_state_key) = opt.private_keys()?;
let stake_table_capacity = opt.stake_table_capacity;
let chain_config = ChainConfig {
chain_id: opt.chain_id.into(),
max_block_size: opt.max_block_size,
base_fee: opt.base_fee.into(),
fee_contract: opt.fee_contract_address,
};
let l1_params = L1Params {
url: opt.l1_provider_url,
};
Expand Down Expand Up @@ -128,7 +122,7 @@ where
l1_params,
stake_table_capacity,
bind_version,
chain_config,
opt.chain_config,
opt.is_da,
)
.await
Expand All @@ -149,7 +143,7 @@ where
l1_params,
stake_table_capacity,
bind_version,
chain_config,
opt.chain_config,
opt.is_da,
)
.await?
Expand Down
21 changes: 4 additions & 17 deletions sequencer/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::{api, persistence};
use crate::{api, persistence, ChainConfig};
use anyhow::{bail, Context};
use bytesize::ByteSize;
use clap::{error::ErrorKind, Args, FromArgMatches, Parser};
use cld::ClDuration;
use core::fmt::Display;
use derivative::Derivative;
use derive_more::From;
use ethers::types::{Address, U256};
use ethers::types::Address;
use hotshot_stake_table::config::STAKE_TABLE_CAPACITY;
use hotshot_types::light_client::StateSignKey;
use hotshot_types::signature_key::BLSPrivKey;
Expand Down Expand Up @@ -43,10 +43,6 @@ use url::Url;
#[derive(Parser, Clone, Derivative)]
#[derivative(Debug(bound = ""))]
pub struct Options {
/// Unique identifier for this instance of the sequencer network.
#[clap(long, env = "ESPRESSO_SEQUENCER_CHAIN_ID", default_value = "0")]
pub chain_id: u64,

/// URL of the HotShot orchestrator.
#[clap(
short,
Expand Down Expand Up @@ -160,10 +156,6 @@ pub struct Options {
#[clap(long, env = "ESPRESSO_SEQUENCER_IS_DA", action)]
pub is_da: bool,

/// Address of the L1 contract used to bridge fee tokens into Espresso.
#[clap(long, env = "ESPRESSO_SEQUENCER_FEE_CONTRACT_PROXY_ADDRESS")]
pub fee_contract_address: Option<Address>,

/// Peer nodes use to fetch missing state
#[clap(long, env = "ESPRESSO_SEQUENCER_STATE_PEERS", value_delimiter = ',')]
#[derivative(Debug(format_with = "fmt_urls"))]
Expand All @@ -173,13 +165,8 @@ pub struct Options {
#[clap(long, env = "ESPRESSO_SEQUENCER_STAKE_TABLE_CAPACITY", default_value_t = STAKE_TABLE_CAPACITY)]
pub stake_table_capacity: usize,

/// Maximum size in bytes of a block
#[clap(long, env = "ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE", value_parser = parse_size)]
pub max_block_size: u64,

#[clap(long, env = "ESPRESSO_SEQUENCER_BASE_FEE")]
/// Minimum fee in WEI per byte of payload
pub base_fee: U256,
#[clap(flatten)]
pub chain_config: ChainConfig,
}

impl Options {
Expand Down
Loading

0 comments on commit 56c7ee7

Please sign in to comment.