Add a POST produceBlockV4 - #625
Conversation
c28b8b8 to
ca2cbed
Compare
This PR adds a POST version of produceBlockV4 in which the validator sends a list of `BuilderPreferences` objects. These object specify a per-builder list of preferences that helps the beacon node chose the right payload bid contained in the block to be produced. This solves issue ethereum#620.
ca2cbed to
fea32e4
Compare
|
I was unable to make ACDC earlier today but here is some feedback from my different points of view. Vero validator client maintainer: All per-key configurability in Vero is handled through the Keymanager API which is standardized across all CL/VC clients. This is the only way Vero allows node operators to specify certain things, like different fee recipient per validator key. I hate the idea of adding support for some kind of YAML file (that hasn't even been standardized) instead of doing this in a more standard way using the Keymanager API. We should seriously stop with these CL-client-specific YAML files, we have the standard Keymanager API for a reason. Setting these various per-key per-builder preferences via the Keymanager API presents some challenges but I'd still prefer solving those over the YAML files. Switching CL/VC clients should be easy and different YAML configurations make that unnecessarily hard, whereas the Keymanager API makes it trivial. Staking-as-a-service provider / node operator: We don't expect to use the advanced functionality described in #620 and I also expect 99% of other node operators not to. In practice, I expect the vast majority of node operators will be running a much simpler configuration similar to one of these: A) trustless P2P bids only (no CLI flags, this could be the default clients ship with) Will these 3 configurations actually cover 99% of the node operators? I don't know. But if they do, we should really avoid overcomplicating things. I think we should consider making the 1% of super-advanced node operators that MAY want to run a ton of different builder configurations on different subsets of their keys to simply run a corresponding number of validator client instances with different configurations. If they need more than a few, that might require a bit of automation, but so would populating a YAML file, so I don't think that makes things significantly more difficult for them. Whereas supporting these per-key per-builder configuration settings does add a considerable amount of maintenance burden/tech debt across all CL/VC codebases. Hope this makes sense, ePBS itself is honestly quite complex on its own already so I may have overlooked something. |
|
Agree with @eth2353 on the Keymanager API point, and tried to implement here ethereum/keymanager-APIs/pull/87 with those specs, per-key block-production preferences (fee recipient, gas limit, graffiti, builder list and constraints) as one atomic document per key. The schema follows the BuilderEntry shape from OffchainLabs/prysm#17124 which I think captures the real use cases well (e.g. the optional proxy endpoint, with the builder URL staying the signed identity), and adopts the preference semantics from this PR, including builder_boost_factor and its reserved values. I understand this PR is for VC to BN but since the Keymanager API sits upstream (user/tool to VC) it needs to carry a superset of what the beacon api will use. So standardizing both makes sense + agreed we dont want to have to support N client-specific YAML dialects. ofc the big open question is which knobs are truly per-builder, and which can be per-key only? e.g., max_execution_payment makes sense as per-builder but probably min_bid is more suitable as per-key. In the Keymanager PR I just defaulted to maximal flexibility for now. |
|
Cross-repository sync note from the SSV side: I raised a I think builder-specs should remain normative for construction and signing. This PR only needs to preserve the resulting signed object across the Validator API boundary:
|
Agree, will update (if this PR is the one adopted) when the builder API is stabilized, but I think the Auth data message should not be specified to be the URL but rather arbitrary data that can be negotiated off-band with the builder. Some builders would want to see here an URL, some others something more generic... there is no need to specify this data to be anything in particular. |
Agreed with @shane-moore the canonicalizing should happen as the first step, not after a RequestAuthV1 was made. Also agree with @potuz that opaque auth data bytes is more expressive / future proof. An alt proposal is to drop all notions of "canonicalization" and just use auth data bytes as-is. Cross referencing to the ethereum/keymanager-APIs/pull/87 PR, a builder entry could look like this, which is unambiguous for VC/BN/ DVT setups etc. {
"url": "https://builder-a.example.com",
"auth_data": "0x123123123....", // new
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a",
"proxy": "http://side-car:9001",
"max_execution_payment": "250000000",
"min_bid": "10000000",
"builder_boost_factor": "100"
}The wrinkle is that before, a proxy could extract the builder url from the auth data to route. Since the SignedRequestAuth wouldn't be required to carry this anymore, either:
wdyt about 2? |
|
From the SSV/DVT side, option 2 works. The same builder-provided auth_data for a validator-builder relationship would need to be configured across all operators so they construct the same RequestAuthV1 signing root. The builder URL and proxy destination can remain unsigned routing metadata. If Eth-Builder-Url misroutes the request, the receiving builder should reject it because the exact signed auth_data does not match what it negotiated. So I do not think SSV requires the URL to be included in RequestAuthV1. |
|
Both PRs updated following this convention: |
Don't we need a POST for use-cases B and C? We need to include the This thread on Eth R&D: https://discord.com/channels/595666850260713488/874767108809031740/1522112101714362448 |
|
Following up on @shane-moore's earlier note from the SSV side — we reviewed the convention this thread converged on, now implemented in builder-specs #165 (opaque pre-agreed The reason for that preference: reconstructing a threshold signature requires byte-identical messages from every operator either way, so URL canonicalization only helps if every validator implementation and every builder normalize URLs bit-identically — a silent cross-implementation failure mode. Opaque bytes signed as-is turn it into plain config discipline: distribute the exact per-builder With Putting on record the properties this relies on, so future revisions keep them:
One suggestion: it would help to RECOMMEND a default value for One question: for the ahead-of-time channel ( |
chong-he
left a comment
There was a problem hiding this comment.
Left a small comment below.
On a side note, this is about replacing the current GET to POST endpoint. Maybe we can rename the title a bit? Something like "replace GET with POST"
| @@ -0,0 +1,63 @@ | |||
| BuilderPreferences: | |||
There was a problem hiding this comment.
I wonder should we put this file under the directory types/gloas/builder_preferences.yaml rather than types/builder_preferences.yaml? As BuilderPreferences is a Gloas thing
ethereum/builder-specs@31712da adds clarity that the builder URL SHOULD be used as the default interpretation of the auth |
| authenticate the request. If omitted, requests to this builder are sent without | ||
| authentication. | ||
| url: | ||
| type: string |
There was a problem hiding this comment.
Assuming this BuilderPreferences here is the same as the BuilderPreferencesV1 that is tentatively defined in the current builder-spec.. if we make this change, how are we supposed to ssz serialize / deserialize a BuilderPreferences object which now contains a field of type string?
nflaig
left a comment
There was a problem hiding this comment.
@potuz this PR doesn't solve #620, see my comments (also cc @JasonVranek I left the comments here as this is easier to review than your PR, I think the points apply to both PRs though)
| node MUST forward to the builder when requesting a bid so that the builder can | ||
| authenticate the request. If omitted, requests to this builder are sent without | ||
| authentication. | ||
| url: |
There was a problem hiding this comment.
this PR deviates quite a lot from what you mentioned in #620
so first of all the BuilderPreferences as defined here do not support p2p builders at all, the builder_id concept is missing from this PR. Can you explain why we no longer wanna support this?
| external builders. The `builder_boost_factor` is expressed per builder in the request body | ||
| rather than as a query parameter. |
There was a problem hiding this comment.
The
builder_boost_factoris expressed per builder
today we use this parameter as a global flag (applied to all builders) to let the operator decide their block production strategy
eg. you can set builder_boost_factor=0 which basically says "always pick local unless the EL fails to produce the payload", or builder_boost_factor=90 which says "pick local payload if builder payload(s) are only marginally more valuable"
having builder_boost_factor and also min_bid expressed per builder works with directly connected builders via api, but how does that work with p2p builders?
I think it would be good to have a way to globally apply builder boost factor and min bid
in #620 there is the following statement
The default BuilderPreferences can be sent explicitly as a default to the endpoint or just be assigned to the builder_id being the max uint.
this seems to suggest a way to do this? I don't see it expressed in this PR though
There was a problem hiding this comment.
Definitely an oversight on my side since I based 630 on 625.
To match the current conventions in beacon 630 / builder 165 / keymanager 88, p2p bids would be configurable per-key, per-builder like: "validator X accepts p2p bids from builder Y only above this min_bid, weighted by this builder_boost_factor." I'm personally not sure that's worth it. p2p bids are entirely trustless payments so a Gwei from one p2p builder is the same as a Gwei from another, and there's no trust dimension left to express per builder. The thing you might genuinely want to differentiate on is censorship but you can't read that off a bid. If the node operator's goal is to "blacklist" specific p2p builders, the builders could always get around by rotating keys. Also most builders worth configuring individually would likely also be reachable over the builder API, where the full knob set already applies.
I'm more partial to per-key p2p preferences. The pragmatic version is to bring back something like the builder_boost_factor query param on produceBlockV4 and treat these as per-key p2p knobs. Then either VCs hold them in their own config as a global, or we extend setBuilders here to carry them per key.
Curious what others think.
There was a problem hiding this comment.
"validator X accepts p2p bids from builder Y only above this min_bid, weighted by this builder_boost_factor." I'm personally not sure that's worth it
yes I don't think it's worth it but neither is that worth it on the builder-api, this is not used today and will not be useful after gloas, builder boost factor and also min-bid is very hard to configure even just for local vs. builder payload, having this per builder doesn't seem very useful, but I rather implement that at this point and trying to debate it. But I don't see why we don't wanna support the full spectrum, all that seems to be required is add a builder_pubkey field, and make url optional and if configured the beacon-node reaches out to the builder via api too + enforcing same policy to the bids of that builder on p2p
There was a problem hiding this comment.
Following this thread, I updated #630 and #88 to offer the same config knobs for p2p bids. tl;dr is including builder_pubkey and excluding url means your BuilderEntry supplies min_bid and builder_boost_factor constraints on the p2p bids coming from builder_pubkey so the BN can apply them when determining the winning bid. Keymanager then needed to change to allow defaults to be configured.
There was a problem hiding this comment.
was thinking about this more and getting more convinced about your point of not needing per-BuilderEntry for p2p builders, maybe that also keeps the configuration simpler for users if we only allow global builder_boost_factor and min_bid to affect p2p bids while for directly connected builders we keep the per-builder configs. So since you made changes to support global parameters now, we might be fine with those only and can avoid overloading the usage of builder_pubkey.
one of the reasons is also the following p2p rule
[IGNORE] this bid is the highest value bid seen for the tuple (bid.slot, bid.parent_block_hash, bid.parent_block_root)
since builder_boost_factor and min_bid are local only, it would be kinda random based on how bids are propagated, so even if you boost a certain builder's bid by 100x, it might not even reach your node due to the rule above, so it might be better from a user perspective to see p2p bids as essentially a single (the highest value) bid as coming from a single builder (not sure that framing makes sense).
The use cases outlined by potuz in #620 sound pretty complete to me already, so if we can cover those which I think we can now since we added the global parameters, then we are kinda getting close to the final design we want.
@JasonVranek maybe you can double check this
This PR adds a POST version of produceBlockV4 in which the validator sends a list of
BuilderPreferencesobjects. These object specify a per-builder list of preferences that helps the beacon node chose the right payload bid contained in the block to be produced. This solves issue #620.