@@ -5,11 +5,16 @@ use anyhow::{bail, ensure, Result};
5
5
use bls:: PublicKeyBytes ;
6
6
use derive_more:: Constructor ;
7
7
use helper_functions:: { misc, signing:: SignForAllForks } ;
8
+ use http_api_utils:: ETH_CONSENSUS_VERSION ;
8
9
use itertools:: Itertools as _;
9
10
use log:: { debug, info} ;
11
+ use mime:: { APPLICATION_JSON , APPLICATION_OCTET_STREAM } ;
10
12
use prometheus_metrics:: Metrics ;
11
- use reqwest:: { Client , Response , StatusCode } ;
12
- use ssz:: SszHash as _;
13
+ use reqwest:: {
14
+ header:: { ACCEPT , CONTENT_TYPE } ,
15
+ Client , RequestBuilder , Response , StatusCode ,
16
+ } ;
17
+ use ssz:: { SszHash as _, SszRead as _, SszWrite as _} ;
13
18
use thiserror:: Error ;
14
19
use typenum:: Unsigned as _;
15
20
use types:: {
@@ -29,7 +34,7 @@ use crate::{
29
34
combined:: { ExecutionPayloadAndBlobsBundle , SignedBuilderBid } ,
30
35
consts:: BUILDER_PROPOSAL_DELAY_TOLERANCE ,
31
36
unphased:: containers:: SignedValidatorRegistrationV1 ,
32
- BuilderConfig ,
37
+ BuilderApiFormat , BuilderConfig ,
33
38
} ;
34
39
35
40
const REQUEST_TIMEOUT : Duration = Duration :: from_secs ( BUILDER_PROPOSAL_DELAY_TOLERANCE ) ;
@@ -157,19 +162,26 @@ impl Api {
157
162
debug ! ( "getting execution payload header from {url}" ) ;
158
163
159
164
let response = self
160
- . client
161
- . get ( url. into_url ( ) )
162
- . timeout ( REQUEST_TIMEOUT )
165
+ . request_with_accept_header ( self . client . get ( url. into_url ( ) ) . timeout ( REQUEST_TIMEOUT ) )
163
166
. send ( )
164
167
. await ?;
168
+
165
169
let response = handle_error ( response) . await ?;
166
170
167
171
if response. status ( ) == StatusCode :: NO_CONTENT {
168
172
info ! ( "builder has no execution payload header available for slot {slot}" ) ;
169
173
return Ok ( None ) ;
170
174
}
171
175
172
- let builder_bid = response. json :: < SignedBuilderBid < P > > ( ) . await ?;
176
+ let builder_bid = match self . config . builder_api_format {
177
+ BuilderApiFormat :: Json => response. json ( ) . await ?,
178
+ BuilderApiFormat :: Ssz => {
179
+ let phase = http_api_utils:: extract_phase_from_headers ( response. headers ( ) ) ?;
180
+ let bytes = response. bytes ( ) . await ?;
181
+
182
+ SignedBuilderBid :: < P > :: from_ssz ( & phase, & bytes) ?
183
+ }
184
+ } ;
173
185
174
186
debug ! ( "get_execution_payload_header response: {builder_bid:?}" ) ;
175
187
@@ -230,18 +242,33 @@ impl Api {
230
242
let block_root = block. message ( ) . hash_tree_root ( ) ;
231
243
let slot = block. message ( ) . slot ( ) ;
232
244
233
- let response = self
234
- . client
235
- . post ( url. into_url ( ) )
236
- . json ( block)
237
- . timeout ( remaining_time)
238
- . send ( )
239
- . await ?;
245
+ let request = self . request_with_accept_header (
246
+ self . client
247
+ . post ( url. into_url ( ) )
248
+ . timeout ( remaining_time)
249
+ . header ( ETH_CONSENSUS_VERSION , block. phase ( ) . as_ref ( ) ) ,
250
+ ) ;
251
+
252
+ let request = match self . config . builder_api_format {
253
+ BuilderApiFormat :: Json => request. json ( block) ,
254
+ BuilderApiFormat :: Ssz => request
255
+ . header ( CONTENT_TYPE , APPLICATION_OCTET_STREAM . as_ref ( ) )
256
+ . body ( block. to_ssz ( ) ?) ,
257
+ } ;
240
258
259
+ let response = request. send ( ) . await ?;
241
260
let response = handle_error ( response) . await ?;
242
- let response: WithBlobsAndMev < ExecutionPayload < P > , P > = response
243
- . json :: < ExecutionPayloadAndBlobsBundle < P > > ( )
244
- . await ?
261
+
262
+ let response: WithBlobsAndMev < ExecutionPayload < P > , P > =
263
+ match self . config . builder_api_format {
264
+ BuilderApiFormat :: Json => response. json ( ) . await ?,
265
+ BuilderApiFormat :: Ssz => {
266
+ let phase = http_api_utils:: extract_phase_from_headers ( response. headers ( ) ) ?;
267
+ let bytes = response. bytes ( ) . await ?;
268
+
269
+ ExecutionPayloadAndBlobsBundle :: < P > :: from_ssz ( & phase, & bytes) ?
270
+ }
271
+ }
245
272
. into ( ) ;
246
273
247
274
let execution_payload = & response. value ;
@@ -266,6 +293,15 @@ impl Api {
266
293
Ok ( response)
267
294
}
268
295
296
+ fn request_with_accept_header ( & self , request_builder : RequestBuilder ) -> RequestBuilder {
297
+ let accept_header = match self . config . builder_api_format {
298
+ BuilderApiFormat :: Json => APPLICATION_JSON ,
299
+ BuilderApiFormat :: Ssz => APPLICATION_OCTET_STREAM ,
300
+ } ;
301
+
302
+ request_builder. header ( ACCEPT , accept_header. as_ref ( ) )
303
+ }
304
+
269
305
fn url ( & self , path : & str ) -> Result < RedactingUrl > {
270
306
self . config . builder_api_url . join ( path) . map_err ( Into :: into)
271
307
}
@@ -345,6 +381,7 @@ mod tests {
345
381
) -> Result < ( ) , BuilderApiError > {
346
382
let api = BuilderApi :: new (
347
383
BuilderConfig {
384
+ builder_api_format : BuilderApiFormat :: Json ,
348
385
builder_api_url : "http://localhost"
349
386
. parse ( )
350
387
. expect ( "http://localhost should be a valid URL" ) ,
0 commit comments