Skip to content

Commit

Permalink
feat: Derive builder domain from beacon api (#35)
Browse files Browse the repository at this point in the history
## 📝 Summary

Builds on top of #34.

This PR removes the flag `cl_network_config_dir` which is used to load
the consensus network spec to derive the builder signing domain.
Instead, it uses the beacon clients to retrieve the required fields.

---

## ✅ I have completed the following steps:

* [x] Run `make lint`
* [x] Run `make test`
* [x] Added tests (if applicable)
  • Loading branch information
ferranbt authored Jul 15, 2024
1 parent a9e53e3 commit 312cf77
Showing 1 changed file with 81 additions and 24 deletions.
105 changes: 81 additions & 24 deletions crates/rbuilder/src/live_builder/base_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ use crate::{
};
use ahash::HashSet;
use alloy_chains::ChainKind;
use alloy_primitives::{utils::parse_ether, Address, B256};
use ethereum_consensus::{builder::compute_builder_domain, crypto::SecretKey, networks::Network};
use alloy_primitives::{utils::parse_ether, Address, FixedBytes, B256};
use ethereum_consensus::{
builder::compute_builder_domain, crypto::SecretKey, primitives::Version,
state_transition::Context as ContextEth,
};
use eyre::{eyre, Context};
use jsonrpsee::RpcModule;
use lazy_static::lazy_static;
Expand Down Expand Up @@ -80,7 +83,6 @@ pub struct BaseConfig {
pub reth_datadir: Option<PathBuf>,
pub reth_db_path: Option<PathBuf>,
pub reth_static_files_path: Option<PathBuf>,
pub cl_network_config_dir: Option<String>,

pub blocklist_file_path: Option<PathBuf>,
pub extra_data: String,
Expand Down Expand Up @@ -325,8 +327,7 @@ impl BaseConfig {

pub fn bls_signer(&self) -> eyre::Result<BLSBlockSigner> {
let chain_spec = self.chain_spec()?;
let signing_domain =
get_signing_domain(chain_spec.chain, self.cl_network_config_dir.clone())?;
let signing_domain = get_signing_domain(chain_spec.chain, self.beacon_clients()?)?;
let secret_key = self.relay_secret_key.value()?;
let secret_key = SecretKey::try_from(secret_key)
.map_err(|e| eyre::eyre!("Failed to parse relay key: {:?}", e.to_string()))?;
Expand All @@ -336,8 +337,7 @@ impl BaseConfig {

pub fn bls_optimistic_signer(&self) -> eyre::Result<BLSBlockSigner> {
let chain_spec = self.chain_spec()?;
let signing_domain =
get_signing_domain(chain_spec.chain, self.cl_network_config_dir.clone())?;
let signing_domain = get_signing_domain(chain_spec.chain, self.beacon_clients()?)?;
let secret_key = self.optimistic_relay_secret_key.value()?;
let secret_key = SecretKey::try_from(secret_key).map_err(|e| {
eyre::eyre!("Failed to parse optimistic relay key: {:?}", e.to_string())
Expand Down Expand Up @@ -530,7 +530,6 @@ impl Default for BaseConfig {
reth_datadir: Some(DEFAULT_RETH_DB_PATH.parse().unwrap()),
reth_db_path: None,
reth_static_files_path: None,
cl_network_config_dir: None,
blocklist_file_path: None,
extra_data: "extra_data_change_me".to_string(),
relays: vec![],
Expand Down Expand Up @@ -602,29 +601,47 @@ pub fn coinbase_signer_from_secret_key(secret_key: &str) -> eyre::Result<Signer>
Ok(Signer::try_from_secret(secret_key)?)
}

fn get_signing_domain(chain: Chain, cl_network_config_dir: Option<String>) -> eyre::Result<B256> {
let network = named_chain_to_network(chain)
.or_else(|| cl_network_config_dir.map(Network::Custom))
.ok_or_else(|| {
eyre::eyre!("Unknown chain. If network is not named, specify cl_network_config_dir")
})?;
fn get_signing_domain(chain: Chain, beacon_clients: Vec<Client>) -> eyre::Result<B256> {
let cl_context = match chain.kind() {
ChainKind::Named(NamedChain::Mainnet) => ContextEth::for_mainnet(),
ChainKind::Named(NamedChain::Sepolia) => ContextEth::for_sepolia(),
ChainKind::Named(NamedChain::Goerli) => ContextEth::for_goerli(),
ChainKind::Named(NamedChain::Holesky) => ContextEth::for_holesky(),
_ => {
let client = beacon_clients
.first()
.ok_or_else(|| eyre::eyre!("No beacon clients provided"))?;

let spec = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(client.get_spec())
})?;

let genesis_fork_version = spec
.get("GENESIS_FORK_VERSION")
.ok_or_else(|| eyre::eyre!("GENESIS_FORK_VERSION not found in spec"))?;

let version: FixedBytes<4> = FixedBytes::from_str(genesis_fork_version)
.map_err(|e| eyre::eyre!("Failed to parse genesis fork version: {:?}", e))?;

let version = Version::from(version);

// use the mainnet one and update the genesis fork version since it is the
// only thing required by 'compute_builder_domain'. We do this because
// there is no default in Context.
let mut network = ContextEth::for_mainnet();
network.genesis_fork_version = version;

network
}
};

let cl_context = ethereum_consensus::state_transition::Context::try_from(network)?;
Ok(B256::from(&compute_builder_domain(&cl_context)?))
}
fn named_chain_to_network(chain: Chain) -> Option<Network> {
match chain.kind() {
ChainKind::Named(NamedChain::Mainnet) => Some(Network::Mainnet),
ChainKind::Named(NamedChain::Sepolia) => Some(Network::Sepolia),
ChainKind::Named(NamedChain::Goerli) => Some(Network::Goerli),
ChainKind::Named(NamedChain::Holesky) => Some(Network::Holesky),
_ => None,
}
}

#[cfg(test)]
mod test {
use super::*;
use alloy_primitives::fixed_bytes;
use reth_db::init_db;
use reth_node_core::{
dirs::{DataDirPath, MaybePlatformPath},
Expand All @@ -633,6 +650,7 @@ mod test {
use reth_primitives::{Chain, SEPOLIA};
use reth_provider::ProviderFactory;
use tempfile::TempDir;
use url::Url;

#[test]
fn test_default_config() {
Expand All @@ -642,6 +660,45 @@ mod test {
assert_eq!(config, config_default);
}

#[test]
fn test_signing_domain_known_chains() {
let cases = [
(
NamedChain::Mainnet,
fixed_bytes!("00000001f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a9"),
),
(
NamedChain::Sepolia,
fixed_bytes!("00000001d3010778cd08ee514b08fe67b6c503b510987a4ce43f42306d97c67c"),
),
(
NamedChain::Goerli,
fixed_bytes!("00000001e4be9393b074ca1f3e4aabd585ca4bea101170ccfaf71b89ce5c5c38"),
),
(
NamedChain::Holesky,
fixed_bytes!("000000015b83a23759c560b2d0c64576e1dcfc34ea94c4988f3e0d9f77f05387"),
),
];

for (chain, domain) in cases.iter() {
let found = get_signing_domain(Chain::from_named(*chain), vec![]).unwrap();
assert_eq!(found, *domain);
}
}

#[ignore]
#[test]
fn test_signing_domain_custom_chain() {
let client = Client::new(Url::parse("http://localhost:8000").unwrap());
let found = get_signing_domain(Chain::from_id(12345), vec![client]).unwrap();

assert_eq!(
found,
fixed_bytes!("00000001aaf2630a2874a74199f4b5d11a7d6377f363a236271bff4bf8eb4ab3")
);
}

#[test]
fn test_reth_db() {
// Setup and initialize a temp reth db (with static files)
Expand Down

0 comments on commit 312cf77

Please sign in to comment.