Withhold V1 getPayload response inside a safety buffer before the cutoff - #520
Merged
Conversation
A V1 getPayload request accepted right up against the slot cutoff still gets the unblinded payload back over the API, even though the relay's own publish is likely too late to be attested to. A proposer holding two slots in a row could exploit that deliberately: let the relay-published block orphan, then use the leaked payload to unbundle the MEV into their own next-slot block. Add a configurable response-safety buffer (default 800ms) before the cutoff. Requests inside it are still built and published to the beacon client as before, but the API withholds the payload and returns the same too-late error an outright-late request already gets. Issue: #519 (step 1)
vladimir-ea
approved these changes
Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue: #519 (step 1 of 1)
What this PR does
A V1
getPayloadrequest accepted right up against the slot cutoff still gets the unblinded payload back over the API, even though the relay's own publish is likely too late to be attested to. A proposer holding two slots in a row could exploit that deliberately: let the relay-published block orphan, then use the leaked payload to unbundle the MEV into their own next-slot block.Adds a configurable response-safety buffer (
get_payload_v1_response_buffer_ms, default 800ms) before the existing cutoff. A V1 request landing in that window is still built and published to the beacon client exactly as before; the API response withholds the payload and returns the sameGetPayloadRequestTooLateerror an outright-late request already gets, so it's indistinguishable from ordinary lateness. A newget_payload_v1_response_withheld_totalcounter makes this observable.What this PR deliberately does not do
_get_payload) integration tests exercising the beacon-publish/DB side effects — there's no existing mock/test-double infrastructure forProposerApi's dependencies (DbHandle,MultiBeaconClient,GrpcGossiperClientManager,AuctioneerHandle, theApi/ApiProvidertraits) anywhere in the codebase, and building that is its own scope of work (noted as a possible follow-up in the issue).Tests
Written before implementation, reviewed and approved before writing the behavior change. The cutoff/buffer decision is extracted into a small pure function (
evaluate_response_buffer, mirroring the existingstream_windowpattern inheader_stream.rs) with unit tests covering: safely inside the window, just inside the buffer, exactly at the outer cutoff boundary, past the cutoff, and a zero-buffer no-op.Reviewer checklist
lint,unit-test) is green