From 9d00b5e1205eee7e3e53ccc0fd2076bad090b0a8 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:08:51 +0200 Subject: [PATCH] experimental: get payload from EL error hints Co-authored-by: thedevbirb --- bolt-sidecar/Cargo.lock | 189 ++++++++++++- bolt-sidecar/Cargo.toml | 4 + .../src/builder/call_trace_manager.rs | 95 ++++--- bolt-sidecar/src/builder/payload_builder.rs | 251 +++++++++++++++--- bolt-sidecar/src/builder/state_root.rs | 10 +- bolt-sidecar/src/builder/template.rs | 35 +-- 6 files changed, 457 insertions(+), 127 deletions(-) diff --git a/bolt-sidecar/Cargo.lock b/bolt-sidecar/Cargo.lock index ce9bef51..5d1a9696 100644 --- a/bolt-sidecar/Cargo.lock +++ b/bolt-sidecar/Cargo.lock @@ -356,7 +356,7 @@ dependencies = [ "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=17633df)", "alloy-sol-types", "itertools 0.12.1", - "jsonrpsee-types", + "jsonrpsee-types 0.22.5", "serde", "serde_json", "thiserror", @@ -427,7 +427,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=17633df)", "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=17633df)", - "jsonrpsee-types", + "jsonrpsee-types 0.22.5", "serde", "thiserror", ] @@ -1247,6 +1247,7 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types 0.1.2", "alloy-rpc-types-beacon 0.1.2", + "alloy-rpc-types-engine 0.1.2", "alloy-rpc-types-trace 0.1.2", "alloy-signer", "alloy-signer-local", @@ -1255,6 +1256,7 @@ dependencies = [ "alloy-transport-ws", "async-trait", "axum", + "base64 0.22.1", "beacon-api-client", "blst", "bytes", @@ -1272,6 +1274,7 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.5", "reth-primitives", + "reth-rpc-layer", "secp256k1 0.29.0", "serde", "serde_json", @@ -1390,6 +1393,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -1455,6 +1464,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2626,6 +2645,7 @@ dependencies = [ "http 1.1.0", "hyper 1.3.1", "hyper-util", + "log", "rustls", "rustls-pki-types", "tokio", @@ -2856,6 +2876,26 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.31" @@ -2874,6 +2914,53 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpsee-core" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21545a9445fbd582840ff5160a9a3e12b8e6da582151cdb07bde9a1970ba3a24" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "jsonrpsee-types 0.23.1", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb25cab482c8512c4f3323a5c90b95a3b8f7c90681a87bf7a68b942d52f08933" +dependencies = [ + "async-trait", + "base64 0.22.1", + "http-body 1.0.0", + "hyper 1.3.1", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types 0.23.1", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + [[package]] name = "jsonrpsee-types" version = "0.22.5" @@ -2887,6 +2974,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "jsonrpsee-types" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f511b714bca46f9a3e97c0e0eb21d2c112e83e444d2db535b5ec7093f5836d73" +dependencies = [ + "beef", + "http 1.1.0", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "jsonwebtoken" version = "9.3.0" @@ -3990,6 +4090,19 @@ dependencies = [ "zstd", ] +[[package]] +name = "reth-rpc-layer" +version = "1.0.0" +source = "git+https://github.com/paradigmxyz/reth?rev=6e146e1#6e146e1140e874b88c304fdc3c5ac0e046e66263" +dependencies = [ + "alloy-rpc-types-engine 0.1.2", + "http 1.1.0", + "jsonrpsee-http-client", + "pin-project", + "tower", + "tracing", +] + [[package]] name = "reth-rpc-types" version = "0.2.0-beta.5" @@ -4002,7 +4115,7 @@ dependencies = [ "alloy-rpc-types-engine 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=17633df)", "alloy-rpc-types-trace 0.1.0", "enr 0.10.0", - "jsonrpsee-types", + "jsonrpsee-types 0.22.5", "secp256k1 0.27.0", "serde", "serde_json", @@ -4246,6 +4359,7 @@ version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ + "log", "once_cell", "ring 0.17.8", "rustls-pki-types", @@ -4254,6 +4368,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.2", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4279,6 +4406,33 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +[[package]] +name = "rustls-platform-verifier" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f0d26fa1ce3c790f9590868f0109289a044acb954525f933e2aa3b871c157d" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-roots", + "winapi", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad" + [[package]] name = "rustls-webpki" version = "0.102.4" @@ -4314,6 +4468,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scale-info" version = "2.11.3" @@ -4453,6 +4616,7 @@ dependencies = [ "core-foundation", "core-foundation-sys", "libc", + "num-bigint", "security-framework-sys", ] @@ -5518,6 +5682,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -5682,6 +5856,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/bolt-sidecar/Cargo.toml b/bolt-sidecar/Cargo.toml index f39e026a..ee9fa61a 100644 --- a/bolt-sidecar/Cargo.toml +++ b/bolt-sidecar/Cargo.toml @@ -28,6 +28,7 @@ alloy-transport-http = { version = "0.1.2" } alloy-transport-ws = { version = "0.1.2" } alloy-pubsub = { version = "0.1.2" } alloy-rpc-types = { version = "0.1.2" } +alloy-rpc-types-engine = { version = "0.1.2" } alloy-rpc-types-beacon = { version = "0.1.2" } alloy-rpc-types-trace = { version = "0.1.2" } alloy-json-rpc = { version = "0.1.2" } @@ -41,6 +42,7 @@ alloy-rlp = { version = "0.3" } # reth reth-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "71c404d" } +reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth", rev = "6e146e1" } # reth-provider = { git = "https://github.com/paradigmxyz/reth", rev = "71c404d" } reqwest = "0.12" @@ -71,6 +73,8 @@ tracing-subscriber = "0.3.18" cb-crypto = { git = "https://github.com/Commit-Boost/commit-boost-client" } cb-common = { git = "https://github.com/Commit-Boost/commit-boost-client" } +base64 = "0.22.1" + [dev-dependencies] alloy-node-bindings = "0.1.1" diff --git a/bolt-sidecar/src/builder/call_trace_manager.rs b/bolt-sidecar/src/builder/call_trace_manager.rs index 467cb4ce..4f6bfce4 100644 --- a/bolt-sidecar/src/builder/call_trace_manager.rs +++ b/bolt-sidecar/src/builder/call_trace_manager.rs @@ -8,7 +8,7 @@ use std::{ task::{Context, Poll}, }; -use alloy_primitives::{BlockNumber, U64}; +use alloy_primitives::{Address, BlockNumber, U64}; use alloy_rpc_types::{ state::{AccountOverride, StateOverride}, TransactionRequest, @@ -115,12 +115,8 @@ impl Future for CallTraceManager { } match this.pending_traces.poll_next_unpin(cx) { - Poll::Ready(Some(Ok((block, trace_result)))) => { - this.handle_trace_result(block, trace_result) - } - Poll::Ready(Some(Err(e))) => { - tracing::error!(err = ?e, "Error while tracing transaction"); - } + Poll::Ready(Some(Ok((block, res)))) => this.handle_trace_result(block, res), + Poll::Ready(Some(Err(e))) => tracing::error!(err = ?e, "Error while tracing tx"), Poll::Ready(None) => return Poll::Ready(()), Poll::Pending => return Poll::Pending, } @@ -191,12 +187,12 @@ impl CallTraceManager { } fn handle_trace_result(&mut self, block: BlockNumber, result: TransportResult) { - match result { + let post_execution_state_diffs = match result { Ok(trace) => { tracing::debug!(block = block, "RPC trace call completed"); - let Ok(PreStateFrame::Default(trace_state)) = trace.try_into_pre_state_frame() - else { + tracing::warn!("{:?}", &trace); + let GethTrace::PreStateTracer(PreStateFrame::Default(trace_state)) = trace else { tracing::error!("Failed to extract pre-state frame from trace result"); return; }; @@ -208,37 +204,26 @@ impl CallTraceManager { merge_account_state_in_overrides(account_override, account_state); } - // If there are more pending trace requests for the same block, process the next one - if let Some(transactions) = self.trace_request_queue.get_mut(&block) { - if let Some(transaction) = transactions.pop_front() { - self.start_new_trace_call_with_overrides(transaction, block); - return; - } - } - - // If there are no more transactions to process for this block, - // send the accumulated state diffs to the response channel if there is - // one waiting for it - if let Some(res) = self.response_queue.remove(&block) { - let _ = res.send(Some(acc_state_diffs.clone())); - self.accumulated_state_diffs.remove(&block); - } + Some(acc_state_diffs.clone()) } Err(err) => { tracing::error!(err = ?err, "RPC error while tracing transaction"); + None + } + }; - // For now, just log the error and continue processing the next trace request - // for the same block, if there is one. - if let Some(transactions) = self.trace_request_queue.get_mut(&block) { - if let Some(transaction) = transactions.pop_front() { - self.start_new_trace_call_with_overrides(transaction, block); - } - } - - if let Some(res) = self.response_queue.remove(&block) { - let _ = res.send(None); - self.accumulated_state_diffs.remove(&block); - } + // If there are more pending trace requests for the same block, process the next one + if let Some(transactions) = self.trace_request_queue.get_mut(&block) { + if let Some(transaction) = transactions.pop_front() { + self.start_new_trace_call_with_overrides(transaction, block); + } + } else { + // If there are no more transactions to process for this block, + // send the accumulated state diffs to the response channel if there is + // one waiting for it + if let Some(res) = self.response_queue.remove(&block) { + let _ = res.send(post_execution_state_diffs); + self.accumulated_state_diffs.remove(&block); } } } @@ -255,17 +240,12 @@ impl CallTraceManager { .cloned() .unwrap_or_default(); - let mut tracing_options = get_trace_options_with_override(state_override); - tracing_options.tracing_options.tracer = Some(GethDebugTracerType::JsTracer(format!( - r#"{{ - data: [], - result: function(ctx, db) {{ - var root = db.GetStorageRoot("{:x}"); - return root; - }}, - }}"#, - transaction.from.unwrap_or_default() - ))); + let tracing_options = get_trace_options_with_override(state_override); + + // TODO: add js tracer support + // tracing_options.tracing_options.tracer = Some(GethDebugTracerType::JsTracer( + // create_js_tracer_script(transaction.from.unwrap_or_default()), + // )); self.pending_traces.push_back(tokio::spawn(async move { ( @@ -279,7 +259,7 @@ impl CallTraceManager { fn get_trace_options_with_override(state_override: StateOverride) -> GethDebugTracingCallOptions { let mut opts = GethDebugTracingOptions::default().with_tracer( - GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::PreStateTracer), + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer), ); opts.config = GethDefaultTracingOptions::default() @@ -305,3 +285,20 @@ fn merge_account_state_in_overrides(account_override: &mut AccountOverride, valu } } } + +#[allow(dead_code)] +fn create_js_tracer_script(address: Address) -> String { + format!( + r#"{{ + data: [], + result: function(ctx, db) {{ + var root = db.GetStorageRoot("{:x}"); + return root; + }}, + fault: function(ctx, db) {{ + return false; + }} + }}"#, + address + ) +} diff --git a/bolt-sidecar/src/builder/payload_builder.rs b/bolt-sidecar/src/builder/payload_builder.rs index f7206c78..920eaf15 100644 --- a/bolt-sidecar/src/builder/payload_builder.rs +++ b/bolt-sidecar/src/builder/payload_builder.rs @@ -12,7 +12,7 @@ use ethereum_consensus::{ }; use reth_primitives::{ constants::BEACON_NONCE, proofs, BlockBody, Bloom, Header, SealedBlock, SealedHeader, - TransactionSigned, EMPTY_OMMER_ROOT_HASH, + TransactionSigned, Withdrawals, EMPTY_OMMER_ROOT_HASH, }; use crate::primitives::{BuilderBid, Slot}; @@ -24,12 +24,7 @@ pub enum PayloadBuilderError { } #[derive(Debug)] -pub struct FallbackPayloadBuilder -where - SRP: StateRootProvider, -{ - state_root_provider: SRP, - +pub struct FallbackPayloadBuilder { fee_recipient: Address, // keypair used for signing the payload @@ -38,15 +33,17 @@ where } /// Minimal execution context required to build a valid payload on the target slot. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ExecutionContext { head_slot_number: Slot, parent_hash: B256, + parent_beacon_block_root: B256, transactions: Vec, block: NextBlockInfo, + excess_blob_gas: u64, } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct NextBlockInfo { number: u64, timestamp: u64, @@ -56,31 +53,35 @@ pub struct NextBlockInfo { gas_limit: u64, } -/// Provider that is able to compute the state root over a set of state diffs. -/// TODO: how do we avoid full access to the state DB here? -pub trait StateRootProvider { - fn get_state_root(&self) -> Result; +#[derive(Debug)] +pub struct Hints { + pub gas_used: Option, + pub receipts_root: Option, + pub withdrawals_root: Option, + pub logs_bloom: Option, + pub blob_gas_used: Option, + pub state_root: Option, } -impl FallbackPayloadBuilder -where - SRP: StateRootProvider, -{ +impl FallbackPayloadBuilder { /// Build a minimal payload to be used as a fallback pub async fn build_fallback_payload( &self, context: ExecutionContext, - ) -> Result { - // TODO: actually get the state root (needs to count post-state diffs) - let state_root = self.state_root_provider.get_state_root()?; + hints: Hints, + ) -> Result { let transactions_root = proofs::calculate_transaction_root(&context.transactions); - // TODO: fill all of these with correct values - let withdrawals_root = Some(B256::default()); - let receipts_root = B256::default(); - let logs_bloom = Bloom::default(); - let gas_used = 0; - let parent_beacon_root = B256::default(); + // TODO: actually get the state root (needs to count post-state diffs) + let state_root = hints.state_root.unwrap_or_default(); + + // TODO: fill somehow + let withdrawals_root = hints.withdrawals_root; + let receipts_root = hints.receipts_root.unwrap_or_default(); + let logs_bloom = hints.logs_bloom.unwrap_or_default(); + let gas_used = hints.gas_used.unwrap_or_default(); + + // TODO: this should be fine (remove min-bid requirement) let value = U256::ZERO; let header = Header { @@ -100,9 +101,9 @@ where mix_hash: context.block.prev_randao, nonce: BEACON_NONCE, base_fee_per_gas: Some(context.block.base_fee), - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: Some(parent_beacon_root), + blob_gas_used: hints.blob_gas_used, + excess_blob_gas: Some(context.excess_blob_gas), + parent_beacon_block_root: Some(context.parent_beacon_block_root), extra_data: context.block.extra_data, }; @@ -113,14 +114,16 @@ where }; let sealed_block = SealedBlock::new(header.seal_slow(), body); - let submission = BuilderBid { - header: to_execution_payload_header(&sealed_block.header), - blob_kzg_commitments: List::default(), - public_key: self.public_key.clone(), - value, - }; - Ok(submission) + // TODO: transform into submission later + // let submission = BuilderBid { + // header: to_execution_payload_header(&sealed_block.header), + // blob_kzg_commitments: List::default(), + // public_key: self.public_key.clone(), + // value, + // }; + + Ok(sealed_block) } } @@ -157,7 +160,7 @@ pub(crate) fn to_execution_payload(value: &SealedBlock) -> ExecutionPayload { .collect::>(); let withdrawals = withdrawals .as_ref() - .unwrap() + .unwrap_or(&Withdrawals::default()) .iter() .map(|w| spec::Withdrawal { index: w.index as usize, @@ -198,3 +201,177 @@ fn to_bytes20(value: Address) -> spec::ExecutionAddress { fn to_byte_vector(value: Bloom) -> ByteVector<256> { ByteVector::<256>::try_from(value.as_ref()).unwrap() } + +#[cfg(test)] +mod tests { + use std::{borrow::BorrowMut, str::FromStr}; + + use alloy_consensus::{Transaction, TxEnvelope}; + use alloy_eips::{ + calc_excess_blob_gas, calc_next_block_base_fee, eip1559::BaseFeeParams, + eip2718::Encodable2718, + }; + use alloy_network::{EthereumWallet, TransactionBuilder, TxSigner}; + use alloy_primitives::{address, hex, Address, Bytes, B256, U256}; + use alloy_rpc_types::TransactionRequest; + use alloy_rpc_types_engine::{ + ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, + }; + use alloy_signer::k256::ecdsa::SigningKey; + use alloy_signer_local::PrivateKeySigner; + use ethereum_consensus::crypto::bls::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey}; + use reth_primitives::{Bloom, TransactionSigned}; + use reth_rpc_layer::{secret_to_bearer_header, JwtSecret}; + use std::io::prelude::*; + + use crate::{ + builder::payload_builder::{ + to_execution_payload, ExecutionContext, FallbackPayloadBuilder, Hints, NextBlockInfo, + }, + RpcClient, + }; + + #[tokio::test] + async fn test_build_fallback_payload() -> eyre::Result<()> { + dotenvy::dotenv().ok(); + + let raw_sk = std::env::var("PRIVATE_KEY")?; + let jwt = std::env::var("ENGINE_JWT")?; + + let execution_rpc = RpcClient::new("http://remotebeast:8545"); + let engine = "http://remotebeast:8551"; + let consensus = "http://remotebeast:3500"; + + let pk = BlsSecretKey::random(&mut rand::thread_rng())?; + + let builder = FallbackPayloadBuilder { + fee_recipient: Address::default(), + public_key: pk.public_key(), + private_key: pk, + }; + + let latest_block = execution_rpc.get_block(Some(20169768), true).await?; + + let base_fee = calc_next_block_base_fee( + latest_block.header.gas_used, + latest_block.header.gas_limit, + latest_block.header.base_fee_per_gas.unwrap_or_default(), + BaseFeeParams::ethereum(), + ); + + let excess_blob_gas = calc_excess_blob_gas( + latest_block.header.excess_blob_gas.unwrap_or_default(), + latest_block.header.blob_gas_used.unwrap_or_default(), + ); + + let sk = SigningKey::from_slice(hex::decode(raw_sk)?.as_slice())?; + let signer = PrivateKeySigner::from_signing_key(sk.clone()); + let wallet = EthereumWallet::from(signer); + + let addy = Address::from_private_key(&sk); + let mut tx = default_transaction(addy, 266); + let tx_signed = tx.build(&wallet).await?; + let mut raw_encoded = tx_signed.encoded_2718(); + let tx_signed_reth = TransactionSigned::decode_enveloped(&mut raw_encoded.as_slice())?; + + let execution_context = ExecutionContext { + head_slot_number: latest_block.header.number.unwrap_or_default(), + parent_hash: latest_block.header.hash.unwrap_or_default(), + excess_blob_gas: excess_blob_gas as u64, + parent_beacon_block_root: latest_block + .header + .parent_beacon_block_root + .unwrap_or_default(), + transactions: vec![tx_signed_reth], + block: NextBlockInfo { + number: latest_block.header.number.unwrap_or_default() + 1, + timestamp: latest_block.header.timestamp + 12, + prev_randao: latest_block.header.mix_hash.unwrap_or_default(), + base_fee: base_fee as u64, + extra_data: Bytes::from_str("0xdeadbeef")?, + gas_limit: 30_000_000, + }, + }; + + let hints = Hints { + gas_used: Some(21000), + receipts_root: Some(B256::from_str( + "0xf78dfb743fbd92ade140711c8bbc542b5e307f0ab7984eff35d751969fe57efa", + )?), + withdrawals_root: None, + logs_bloom: None, + blob_gas_used: None, + state_root: Some(B256::from_str( + "0x9e20f7759c74e92d0cd8ca721be834b3775dd0b7a757fcd3a26aeb3358990f5e", + )?), + }; + + let block = builder + .build_fallback_payload(execution_context, hints) + .await?; + + let exec_payload_reth: ExecutionPayload = ExecutionPayload::V3(ExecutionPayloadV3 { + blob_gas_used: block.blob_gas_used(), + excess_blob_gas: block.excess_blob_gas.unwrap_or_default(), + payload_inner: ExecutionPayloadV2 { + payload_inner: ExecutionPayloadV1 { + base_fee_per_gas: U256::from(block.base_fee_per_gas.unwrap_or_default()), + block_hash: B256::from_str( + "1857c16c66715f087b8bf58235171b7942acf66e52c919d7526d37e523c7dcad", + )?, + block_number: block.number, + extra_data: block.extra_data.clone(), + transactions: block.raw_transactions(), + fee_recipient: block.header.beneficiary, + gas_limit: block.gas_limit, + gas_used: block.gas_used, + logs_bloom: block.logs_bloom, + parent_hash: block.parent_hash, + prev_randao: block.mix_hash, + receipts_root: block.receipts_root, + state_root: block.state_root, + timestamp: block.timestamp, + }, + withdrawals: vec![], + }, + }); + + let body = format!( + r#"{{"id":1,"jsonrpc":"2.0","method":"engine_newPayloadV3","params":[{}, [], "{:?}"]}}"#, + serde_json::to_string(&exec_payload_reth)?, + exec_payload_reth.parent_hash() + ); + + println!("{}", body); + + let auth_jwt = secret_to_bearer_header(&JwtSecret::from_hex(jwt)?); + let engine_client = reqwest::Client::new(); + let engine_error_1 = engine_client + .post(engine) + .header("Content-Type", "application/json") + .header("Authorization", auth_jwt) + .body(body) + .send() + .await?; + + let res: serde_json::Value = engine_error_1.json().await?; + + println!("{:?}", res); + panic!(); + + Ok(()) + } + + fn default_transaction(sender: Address, nonce: u64) -> TransactionRequest { + TransactionRequest::default() + .with_from(sender) + // Burn it + .with_to(Address::ZERO) + .with_chain_id(1) + .with_nonce(nonce) + .with_value(U256::from(100)) + .with_gas_limit(21_000) + .with_max_priority_fee_per_gas(1_000_000_000) + .with_max_fee_per_gas(20_000_000_000) + } +} diff --git a/bolt-sidecar/src/builder/state_root.rs b/bolt-sidecar/src/builder/state_root.rs index bc855835..502f27c0 100644 --- a/bolt-sidecar/src/builder/state_root.rs +++ b/bolt-sidecar/src/builder/state_root.rs @@ -18,14 +18,14 @@ mod tests { tracing::info!("Starting test_trace_call"); - let rpc_url = std::env::var("RPC_URL").expect("RPC_URL must be set"); - let client = RpcClient::new(&rpc_url); + let rpc_url = "http://remotebeast:8545"; + let client = RpcClient::new(rpc_url); - let (call_trace_manager, call_trace_handler) = CallTraceManager::new(&rpc_url); + let (call_trace_manager, call_trace_handler) = CallTraceManager::new(rpc_url); tokio::spawn(call_trace_manager); - // https://etherscan.io/block/20125606 - let block_number = 20125606; + // https://etherscan.io/block/20167237 + let block_number = 20167539; let latest_block = client.get_block(Some(block_number), true).await?; let latest_state_root = B256::from(latest_block.header.state_root.0); diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index 5cbe2622..db25b112 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -11,7 +11,7 @@ use alloy_primitives::{Address, U256}; use crate::{ common::max_transaction_cost, - primitives::{AccountState, BuilderBid, PayloadAndBid, SignedBuilderBid, TxInfo}, + primitives::{AccountState, TxInfo}, }; /// A block template that serves as a fallback block, but is also used @@ -31,6 +31,7 @@ pub struct BlockTemplate { } impl BlockTemplate { + /// Return the state diff of the block template. pub fn state_diff(&self) -> &StateDiff { &self.state_diff } @@ -116,27 +117,6 @@ impl BlockTemplate { } } -impl TryFrom for PayloadAndBid { - type Error = Box; - - fn try_from(value: BlockTemplate) -> Result { - let bid = SignedBuilderBid { - message: BuilderBid { - header: todo!(), - blob_kzg_commitments: todo!(), - value: todo!(), - public_key: todo!(), - }, - signature: todo!(), - }; - - Ok(PayloadAndBid { - payload: todo!(), - bid, - }) - } -} - /// StateDiff tracks the intermediate changes to the state according to the block template. #[derive(Debug, Default)] pub struct StateDiff { @@ -153,14 +133,3 @@ impl StateDiff { self.diffs.get(address).copied() } } - -#[derive(Debug)] -pub struct Unsigned { - message: T, -} - -pub trait Signable { - type Signature; - - fn sign(&self) -> Self::Signature; -}