Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Submit constraints only after commitments deadline + block template refactor #117

Merged
merged 168 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 161 commits
Commits
Show all changes
168 commits
Select commit Hold shift + click to select a range
4f0e7ad
feat(sidecar): enforce max_commitments_per_slot checks
thedevbirb Jul 4, 2024
a7af8e4
chore(sidecar): add constraints of slots to block template
thedevbirb Jul 4, 2024
692043d
feat(sidecar): add constraints to execution state and send them after…
thedevbirb Jul 4, 2024
21e1f22
chore(sidecar): extra private fields on constraint
thedevbirb Jul 4, 2024
4ae0f0c
feat(sidecar): replace transactions with signed constraints list in b…
thedevbirb Jul 4, 2024
5560c78
wip: fix getpayload response from mev-boost
merklefruit Jul 12, 2024
baa1cb5
wip: save res to file for debugging
merklefruit Jul 12, 2024
0bedfca
fix: get payload response deserialization
merklefruit Jul 12, 2024
fcffacf
chore: minor changes
merklefruit Jul 12, 2024
062c3f9
feat(sidecar): use PooledTransactionsElement to support blob sidecars
mempirate Jul 12, 2024
c168e14
feat(sidecar): integrate PooledTransactionsElement everywhere
mempirate Jul 12, 2024
edf0c6c
fix(sidecar): integration + fix tests
mempirate Jul 12, 2024
f1a5ae1
fix(sidecar): remove stale templates + test
mempirate Jul 12, 2024
2f472df
chore(sidecar): ignore trace call test since it's slow and we're usin…
thedevbirb Jul 15, 2024
b9a9763
chore: addressed review comments
merklefruit Jul 15, 2024
fd3eb6d
chore(sidecar): pre-confirmation -> preconfirmation
thedevbirb Jul 15, 2024
a47ba99
chore(sidecar): retain logic cleanup
thedevbirb Jul 15, 2024
cb71464
fix(sidecar): max commitments reached error
thedevbirb Jul 15, 2024
ea88f7c
chore(sidecar): chain_id method for pooled transaction type
thedevbirb Jul 15, 2024
9a7e967
chore(sidecar): manually decode hex chain_id
thedevbirb Jul 15, 2024
5200f22
fix(mev-boost): test
namn-grg Jul 12, 2024
d34eefc
chore(builder): add sample blob tx
namn-grg Jul 12, 2024
92ba9bd
chore(builder): test fix wip
namn-grg Jul 12, 2024
6fba626
feat(builder): blob merkle multiproof support
namn-grg Jul 15, 2024
3188ef4
fix(builder): preconf test
namn-grg Jul 15, 2024
21095ac
chore(builder): remove testblobtx func
namn-grg Jul 15, 2024
56c7857
fix(devnet): build docker images using --load flag for new docker ver…
thedevbirb Jul 15, 2024
bb469a0
chore(sidecar): validator indexes docs
thedevbirb Jul 15, 2024
58dc11a
feat: populate blobs bundle when self-building
merklefruit Jul 15, 2024
555209f
fix(sidecar): constraint type is skipping raw tx bytes fields
thedevbirb Jul 15, 2024
cb5b5e0
fix(builder): fetch constraints when worker build payload and not on …
thedevbirb Jul 16, 2024
c6057d8
fix(sidecar): enforce invariant on local payload, which is set IFF we…
thedevbirb Jul 16, 2024
c24ebe2
Merge pull request #135 from chainbound/fix/devnet-constraints-deadline
merklefruit Jul 16, 2024
87cbfa5
Merge pull request #132 from chainbound/feat/blobs-bundle-in-local-pa…
merklefruit Jul 16, 2024
86ac3fc
wip: commitment test utils
merklefruit Jul 15, 2024
48a68d1
chore: small nit
merklefruit Jul 15, 2024
87a0ac2
feat: cleaned tests with test_util
merklefruit Jul 15, 2024
52ec1b2
chore: added todo
merklefruit Jul 15, 2024
3d8ca34
chore: added tx validation constantsC
merklefruit Jul 15, 2024
e8e4d9c
feat: added code check
merklefruit Jul 15, 2024
5e3db9e
feat: init code check
merklefruit Jul 15, 2024
6ca40c5
chore: small fixes
merklefruit Jul 16, 2024
edf065c
chore: rename
merklefruit Jul 16, 2024
aa021ab
Merge pull request #130 from chainbound/feat/invalidate-state
thedevbirb Jul 16, 2024
f4cadee
fix(sidecar): same nonce pre-confirmations on the same slot
thedevbirb Jul 16, 2024
a2a00c2
fix(sidecar): use separate get_block_template and remove_block_template
thedevbirb Jul 16, 2024
6e68814
chore(sidecar): docs, fmt
thedevbirb Jul 16, 2024
dc63777
fix(sidecar): cumulated diffs check wip
thedevbirb Jul 16, 2024
7bf5dd8
fix(sidecar): check against cumulated nonce and balance diff + highes…
thedevbirb Jul 17, 2024
e24eebd
chore(sidecar): expected and actual values in nonce validation errors
thedevbirb Jul 17, 2024
0e00315
chore: minor fixes
merklefruit Jul 17, 2024
d99a29e
Merge branch 'fix/sidecar/same-nonce' of github.com:chainbound/bolt-v…
merklefruit Jul 17, 2024
98e58f0
chore(sidecar): move tests
thedevbirb Jul 17, 2024
9b9db34
test(sidecar): invalid inclusion slot
thedevbirb Jul 17, 2024
da4e537
chore: added commitment balance validation multiple test
merklefruit Jul 17, 2024
e9a5c8c
chore: rm duplicated logic of checking intermediate diffs
merklefruit Jul 17, 2024
2d5f807
Merge pull request #138 from chainbound/chore/test-sidecar-balance-mu…
thedevbirb Jul 17, 2024
c83061e
test(sidecar): nonce too high and too low with diffs
thedevbirb Jul 17, 2024
b1cc672
test(sidecar): fix test base fee too low
thedevbirb Jul 17, 2024
4cb7be7
test
thedevbirb Jul 17, 2024
0002ce4
Merge pull request #137 from chainbound/fix/sidecar/same-nonce
thedevbirb Jul 17, 2024
efbd001
feat(sidecar): enforce max_commitments_per_slot checks
thedevbirb Jul 4, 2024
6c0327a
chore(sidecar): add constraints of slots to block template
thedevbirb Jul 4, 2024
4100a70
feat(sidecar): add constraints to execution state and send them after…
thedevbirb Jul 4, 2024
77693d3
chore(sidecar): extra private fields on constraint
thedevbirb Jul 4, 2024
613d0a9
feat(sidecar): replace transactions with signed constraints list in b…
thedevbirb Jul 4, 2024
bcbfd17
wip: fix getpayload response from mev-boost
merklefruit Jul 12, 2024
eaffe0b
wip: save res to file for debugging
merklefruit Jul 12, 2024
e472775
fix: get payload response deserialization
merklefruit Jul 12, 2024
faa0fc2
chore: minor changes
merklefruit Jul 12, 2024
40da715
feat(sidecar): use PooledTransactionsElement to support blob sidecars
mempirate Jul 12, 2024
f2d17a2
feat(sidecar): integrate PooledTransactionsElement everywhere
mempirate Jul 12, 2024
1ea93e9
fix(sidecar): integration + fix tests
mempirate Jul 12, 2024
66879ed
fix(sidecar): remove stale templates + test
mempirate Jul 12, 2024
d61e8a7
chore(sidecar): ignore trace call test since it's slow and we're usin…
thedevbirb Jul 15, 2024
5c48b8e
chore: addressed review comments
merklefruit Jul 15, 2024
9a53ded
chore(sidecar): pre-confirmation -> preconfirmation
thedevbirb Jul 15, 2024
34021ea
chore(sidecar): retain logic cleanup
thedevbirb Jul 15, 2024
8084f93
fix(sidecar): max commitments reached error
thedevbirb Jul 15, 2024
d4418b5
chore(sidecar): chain_id method for pooled transaction type
thedevbirb Jul 15, 2024
6a00784
chore(sidecar): manually decode hex chain_id
thedevbirb Jul 15, 2024
0e5ffa7
chore(sidecar): validator indexes docs
thedevbirb Jul 15, 2024
9204aef
fix(sidecar): constraint type is skipping raw tx bytes fields
thedevbirb Jul 15, 2024
9ff23ce
fix(builder): fetch constraints when worker build payload and not on …
thedevbirb Jul 16, 2024
2a41483
fix(sidecar): enforce invariant on local payload, which is set IFF we…
thedevbirb Jul 16, 2024
79b3853
feat: populate blobs bundle when self-building
merklefruit Jul 15, 2024
c175f89
wip: commitment test utils
merklefruit Jul 15, 2024
a7cafca
chore: small nit
merklefruit Jul 15, 2024
95902fd
feat: cleaned tests with test_util
merklefruit Jul 15, 2024
816cd5a
chore: added todo
merklefruit Jul 15, 2024
c23b69c
chore: added tx validation constantsC
merklefruit Jul 15, 2024
07876bd
feat: added code check
merklefruit Jul 15, 2024
5bff4b5
feat: init code check
merklefruit Jul 15, 2024
ed7ab9c
chore: small fixes
merklefruit Jul 16, 2024
4064db6
chore: rename
merklefruit Jul 16, 2024
1af755b
fix(sidecar): same nonce pre-confirmations on the same slot
thedevbirb Jul 16, 2024
8eae331
fix(sidecar): use separate get_block_template and remove_block_template
thedevbirb Jul 16, 2024
5d940ee
chore(sidecar): docs, fmt
thedevbirb Jul 16, 2024
ef1d6cf
fix(sidecar): cumulated diffs check wip
thedevbirb Jul 16, 2024
75893d9
fix(sidecar): check against cumulated nonce and balance diff + highes…
thedevbirb Jul 17, 2024
a9e386a
chore: minor fixes
merklefruit Jul 17, 2024
10969d5
chore(sidecar): expected and actual values in nonce validation errors
thedevbirb Jul 17, 2024
7ddb67a
chore(sidecar): move tests
thedevbirb Jul 17, 2024
5cbf434
test(sidecar): invalid inclusion slot
thedevbirb Jul 17, 2024
1862cc4
chore: added commitment balance validation multiple test
merklefruit Jul 17, 2024
9351357
chore: rm duplicated logic of checking intermediate diffs
merklefruit Jul 17, 2024
2f9afcd
test(sidecar): nonce too high and too low with diffs
thedevbirb Jul 17, 2024
17d7752
test(sidecar): fix test base fee too low
thedevbirb Jul 17, 2024
0d1ce4a
test
thedevbirb Jul 17, 2024
8940909
chore: rabbit review
merklefruit Jul 17, 2024
5a81ddf
feat(sidecar): enforce max_commitments_per_slot checks
thedevbirb Jul 4, 2024
f6c093e
chore(sidecar): add constraints of slots to block template
thedevbirb Jul 4, 2024
e4d5c0c
feat(sidecar): add constraints to execution state and send them after…
thedevbirb Jul 4, 2024
6fc37a1
chore(sidecar): extra private fields on constraint
thedevbirb Jul 4, 2024
238aee3
feat(sidecar): replace transactions with signed constraints list in b…
thedevbirb Jul 4, 2024
519c9b3
wip: fix getpayload response from mev-boost
merklefruit Jul 12, 2024
22e088a
wip: save res to file for debugging
merklefruit Jul 12, 2024
2ec0fe2
fix: get payload response deserialization
merklefruit Jul 12, 2024
b065100
chore: minor changes
merklefruit Jul 12, 2024
488323f
feat(sidecar): use PooledTransactionsElement to support blob sidecars
mempirate Jul 12, 2024
03367e0
feat(sidecar): integrate PooledTransactionsElement everywhere
mempirate Jul 12, 2024
bd359d2
fix(sidecar): integration + fix tests
mempirate Jul 12, 2024
0f47129
fix(sidecar): remove stale templates + test
mempirate Jul 12, 2024
46118a0
chore(sidecar): ignore trace call test since it's slow and we're usin…
thedevbirb Jul 15, 2024
68a8464
chore: addressed review comments
merklefruit Jul 15, 2024
4f32d1c
chore(sidecar): pre-confirmation -> preconfirmation
thedevbirb Jul 15, 2024
e95848e
chore(sidecar): retain logic cleanup
thedevbirb Jul 15, 2024
d1049f9
fix(sidecar): max commitments reached error
thedevbirb Jul 15, 2024
d7d1c3d
chore(sidecar): chain_id method for pooled transaction type
thedevbirb Jul 15, 2024
6d59ae9
chore(sidecar): manually decode hex chain_id
thedevbirb Jul 15, 2024
c3d3f2f
chore(sidecar): validator indexes docs
thedevbirb Jul 15, 2024
56472e6
fix(sidecar): constraint type is skipping raw tx bytes fields
thedevbirb Jul 15, 2024
33c7e0b
fix(builder): fetch constraints when worker build payload and not on …
thedevbirb Jul 16, 2024
c23b271
fix(sidecar): enforce invariant on local payload, which is set IFF we…
thedevbirb Jul 16, 2024
af28721
feat: populate blobs bundle when self-building
merklefruit Jul 15, 2024
a8f6d02
wip: commitment test utils
merklefruit Jul 15, 2024
b1fea68
chore: small nit
merklefruit Jul 15, 2024
ab3cdbe
feat: cleaned tests with test_util
merklefruit Jul 15, 2024
084ac17
chore: added todo
merklefruit Jul 15, 2024
f72ebc2
chore: added tx validation constantsC
merklefruit Jul 15, 2024
c76c29c
feat: added code check
merklefruit Jul 15, 2024
62805cc
feat: init code check
merklefruit Jul 15, 2024
669d641
chore: small fixes
merklefruit Jul 16, 2024
ca62c3e
chore: rename
merklefruit Jul 16, 2024
57ca5b6
fix(sidecar): same nonce pre-confirmations on the same slot
thedevbirb Jul 16, 2024
bd070ae
fix(sidecar): use separate get_block_template and remove_block_template
thedevbirb Jul 16, 2024
0b5ae0f
chore(sidecar): docs, fmt
thedevbirb Jul 16, 2024
a3b15c8
fix(sidecar): cumulated diffs check wip
thedevbirb Jul 16, 2024
4aab0d6
fix(sidecar): check against cumulated nonce and balance diff + highes…
thedevbirb Jul 17, 2024
5416012
chore: minor fixes
merklefruit Jul 17, 2024
d0da8e0
chore(sidecar): expected and actual values in nonce validation errors
thedevbirb Jul 17, 2024
6cd400a
chore(sidecar): move tests
thedevbirb Jul 17, 2024
f8fa5c4
test(sidecar): invalid inclusion slot
thedevbirb Jul 17, 2024
7da5bca
chore: added commitment balance validation multiple test
merklefruit Jul 17, 2024
3356699
chore: rm duplicated logic of checking intermediate diffs
merklefruit Jul 17, 2024
53d5a39
test(sidecar): nonce too high and too low with diffs
thedevbirb Jul 17, 2024
4fa0d90
test(sidecar): fix test base fee too low
thedevbirb Jul 17, 2024
ddb69b8
test
thedevbirb Jul 17, 2024
882c68c
chore: rabbit review
merklefruit Jul 17, 2024
13723af
Merge branch 'fix/sidecar/constraints-deadline' of github.com:chainbo…
merklefruit Jul 17, 2024
abf9eb8
chore: small fixes
merklefruit Jul 17, 2024
1b563b8
feat(bolt-spammer): alloy migration
thedevbirb Jul 17, 2024
487d06a
feat(spammer-helder): bump reth-primitives version
thedevbirb Jul 17, 2024
4790102
fix: spammer-helder signing fixes
merklefruit Jul 18, 2024
d34f41b
choore: rm unused dependency
merklefruit Jul 18, 2024
30d8e98
chore: update docs
merklefruit Jul 18, 2024
9fc9bb0
fix: set tx nonce and sender
merklefruit Jul 18, 2024
67d5c3c
Merge pull request #142 from chainbound/feat/spammer/alloy
merklefruit Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 34 additions & 29 deletions bolt-sidecar/bin/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ async fn main() -> eyre::Result<()> {
let signer = Signer::new(config.private_key.clone().unwrap());

let state_client = StateClient::new(config.execution_api_url.clone());
let mut execution_state = ExecutionState::new(state_client).await?;
let mut execution_state =
ExecutionState::new(state_client, config.limits.max_commitments_per_slot).await?;

let mevboost_client = MevBoostClient::new(config.mevboost_url.clone());
let beacon_client = BeaconClient::new(config.beacon_api_url.clone());
Expand Down Expand Up @@ -66,54 +67,40 @@ async fn main() -> eyre::Result<()> {
loop {
tokio::select! {
Some(ApiEvent { request, response_tx }) = api_events_rx.recv() => {
tracing::info!("Received commitment request: {:?}", request);
let start = std::time::Instant::now();

let validator_index = match consensus_state.validate_request(&request) {
Ok(index) => index,
Err(e) => {
tracing::error!("Failed to validate request: {:?}", e);
tracing::error!(err = ?e, "Failed to validate request");
let _ = response_tx.send(Err(ApiError::Custom(e.to_string())));
continue;
}
};

if let Err(e) = execution_state
.try_commit(&request)
.await
{
tracing::error!("Failed to commit request: {:?}", e);
if let Err(e) = execution_state.validate_commitment_request(&request).await {
tracing::error!(err = ?e, "Failed to commit request");
let _ = response_tx.send(Err(ApiError::Custom(e.to_string())));
continue;
}
};

// TODO: match when we have more request types
let CommitmentRequest::Inclusion(request) = request;

tracing::info!(
elapsed = ?start.elapsed(),
tx_hash = %request.tx.hash(),
"Validation against execution state passed"
);

// parse the request into constraints and sign them with the sidecar signer
let message = ConstraintsMessage::build(validator_index, request.slot, request);
// TODO: review all this `clone` usage

// parse the request into constraints and sign them with the sidecar signer
let slot = request.slot;
let message = ConstraintsMessage::build(validator_index, request);
let signature = signer.sign(&message.digest())?.to_string();
let signed_constraints = vec![SignedConstraints { message, signature }];
let signed_constraints = SignedConstraints { message, signature };

// TODO: fix retry logic
let max_retries = 5;
let mut i = 0;
'inner: while let Err(e) = mevboost_client
.submit_constraints(&signed_constraints)
.await
{
tracing::error!(err = ?e, "Error submitting constraints, retrying...");
tokio::time::sleep(Duration::from_millis(100)).await;
i+=1;
if i >= max_retries {
break 'inner
}
}
execution_state.add_constraint(slot, signed_constraints.clone());
merklefruit marked this conversation as resolved.
Show resolved Hide resolved
merklefruit marked this conversation as resolved.
Show resolved Hide resolved

let res = serde_json::to_value(signed_constraints).map_err(Into::into);
let _ = response_tx.send(res).ok();
Expand All @@ -133,12 +120,30 @@ async fn main() -> eyre::Result<()> {
Some(slot) = consensus_state.commitment_deadline.wait() => {
tracing::info!(slot, "Commitment deadline reached, starting to build local block");

let Some(template) = execution_state.get_block_template(slot) else {
let Some(template) = execution_state.remove_block_template(slot) else {
tracing::warn!("No block template found for slot {slot} when requested");
continue;
};

if let Err(e) = local_builder.build_new_local_payload(template.transactions).await {
tracing::trace!(?template.signed_constraints_list, "Submitting constraints to MEV-Boost");

// TODO: fix retry logic, and move this to separate task
let max_retries = 5;
let mut i = 0;
'inner: while let Err(e) = mevboost_client
.submit_constraints(&template.signed_constraints_list)
.await
{
tracing::error!(err = ?e, "Error submitting constraints, retrying...");
tokio::time::sleep(Duration::from_millis(100)).await;
i+=1;
if i >= max_retries {
tracing::error!("Max retries reached while submitting to MEV-Boost");
break 'inner
}
}
Comment on lines +128 to +144
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retry Logic for Submitting Constraints

The retry logic for submitting constraints to mevboost_client is a crucial addition. However, the fixed delay and retry count could be made configurable to adapt to different network conditions or operational requirements.

- let max_retries = 5;
- let mut i = 0;
+ let max_retries = config.retry.max_retries;
+ let mut i = config.retry.initial_retry_count;
- tokio::time::sleep(Duration::from_millis(100)).await;
+ tokio::time::sleep(config.retry.retry_delay).await;

Make the retry logic parameters configurable through the system configuration.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tracing::trace!(?template.signed_constraints_list, "Submitting constraints to MEV-Boost");
// TODO: fix retry logic, and move this to separate task
let max_retries = 5;
let mut i = 0;
'inner: while let Err(e) = mevboost_client
.submit_constraints(&template.signed_constraints_list)
.await
{
tracing::error!(err = ?e, "Error submitting constraints, retrying...");
tokio::time::sleep(Duration::from_millis(100)).await;
i+=1;
if i >= max_retries {
tracing::error!("Max retries reached while submitting to MEV-Boost");
break 'inner
}
}
tracing::trace!(?template.signed_constraints_list, "Submitting constraints to MEV-Boost");
// TODO: fix retry logic, and move this to separate task
let max_retries = config.retry.max_retries;
let mut i = config.retry.initial_retry_count;
'inner: while let Err(e) = mevboost_client
.submit_constraints(&template.signed_constraints_list)
.await
{
tracing::error!(err = ?e, "Error submitting constraints, retrying...");
tokio::time::sleep(config.retry.retry_delay).await;
i+=1;
if i >= max_retries {
tracing::error!("Max retries reached while submitting to MEV-Boost");
break 'inner
}
}

Comment on lines +123 to +144
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor retry logic for constraint submission.

The retry logic for submitting constraints is critical but the implementation can be improved. Making retry parameters configurable, as suggested in the previous review, would allow the system to adapt better to various network conditions.

- let max_retries = 5;
- let mut i = 0;
+ let max_retries = config.retry.max_retries;
+ let mut i = config.retry.initial_retry_count;
- tokio::time::sleep(Duration::from_millis(100)).await;
+ tokio::time::sleep(config.retry.retry_delay).await;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let Some(template) = execution_state.remove_block_template(slot) else {
tracing::warn!("No block template found for slot {slot} when requested");
continue;
};
if let Err(e) = local_builder.build_new_local_payload(template.transactions).await {
tracing::trace!(?template.signed_constraints_list, "Submitting constraints to MEV-Boost");
// TODO: fix retry logic, and move this to separate task
let max_retries = 5;
let mut i = 0;
'inner: while let Err(e) = mevboost_client
.submit_constraints(&template.signed_constraints_list)
.await
{
tracing::error!(err = ?e, "Error submitting constraints, retrying...");
tokio::time::sleep(Duration::from_millis(100)).await;
i+=1;
if i >= max_retries {
tracing::error!("Max retries reached while submitting to MEV-Boost");
break 'inner
}
}
let Some(template) = execution_state.remove_block_template(slot) else {
tracing::warn!("No block template found for slot {slot} when requested");
continue;
};
tracing::trace!(?template.signed_constraints_list, "Submitting constraints to MEV-Boost");
// TODO: fix retry logic, and move this to separate task
let max_retries = config.retry.max_retries;
let mut i = config.retry.initial_retry_count;
'inner: while let Err(e) = mevboost_client
.submit_constraints(&template.signed_constraints_list)
.await
{
tracing::error!(err = ?e, "Error submitting constraints, retrying...");
tokio::time::sleep(config.retry.retry_delay).await;
i+=1;
if i >= max_retries {
tracing::error!("Max retries reached while submitting to MEV-Boost");
break 'inner
}
}


if let Err(e) = local_builder.build_new_local_payload(&template).await {
tracing::error!(err = ?e, "CRITICAL: Error while building local payload at slot deadline for {slot}");
};
},
Expand Down
48 changes: 25 additions & 23 deletions bolt-sidecar/src/api/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ use std::{sync::Arc, time::Duration};
use tokio::net::TcpListener;

use super::spec::{
BuilderApi, BuilderApiError, ConstraintsApi, GET_HEADER_PATH, GET_PAYLOAD_PATH,
REGISTER_VALIDATORS_PATH, STATUS_PATH,
BuilderApiError, ConstraintsApi, GET_HEADER_PATH, GET_PAYLOAD_PATH, REGISTER_VALIDATORS_PATH,
STATUS_PATH,
};
use crate::{
client::mevboost::MevBoostClient,
Expand All @@ -35,11 +35,9 @@ const GET_HEADER_WITH_PROOFS_TIMEOUT: Duration = Duration::from_millis(500);

/// A proxy server for the builder API.
/// Forwards all requests to the target after interception.
pub struct BuilderProxyServer<T: BuilderApi, P> {
pub struct BuilderProxyServer<T, P> {
proxy_target: T,
// TODO: fill with local payload when we fetch a payload
// in failed get_header
// This will only be some in case of a failed get_header
/// INVARIANT: This will be `Some` IFF we have signed a local header for the latest slot.
local_payload: Mutex<Option<GetPayloadResponse>>,
/// The payload fetcher to get locally built payloads.
payload_fetcher: P,
Expand Down Expand Up @@ -127,7 +125,12 @@ where
Ok(res) => match res {
Err(builder_err) => builder_err,
Ok(header) => {
tracing::debug!(elapsed = ?start.elapsed(), "Returning signed builder bid: {:?}", header);
// Clear the local payload cache if we have a successful response
// By definition of `server.local_payload`, this will be `Some` IFF we have signed a local header
let mut local_payload = server.local_payload.lock();
*local_payload = None;

Comment on lines +128 to +132
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider handling the local payload cache more robustly.

Clearing the local payload cache upon a successful response is a crucial operation. However, the current implementation does not handle potential race conditions or errors that might occur before this point. Consider adding error handling or a recovery mechanism to ensure the cache is consistent even in the face of errors.

- *local_payload = None;
+ if let Err(e) = *local_payload.take() {
+     tracing::error!("Failed to clear local payload: {:?}", e);
+ }

Committable suggestion was skipped due to low confidence.

tracing::debug!(elapsed = ?start.elapsed(), "Returning signed builder bid");
return Ok(Json(header));
}
},
Expand All @@ -137,33 +140,31 @@ where
// On ANY error, we fall back to locally built block
tracing::warn!(slot, elapsed = ?start.elapsed(), err = ?err, "Proxy error, fetching local payload instead");

let payload = match server.payload_fetcher.fetch_payload(slot).await {
Some(payload) => {
tracing::info!(elapsed = ?start.elapsed(), "Fetched local payload for slot {slot}");
payload
}
let payload_and_bid = match server.payload_fetcher.fetch_payload(slot).await {
Some(payload_and_bid) => payload_and_bid,
None => {
// TODO: handle failure? In this case, we don't have a fallback block
// which means we haven't made any commitments. This means the beacon client should
// which means we haven't made any commitments. This means the EL should
Comment on lines +143 to +147
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error handling for local payload production failures.

The current implementation logs an error when no local payload is produced, but it does not attempt to recover or provide an alternative response. This might leave the system in an inconsistent state if it frequently occurs.

- tracing::error!("No local payload produced for slot {slot}");
+ tracing::error!("No local payload produced for slot {slot}, attempting to recover...");
+ // Add recovery logic here

Committable suggestion was skipped due to low confidence.

// fallback to local block building.
tracing::error!("No local payload produced for slot {slot}");
return Err(BuilderApiError::FailedToFetchLocalPayload(slot));
}
};

let hash = payload.bid.message.header.block_hash.clone();
let number = payload.bid.message.header.block_number;
let hash = payload_and_bid.bid.message.header.block_hash.clone();
let number = payload_and_bid.bid.message.header.block_number;
tracing::info!(elapsed = ?start.elapsed(), %hash, "Fetched local payload for slot {slot}");

{
// Set the payload for the following get_payload request
// Since we've signed a local header, set the payload for
// the following `get_payload` request.
let mut local_payload = server.local_payload.lock();
*local_payload = Some(payload.payload);
*local_payload = Some(payload_and_bid.payload);
}

let versioned_bid = VersionedValue::<SignedBuilderBid> {
version: Fork::Deneb,
data: payload.bid,
// TODO: a more elegant way to do this? Can we avoid this meta field?
data: payload_and_bid.bid,
meta: Default::default(),
};

Expand Down Expand Up @@ -192,7 +193,8 @@ where
e
})?;

// If we have a locally built payload, return it and clear the cache.
// If we have a locally built payload, it means we signed a local header.
// Return it and clear the cache.
if let Some(payload) = server.local_payload.lock().take() {
let requested_block = &signed_blinded_block
.message
Expand All @@ -204,8 +206,8 @@ where
// beacon node has signed, we are at risk of equivocation and slashing.
if payload.block_hash() != requested_block {
tracing::error!(
expected = requested_block.to_string(),
have = payload.block_hash().to_string(),
expected = %requested_block.to_string(),
have = %payload.block_hash().to_string(),
"Local block hash does not match requested block hash"
);

Expand All @@ -215,7 +217,7 @@ where
});
};

tracing::info!("Local block found, returning: {payload:?}");
tracing::debug!("Local block found, returning: {payload:?}");
return Ok(Json(payload));
}

Expand Down
5 changes: 5 additions & 0 deletions bolt-sidecar/src/api/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub enum BuilderApiError {
InvalidFork(String),
#[error("Invalid local payload block hash. expected: {expected}, got: {have}")]
InvalidLocalPayloadBlockHash { expected: String, have: String },
#[error("Generic error: {0}")]
Generic(String),
}

impl IntoResponse for BuilderApiError {
Expand Down Expand Up @@ -110,6 +112,9 @@ impl IntoResponse for BuilderApiError {
BuilderApiError::InvalidLocalPayloadBlockHash { .. } => {
(StatusCode::BAD_REQUEST, self.to_string()).into_response()
}
BuilderApiError::Generic(err) => {
(StatusCode::INTERNAL_SERVER_ERROR, Json(err)).into_response()
}
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions bolt-sidecar/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use ethereum_consensus::{
ssz::prelude::{List, MerkleizationError},
};
use payload_builder::FallbackPayloadBuilder;
use reth_primitives::TransactionSigned;
use signature::sign_builder_message;

use crate::{
Expand Down Expand Up @@ -99,8 +98,11 @@ impl LocalBuilder {
///
pub async fn build_new_local_payload(
&mut self,
transactions: Vec<TransactionSigned>,
template: &BlockTemplate,
) -> Result<(), BuilderError> {
let transactions = template.as_signed_transactions();
let blobs_bundle = template.as_blobs_bundle();

// 1. build a fallback payload with the given transactions, on top of
// the current head of the chain
let sealed_block = self
Expand All @@ -119,8 +121,7 @@ impl LocalBuilder {
let eth_payload = compat::to_consensus_execution_payload(&sealed_block);
let payload_and_blobs = PayloadAndBlobs {
execution_payload: eth_payload,
// TODO: add included blobs here
blobs_bundle: Default::default(),
blobs_bundle,
};

// 2. create a signed builder bid with the sealed block header we just created
Expand Down
2 changes: 1 addition & 1 deletion bolt-sidecar/src/builder/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use tree_hash_derive::TreeHash;

use crate::ChainConfig;

/// Sign a SSZ object a BLS secret key, using the Application Builder domain
/// Sign a SSZ object with a BLS secret key, using the Application Builder domain
/// for signing arbitrary builder-api messages in the out-of-protocol specifications.
///
/// Fun Note: we use a `blst` secret key to sign a message, and produce an `alloy` signature,
Expand Down
15 changes: 11 additions & 4 deletions bolt-sidecar/src/builder/state_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@ mod tests {
use partial_mpt::StateTrie;
use reqwest::Url;

use crate::{builder::CallTraceManager, client::rpc::RpcClient};
use crate::{
builder::CallTraceManager, client::rpc::RpcClient, test_util::try_get_execution_api_url,
};

#[ignore]
#[tokio::test]
async fn test_trace_call() -> eyre::Result<()> {
dotenvy::dotenv().ok();
tracing_subscriber::fmt::init();
let _ = tracing_subscriber::fmt::try_init();

let Some(rpc_url) = try_get_execution_api_url().await else {
tracing::warn!("EL_RPC not reachable, skipping test");
return Ok(());
};

tracing::info!("Starting test_trace_call");

let rpc_url = std::env::var("RPC_URL").expect("RPC_URL must be set");
let rpc_url = Url::parse(&rpc_url).unwrap();
let rpc_url = Url::parse(rpc_url).unwrap();
let client = RpcClient::new(rpc_url.clone());

let (call_trace_manager, call_trace_handler) = CallTraceManager::new(rpc_url);
Expand Down
Loading
Loading