reth_bench/bench/
send_payload.rs1use super::helpers::{fetch_block_access_list, load_jwt_secret, read_input};
2use alloy_consensus::TxEnvelope;
3use alloy_primitives::Bytes;
4use alloy_provider::{
5 network::{AnyNetwork, AnyRpcBlock},
6 RootProvider,
7};
8use alloy_rpc_client::ClientBuilder;
9use alloy_rpc_types_engine::ExecutionPayload;
10use clap::Parser;
11use eyre::{OptionExt, Result};
12use reth_cli_runner::CliContext;
13use std::io::Write;
14
15#[derive(Debug, Parser)]
18pub struct Command {
19 #[arg(short, long)]
21 path: Option<String>,
22
23 #[arg(
25 short,
26 long,
27 required_if_eq_any([("mode", "execute"), ("mode", "cast")]),
29 required_unless_present("mode")
31 )]
32 rpc_url: Option<String>,
33
34 #[arg(short, long)]
37 jwt_secret: Option<String>,
38
39 #[arg(long, default_value_t = 3)]
40 new_payload_version: u8,
41
42 #[arg(long, value_enum, default_value = "execute")]
44 mode: Mode,
45}
46
47#[derive(Debug, Clone, clap::ValueEnum)]
48enum Mode {
49 Execute,
52 Cast,
55 Json,
57}
58
59impl Command {
60 pub async fn execute(self, _ctx: CliContext) -> Result<()> {
62 let block_json = read_input(self.path.as_deref())?;
64
65 let jwt_secret = load_jwt_secret(self.jwt_secret.as_deref())?;
67
68 let block = serde_json::from_str::<AnyRpcBlock>(&block_json)?
70 .into_inner()
71 .map_header(|header| header.map(|h| h.into_header_with_defaults()))
72 .try_map_transactions(|tx| -> eyre::Result<TxEnvelope> {
73 tx.try_into().map_err(|_| eyre::eyre!("unsupported tx type"))
74 })?
75 .into_consensus();
76
77 let use_v4 = block.header.requests_hash.is_some();
78 let use_v5 = block.header.block_access_list_hash.is_some();
79
80 let parent_beacon_block_root = block.header.parent_beacon_block_root;
82
83 let blob_versioned_hashes =
85 block.body.blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
86
87 let execution_payload = if use_v5 {
90 let encoded_bal = self.fetch_encoded_block_access_list(block.header.number).await?;
91 ExecutionPayload::from_block_slow_with_bal(&block, encoded_bal).0
92 } else {
93 ExecutionPayload::from_block_slow(&block).0
94 };
95
96 let json_request = if use_v4 {
98 serde_json::to_string(&(
99 execution_payload,
100 blob_versioned_hashes,
101 parent_beacon_block_root,
102 block.header.requests_hash.unwrap_or_default(),
103 ))?
104 } else {
105 serde_json::to_string(&(
106 execution_payload,
107 blob_versioned_hashes,
108 parent_beacon_block_root,
109 ))?
110 };
111
112 match self.mode {
114 Mode::Execute => {
115 let mut command = std::process::Command::new("cast");
117 let method = if use_v5 {
118 "engine_newPayloadV5"
119 } else if use_v4 {
120 "engine_newPayloadV4"
121 } else {
122 "engine_newPayloadV3"
123 };
124 command.arg("rpc").arg(method).arg("--raw");
125 if let Some(rpc_url) = self.rpc_url {
126 command.arg("--rpc-url").arg(rpc_url);
127 }
128 if let Some(secret) = &jwt_secret {
129 command.arg("--jwt-secret").arg(secret);
130 }
131
132 let mut process = command.stdin(std::process::Stdio::piped()).spawn()?;
134
135 process
137 .stdin
138 .take()
139 .ok_or_eyre("stdin not available")?
140 .write_all(json_request.as_bytes())?;
141
142 process.wait()?;
144 }
145 Mode::Cast => {
146 let mut cmd = format!(
147 "cast rpc engine_newPayloadV{} --raw '{}'",
148 self.new_payload_version, json_request
149 );
150
151 if let Some(rpc_url) = self.rpc_url {
152 cmd += &format!(" --rpc-url {rpc_url}");
153 }
154 if let Some(secret) = &jwt_secret {
155 cmd += &format!(" --jwt-secret {secret}");
156 }
157
158 println!("{cmd}");
159 }
160 Mode::Json => {
161 println!("{json_request}");
162 }
163 }
164
165 Ok(())
166 }
167
168 async fn fetch_encoded_block_access_list(&self, block_number: u64) -> Result<Bytes> {
169 let rpc_url = self
170 .rpc_url
171 .as_deref()
172 .ok_or_eyre("--rpc-url is required to fetch the block access list for V5 payloads")?;
173 let client = ClientBuilder::default()
174 .layer(alloy_transport::layers::RetryBackoffLayer::new(10, 800, u64::MAX))
175 .http(rpc_url.parse()?);
176 let provider = RootProvider::<AnyNetwork>::new(client);
177 let bal = fetch_block_access_list(&provider, block_number).await?;
178 Ok(alloy_rlp::encode(bal).into())
179 }
180}