mev-boost icon indicating copy to clipboard operation
mev-boost copied to clipboard

Identifier for builder to proposer transaction

Open beetrootkid opened this issue 2 years ago • 25 comments

Current Understanding

  • The Builder will set their own feeRecipient for a block
  • They will then send a share of their earnings to the proposer (to their specified "feeRecipient" account in a transaction

The Problem For a staking provider with multiple customers, it is difficult to: a) Prove to customers they have received the agreed upon earning. b) Know how much to charge customers (if fees are based on a percentage of rewards)

Some kind of indicator or marker on the transaction would be great 🙏

Update

Continuation of the discussion here: https://collective.flashbots.net/t/block-scoring-for-mev-boost-relays/202

beetrootkid avatar Jul 20 '22 07:07 beetrootkid

Agreed that having a simple method for querying how much the proposer was paid for a block is desirable for staking providers.

It could be as simple as requiring the last transaction in a block to be a transfer to the proposer's specified feeRecipient. This would preclude other methods of paying the proposer such as via a contract but would be the easiest to query. The relay would need to only count this last transaction when it calculates the value field for BidTrace to avoid a situation where builders simply ignore this convention and send the payment via other means.

kanewallmann avatar Jul 21 '22 06:07 kanewallmann

An alternative to requiring the last transaction in a block to be a transfer to the proposer's feeRecipient could be to require a payment is made to a specific contract in a block designed to facilitate payments and make them visible. That contract would 1. pass the payment on to the feeRecipient and 2. emit a log.

Something like this:

event BuilderPayment(uint256 value, address feeRecipient, address builderAddress);

function makePayment(address _feeRecipient) external payable {
    _feeRecipient.call{value: msg.value}("");
    emit BuilderPayment(msg.value, _feeRecipient, msg.sender);
}

This would emits logs with some structure that would make monitoring a bit easier, I think. It adds a little overhead (~10k gas?) but I think it'd be worth it.

bertmiller avatar Jul 21 '22 13:07 bertmiller

Thanks @bertmiller,

Using send instead of call might be a better (or manually specifying some gas). If the fee recipient can't process the payment with the 2300 gas stipend then the transfer fails but the event is still emitted. Otherwise, there's some extra edge cases where the feeRecipient tries to consume too much gas.

It also adds some complexity to the relay. The relay would need to scan for these events and sum them to calculate the value field for BidTrace. If the relay uses the naive approach of calculating the delta of balances before and after the block then builders have no incentive to call this contract vs just a regular transfer.

kanewallmann avatar Jul 21 '22 21:07 kanewallmann

Builders setting their own address as the feeRecipient in the block header and including a transaction to the proposer is the correct implementation approach. The gas overhead is warranted and can be enforced by relays and by the mev-boost implementation through payment proofs. See #99

The main question is if the payment proofs should be on the builder to proposer transaction only or on the total balance change of the proposer's address.

Payment proof at transaction level

This can be implemented using a simple Merkle proof on the transaction trie. Routing through a contract as suggested by @bertmiller seems preferable. Replay / reorg protection needs to be added to these transactions.

The drawback of this solution is that it does not "accurately" measure block value. In particular, it will not capture payment transaction by end users directly to the proposer which bypass the payment contract. Does this undermine the incentive compatibility of mev-boost or allow for implementation of undesirable features like reorg bribery at the smart contract level?

Payment proof at the account balance level

This approach to payment proofs looks at the total difference in balance of the proposer's eth account between the previous block and the current block. This can be implemented with two Merkle proofs.

The drawback of this solution is that any withdrawals from the proposer's account balance will count against the value of the block. This means the validator will need to use a new account for every block, causing issues with notifying block builders of their desired fee recipient address, or will need to only withdraw from their account on blocks proposed by other validators.

thegostep avatar Jul 22 '22 13:07 thegostep

Here is some additional context on the various payment methods which can be used in mev-boost.

For clarity, lets refer to the fee recipient address set by the block builder in the block header as feeRecipient with the understanding that the builder will set this as their own address for the blocks they produce. We can refer to the payment address requested by the validator as the proposerAccount.

  • base_fee: amount defined by protocol, always paid from user --> network, user can define upper bound they are willing to pay using maxFeePerGas , the user needs to have sufficient ETH balance in their account before transaction execution - this is not MEV since there is no way for the validator to get this payment. The payment cannot be made within of EVM execution (unfortunately).
  • priority_fee: amount defined by the user as maxPriorityFeePerGas, always paid from user --> feeRecipient. The user needs to have sufficient ETH balance in their account before transaction execution. The priority fee is debited from the user account and credited to the fee recipient immediately after execution. The block builder is expected to collect this payment
  • block.coinbase transfer: the EVM has an opcode which allows end user to send funds directly to the feeRecipient address from the block header. The recipient of this payment is the same as the priority_fee payment recipient, but this transfer triggers EVM execution including associated gas costs and security risks. The block builder is expected to collect this payment
  • proposerAccount transfer: anyone can create a transaction which sends eth to any other Ethereum account. Since the proposer is publishing their desired payment address to the public, it's possible for any user to send payments directly to this account. While this is technically MEV, there is no way for the block builder to capture this value! This is the suggested method for the builder to pay the validator for the blockspace used.

thegostep avatar Jul 22 '22 17:07 thegostep

I'd probably argue that proofs or verification that the payment was what was promised sit in a different issue (and potentially should be handled by the consensus layer client(?)). The objective of this request was for their to be an easy way to identify the payment on-chain.

beetrootkid avatar Jul 27 '22 15:07 beetrootkid

On further thinking, the most obvious addition would be a payload containing the proposer's (validator's) public key... but then that might create all kinds of privacy issues? However, most (if not all) of that information is already on-chain but in multiple places... Anyhow, just thought I'd note the idea here. 🙏

beetrootkid avatar Aug 03 '22 13:08 beetrootkid

The Flashbots relays offer a data API that provides summaries for all delivered payloads: https://flashbots.notion.site/Relay-API-Spec-5fb0819366954962bc02e81cb33840f5#417abe417dde45caaff3dc15aaae65dd

The summaries include the value that the relay confirmed by simulating the block. Here's an example for the trace of a specific slot: https://builder-relay-ropsten.flashbots.net/relay/v1/data/bidtraces/proposer_payload_delivered?slot=467483

Node operators and staking pools are using this to know about the proposer value of payloads that were delivered to the proposers, after they sent their signed blinded beacon block.

This API is live on all our relays.

metachris avatar Aug 03 '22 19:08 metachris

hmm.. .I mean that works, of course, but I know that we (and I suspect this is a broader principle across the ecosystem) rely solely upon on-chain data rather logging or api reporting for data on validator activity.

beetrootkid avatar Aug 04 '22 07:08 beetrootkid

this data should be registered on-chain and not rely on an API for it.

ricardolyn avatar Aug 04 '22 08:08 ricardolyn

Agree that having all the data needed for checking on-chain would be preferable.

For that to work, wouldn't the validator registrations also need to be on-chain, to know the feeRecipient the validator requested the payment to? Otherwise you wouldn't have any knowledge which feeRecipient address the proposer wanted to receive the payment at 🤔

metachris avatar Aug 04 '22 11:08 metachris

I think that feels more like proof it was paid where it should’ve been paid. All this issue was intended to focus on was understanding that something was paid for successfully proposing.

beetrootkid avatar Aug 04 '22 11:08 beetrootkid

We've released a builder to proposer payment proposal for this issue. Actively seeking feedback - @terencechain @beetrootkid @ralexstokes @kanewallmann

Here's a summary:

Framing the problem

Staking providers and node operators outlined a need for an accounting mechanism for MEV payments. This mechanism unlocks a number of necessary operations, such as proving receipt of MEV rewards, fair distribution of rewards to staking customers, and preventing malicious activities, such as MEV hiding, mev-penalty-system, among others.

Framing the solution

To enable these operations, and after consideration of several solutions, Flashbots proposes implementation of a smart contract that transfers payment from the builder to the proposer via the following process:

  1. The builder sets their own address as feeRecipient of the payload header they are constructing.
  2. The builder includes a transaction which pays ETH to the proposer’s feeRecipient **address through a smart contract.

We propose a standardized specification for how payments are made and how proofs are provided by builders to the relay. We refer to this solution as smart contract based payments to validators.

Specification

Parameters for the message submitted by builder to the relay:

  • payloadHeader: [ExecutionPayloadHeaderV1](https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/beacon-chain.md#executionpayloadheader)
  • paymentProof:
    • transactionHash(bytes32): hash of the transaction which emits the event
    • transactionIndex(uint256): index of the transaction in the transaction array of the payload
    • logIndex(uint256): index of the event in the transaction
    • value(uint256): amount of ETH being transferred and emitted in the event

Suggested smart contract payment implementation

event BuilderPayment(uint256 value, address validator, address builder);

function pay(address payable validator, uint256 gas) external payable {
		require(msg.sender == block.coinbase);
    validator.transfer(msg.value);
    emit BuilderPayment(msg.value, validator, block.coinbase);
}

function payAndCall(address validator, uint256 gas, bytes calldata data) external payable {
		require(msg.sender == block.coinbase);	
		validator.call{gas: gas, value: msg.value}(data);
		emit BuilderPayment(msg.value, validator, block.coinbase);
}

Accounting and payment verification

The benefit of using a smart contract is that it allows for emitting an event, which is useful for off-chain parsing. With this solution it is possible to verify payments by inspecting the event log of a block’s transactions’ index and hash.

The only event considered valid is the payment proof event log specified for a given index and hash. As a result, it is simple to find, verify, and account for the payment made by the builder by looking for the log associated with the index and hash values provided in the payment proof.

Payment proofs in this format are more restrictive because require that payment for the full block must be done using a single call emitting a single log. Additionally, any other transfer to or from the validator fee recipient account, whether it is in the same transaction or another transaction, are ignored. These restrictions ensure validators are able to withdraw their rewards without impacting mev-boost profit switching calculations or the accounting systems used by node operators.

Further considerations

Withdrawals

Validators need to be able to withdraw from the account which receives payment. If payments are made directly to a validator’s recipient account, then the validator would be unable to withdraw anything from that account without adversely affecting the simulated value of that block. The reason is that if the profit of a block is measured by the difference in a validator’s recipient account before and after that block, then a withdraw makes the block look less profitable because it removes assets. To prevent this, validators would need to create a new recipient account for each block they produce.

Out of band bribes

Additional payment methods using transfers to the validator account, or transfers to the validator on L2s, for example, would allow for validator bribery. Further research and input is needed to address this externality.

Gas usage

Adding a transaction that calls a smart contract costs additional gas, and becomes an overhead expense on every block.

Restrictive design space

Requiring all payments to be done through a single smart contract transaction limits the design space available for builders to complete payments for blockspace. In turn this could limit the development of solutions like MEV-smoothing or blockspace futures.

kailinr avatar Aug 12 '22 17:08 kailinr

I'm not a coder so not qualified to comment on the code snippet. However, this does appear to address the core issue of deterministically identifying the transaction to the validator from the builder. Which is great. A couple of questions though:

  1. I don't understand the point being made in the "Withdrawals" section - could you explain / expand?
  2. How would we protect the process from abuse? Presumably, anyone could use the relay to send a txn?

beetrootkid avatar Aug 13 '22 10:08 beetrootkid

Requiring a smart contract appears to be overkill for this. Is there an issue with querying a node for the relevant address balance before and after the proposed block, and taking the delta as the reward? This is cheap (the movement of funds is a simple transfer, no withdrawal required), secure (no smart contract risk) and flexible (multiple payments can be made in a single block). The only downside of a direct method that I can see is that it doesn't emit an event, but any node operator of a decent size should be able to query the chain directly to obtain the data they need regarding rewards.

If there is concern of a malicious validator carrying out a withdrawal in their proposed block to falsely decrease the value paid to them then the individual transactions in the block could be examined to separate out balance increases from decreases, and only count the increases.

mcdee avatar Aug 26 '22 10:08 mcdee

Fyi, we (Flashbots) are currently proceeding with payment from builder to proposer in a direct transaction at the end of the block.

metachris avatar Aug 26 '22 12:08 metachris

thank you. @metachris, do you know when that functionality will be available?

ricardolyn avatar Aug 26 '22 14:08 ricardolyn

As @metachris pointed out, we recently reduced the specification to simple EOA payment transfer, still a work-in-progress. Would love input from @mcdee @kanewallmann et al. on using simple tx payments instead of smart-contract implementation short-term?

kailinr avatar Aug 29 '22 16:08 kailinr

Personally I have no requirement for proposer payments to be fed through a smart contract for accounting purposes. As mentioned in the specification, there is overhead and smart contract risk, and all of the accounting requirements can be met by querying the relevant transactions directly.

mcdee avatar Aug 29 '22 17:08 mcdee

@kailinr about this part of the process

The builder sets their own address as feeRecipient of the payload header they are constructing.

Are the builder fee recipients published somewhere? How many and how often would you expect them to change?

Reason for asking is, for reporting purposes and calculating the EL block reward, we'd like to identify if a block was built by a VC or using mev-boost using on-chain data only.

joaorodrigues-consensys avatar Sep 01 '22 11:09 joaorodrigues-consensys

The payment via transaction is live now.

Reason for asking is, for reporting purposes and calculating the EL block reward, we'd like to identify if a block was built by a VC or using mev-boost using on-chain data only.

There is no such list, and feeRecipient might change often / whenever. I don't see those being a good indicator 🤔

Our relay provides an API that shows the delivered payloads:

  • https://flashbots.notion.site/Relay-API-Spec-5fb0819366954962bc02e81cb33840f5#417abe417dde45caaff3dc15aaae65dd
  • https://builder-relay-ropsten.flashbots.net/relay/v1/data/bidtraces/proposer_payload_delivered?limit=10

But that API is voluntary too, although we do hope that all relays implement it.

The relay monitor could also be a useful data source here.

metachris avatar Sep 01 '22 13:09 metachris

I created a forum post to discuss defining a standard for block scoring: https://collective.flashbots.net/t/block-scoring-for-mev-boost-relays/202

thegostep avatar Sep 01 '22 22:09 thegostep

@metachris thanks for your reply. Is there any good on-chain indicator we could use to determine blocks built by mev-boost? Saw the Extra Data: Flashbots flashblock (Hex:0x466c617368626f747320666c617368626c6f636b) in this block and was wondering if we could use it to identify that a given block was built by you guys. Thanks

joaorodrigues-consensys avatar Sep 02 '22 15:09 joaorodrigues-consensys

You could, but that's likely to change at some point (soon).

metachris avatar Sep 03 '22 12:09 metachris

Continuation of the discussion: https://collective.flashbots.net/t/block-scoring-for-mev-boost-relays/202

metachris avatar Sep 06 '22 13:09 metachris