reth_node_core/args/
payload_builder.rs
1use crate::{cli::config::PayloadBuilderConfig, version::default_extra_data};
2use alloy_consensus::constants::MAXIMUM_EXTRA_DATA_SIZE;
3use alloy_eips::{eip1559::ETHEREUM_BLOCK_GAS_LIMIT_36M, merge::SLOT_DURATION};
4use clap::{
5 builder::{RangedU64ValueParser, TypedValueParser},
6 Arg, Args, Command,
7};
8use reth_cli_util::{parse_duration_from_secs, parse_duration_from_secs_or_ms};
9use std::{borrow::Cow, ffi::OsStr, time::Duration};
10
11#[derive(Debug, Clone, Args, PartialEq, Eq)]
13#[command(next_help_heading = "Builder")]
14pub struct PayloadBuilderArgs {
15 #[arg(long = "builder.extradata", value_parser = ExtraDataValueParser::default(), default_value_t = default_extra_data())]
17 pub extra_data: String,
18
19 #[arg(long = "builder.gaslimit", default_value_t = ETHEREUM_BLOCK_GAS_LIMIT_36M, value_name = "GAS_LIMIT")]
21 pub gas_limit: u64,
22
23 #[arg(long = "builder.interval", value_parser = parse_duration_from_secs_or_ms, default_value = "1", value_name = "DURATION")]
29 pub interval: Duration,
30
31 #[arg(long = "builder.deadline", value_parser = parse_duration_from_secs, default_value = "12", value_name = "SECONDS")]
33 pub deadline: Duration,
34
35 #[arg(long = "builder.max-tasks", default_value = "3", value_parser = RangedU64ValueParser::<usize>::new().range(1..))]
37 pub max_payload_tasks: usize,
38}
39
40impl Default for PayloadBuilderArgs {
41 fn default() -> Self {
42 Self {
43 extra_data: default_extra_data(),
44 gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_36M,
45 interval: Duration::from_secs(1),
46 deadline: SLOT_DURATION,
47 max_payload_tasks: 3,
48 }
49 }
50}
51
52impl PayloadBuilderConfig for PayloadBuilderArgs {
53 fn extra_data(&self) -> Cow<'_, str> {
54 self.extra_data.as_str().into()
55 }
56
57 fn interval(&self) -> Duration {
58 self.interval
59 }
60
61 fn deadline(&self) -> Duration {
62 self.deadline
63 }
64
65 fn gas_limit(&self) -> u64 {
66 self.gas_limit
67 }
68
69 fn max_payload_tasks(&self) -> usize {
70 self.max_payload_tasks
71 }
72}
73
74#[derive(Clone, Debug, Default)]
75#[non_exhaustive]
76struct ExtraDataValueParser;
77
78impl TypedValueParser for ExtraDataValueParser {
79 type Value = String;
80
81 fn parse_ref(
82 &self,
83 _cmd: &Command,
84 _arg: Option<&Arg>,
85 value: &OsStr,
86 ) -> Result<Self::Value, clap::Error> {
87 let val =
88 value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
89 if val.len() > MAXIMUM_EXTRA_DATA_SIZE {
90 return Err(clap::Error::raw(
91 clap::error::ErrorKind::InvalidValue,
92 format!(
93 "Payload builder extradata size exceeds {MAXIMUM_EXTRA_DATA_SIZE}-byte limit"
94 ),
95 ))
96 }
97 Ok(val.to_string())
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use clap::Parser;
105
106 #[derive(Parser)]
108 struct CommandParser<T: Args> {
109 #[command(flatten)]
110 args: T,
111 }
112
113 #[test]
114 fn test_args_with_valid_max_tasks() {
115 let args =
116 CommandParser::<PayloadBuilderArgs>::parse_from(["reth", "--builder.max-tasks", "1"])
117 .args;
118 assert_eq!(args.max_payload_tasks, 1)
119 }
120
121 #[test]
122 fn test_args_with_invalid_max_tasks() {
123 assert!(CommandParser::<PayloadBuilderArgs>::try_parse_from([
124 "reth",
125 "--builder.max-tasks",
126 "0"
127 ])
128 .is_err());
129 }
130
131 #[test]
132 fn test_default_extra_data() {
133 let extra_data = default_extra_data();
134 let args = CommandParser::<PayloadBuilderArgs>::parse_from([
135 "reth",
136 "--builder.extradata",
137 extra_data.as_str(),
138 ])
139 .args;
140 assert_eq!(args.extra_data, extra_data);
141 }
142
143 #[test]
144 fn test_invalid_extra_data() {
145 let extra_data = "x".repeat(MAXIMUM_EXTRA_DATA_SIZE + 1);
146 let args = CommandParser::<PayloadBuilderArgs>::try_parse_from([
147 "reth",
148 "--builder.extradata",
149 extra_data.as_str(),
150 ]);
151 assert!(args.is_err());
152 }
153
154 #[test]
155 fn payload_builder_args_default_sanity_check() {
156 let default_args = PayloadBuilderArgs::default();
157 let args = CommandParser::<PayloadBuilderArgs>::parse_from(["reth"]).args;
158 assert_eq!(args, default_args);
159 }
160
161 #[test]
162 fn test_args_with_s_interval() {
163 let args =
164 CommandParser::<PayloadBuilderArgs>::parse_from(["reth", "--builder.interval", "50"])
165 .args;
166 assert_eq!(args.interval, Duration::from_secs(50));
167 }
168
169 #[test]
170 fn test_args_with_ms_interval() {
171 let args =
172 CommandParser::<PayloadBuilderArgs>::parse_from(["reth", "--builder.interval", "50ms"])
173 .args;
174 assert_eq!(args.interval, Duration::from_millis(50));
175 }
176}