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

0x02 Credential Prefix #2454

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open

0x02 Credential Prefix #2454

wants to merge 3 commits into from

Conversation

jclapis
Copy link

@jclapis jclapis commented May 28, 2021

Summary

This PR adds a new withdrawal credential prefix, 0x02, to the eth2 specification.
It was decided upon after a lengthy brainstorming session in the ETH R&D Discord.

The new prefix is an extension of 0x01 (PR here) and follows the same formatting and behavior rules:

  • Its contents will be a 20-byte eth1 address
  • Withdrawals must be sent to that address

In addition, 0x02 includes a new constraint related to the current merge design that will be unique to validators with this prefix:

  • Blocks proposed by validators using this prefix will be invalid if the coinbase for those blocks is not equal to eth1_withdrawal_address.

In other words, 0x02 comes with the constraint that when these validators propose a block, the priority fees must be sent to the withdrawal address for the blocks to be considered valid. As the withdrawal address is an eth1 address, the new prefix should not introduce any fundamental synchronization issues between the execution and consensus layers.

Rationale

The current merge design assumes that a validator owns all of the ETH deposited to the Beacon Chain, and is thus entitled to all of the rewards that come from attestations, block proposals, and priority fees. These rewards are distributed by sending attestation and block proposal rewards to the validator address on the Beacon Chain, and priority fees to the coinbase address specified in an argument provided to the execution client on startup (which is an eth1 address).

This assumption is not valid in certain contexts.

In the case of decentralized staking pools, where a node operator only owns some of the 32 ETH deposit that created the validator, it is desirable for the node operator to only be entitled to a proportion of the total rewards from each block, including the priority fees; however, in the current design, the node operator will be able to pocket all of the priority fees by setting coinbase to an address that they personally control.

Since the coinbase is just an argument in the execution client, it is trivial to change it and exploit this behavior. Doing so decouples those fees from the staking pool entirely, thus robbing the pool from some of the reward that it should be entitled to.

This problem encourages node operators to act selfishly, thereby reducing the rewards accumulated by decentralized staking pools and ultimately encouraging centralized staking pools.

This is not a desirable outcome.

One mitigation strategy would be to employ a watching system that detects when the node "cheats" in this way, and punish them by removing their ability to withdraw their initial deposit. Unfortunately, this strategy does not scale - the maximum punishment would equal their portion of the initial 32 ETH deposit (say, for example, 16 of their own ETH). It is entirely possible for the node operator to accumulate more than 16 ETH from priority fees by cheating, and thus defeat the punishment. Furthermore, this encourages a centralized "manager" aspect that decentralized staking platforms may have fundamental conflicts with.

Ultimately, solving this problem requires a solution that:

  • Allows for the fair distribution of priority fees
  • Can scale to arbitrary balances
  • Does not interfere with other validators that use the existing 0x00 or 0x01 prefixes
  • Does not add extra burden on (and thus, delay) the merge

0x02 is our proposal to satisfy these criteria.

Possible Implementation

One reasonable implementation would be for the validator client to send the withdrawal address to the execution client upon block proposal, and for the execution client to then check for the 0x02 prefix.

If the prefix is absent, it would send priority fees to the address provided in the coinbase argument as it does today.

If the 0x02 prefix is present, it would then send them to the withdrawal_address instead.

The address presented by the validator client would take precedence over the coinbase argument for 0x02-based validators.

Timing

One drawback to this proposal is added complexity, the implementation of which may delay the merge. If this ends up being true upon further investigation, there is a possible workaround in some circumstances:

  • Accept the proposal now, so that services can create validators with 0x02 credentials today
  • Proceed with the merge as planned, deferring 0x02's implementation to a later date (call it X)
  • Validators can "cheat" between now and X
  • After X, they are forced to use the withdrawal_address

In this situation, the watcher-based tracking strategy above could be employed to observe how much ETH was "stolen" by the malicious node operator.
As long as X occurs before they've had time to accumulate a "stolen" balance greater than their initial proportion of the deposit, they could be penalized accordingly without any damage to the staking pool.

Even if X is long enough that they do manage to steal some ETH overall, we submit that this solution is better than doing nothing and letting them steal the priority fees in perpetuity.

A Note on MEV

0x02 is targeted as a solution to coinbase-based balance problems. It is not intended to solve MEV-based problems; however, it could do so in certain situations. For example, the current Flashbot system generally distributes rewards to the coinbase address of the execution client. In this case, 0x02 would enforce the fair distribution of these MEV rewards.

In situations where node operators set up their own MEV system locally or create an agreement where they are paid for their work via side channels, this proposal will not solve the "stolen balance" problem, nor is it an attempt to do so. That is a larger problem that will need to be solved via some other means, and staking pool providers will need to alert users to this issue accordingly.

That being said, the priority fee distribution problem lives in parallel with side-channel problems; it does not go away with the existence of MEV. One could argue that unfair distribution of priority fee rewards will be more common than MEV-based issues in the context of decentralized pools: a typical at-home node operator may not be familiar with MEV, know how to run a compatible client, or know how to subscribe to such a service. However, switching the coinbase argument out for an address that they control is well within the grasp of any node operator.

Because of this, we submit that 0x02 provides an important improvement to the eth2 spec, even in a world where MEV-based side-channel payment problems exist.

Proponents

  • Rocket Pool is a major proponent of this proposal. The problem it solves is one of the protocol's largest challenges to competition with centralized staking pools.
  • Blox Staking would be able to leverage this proposal. They have expressed a desire to have priority fees paid to custom addresses that their customers provide, rather than having to manage and map them internally. This proposal would provide a path towards that goal.

Open Questions

How are withdrawal address changes handled? If someone creates a 0x02 address, are they bound to it permanently?

Rocket Pool actually considers this "address locking" to be a requirement for 0x02 to work as intended. It guarantees that the priority fee distribution is fair (because the withdrawal address is a smart contract that handles rewards delegation). Furthermore, it improves the security of legitimate node operators. This would stop an attacker from gaining access to a legitimate node and changing the coinbase to their own address. For Rocket Pool's use case, the ability for the user to modify the withdrawal address after validator creation would invalidate this proposal entirely.

That being said, for Blox's use case, it may be desirable for users to be able to modify their addresses. One possible middle-ground is to allow the withdrawal address to be changed by a signed transaction from the withdrawal address itself. This would support the use cases of both parties.

Can 0x00 validators "upgrade" to 0x02, in the same way that they can upgrade to 0x01 currently?

We don't see why not, though the conditions associated with 0x02 will need to be made very clear to a user who does this.

@alonmuroch
Copy link
Contributor

I'm a proponent of continuing support for non operator controlled withdrawal credentials, that is, whoever controls the 32 ETH should be able to control all types of rewards (might it be a DIY, custodial staking or a protocol).

Having said that, I think creating a 0x02 credentials exacerbates the issue of not being able to upgrade from 0x00 to 0x01/0x02 as some services (Lido from the top of my head) could face an issue of having some validators with 0x00 and some with 0x02 (and maybe some with 0x01?).
Is upgrading 0x00 to 0x01/0x02 will be supported from day one or at a later stage?

@yorickdowne
Copy link

yorickdowne commented May 30, 2021

“Day one” in this case would be just before merge, so users can upgrade credentials. Edit: Reading Mikhail’s comments in R&D, a one-time change from 0x0 to 0x1 is required for withdrawal, and could just as easily be “to 0x1 or 0x2”.

Changing from 0x1 to 0x2 is not currently in the cards, there are DoS concerns. Ditto for changing the address, in a case where 0x1/2 do not reference a contract.

If this PR is accepted and makes it into the specs, pools like RocketPool can use it for all mainnet deposits. Providers like Blox could use it going forward, and special-case 0x0 and 0x1 credentials: Blox controls the execution client, which means those rewards could go to a Blox-controlled contract for disbursement, or the execution client Blox uses gets changed to deal with those in a particular way.

@vshvsh
Copy link

vshvsh commented May 31, 2021

That looks like a building delegation mechanism in ETH2 step by step. I think it's a good direction but there might be a better approach in sketching out the whole end result instead of doing it in small ad-hoc steps: that would result in a more clear overall design.

@hwwhww hwwhww added general:enhancement New feature or request general:RFC Request for Comments labels Jun 1, 2021
specs/phase0/validator.md Outdated Show resolved Hide resolved
@mkalinin
Copy link
Collaborator

mkalinin commented Jun 2, 2021

If this change is accepted then it implies the following subsequent changes made after the Merge:

  • a version of assembleBlock with extra coinbase parameter -- implementation only
  • following spec update and its further implementation:
    •   def is_valid_coinbase(state: BeaconState, execution_payload: ExecutionPayload) -> bool:
            proposer_index = get_beacon_proposer_index(state)
            withdrawal_credentials_prefix = state.validators[proposer_index].withdrawal_credentials[:1]
            if (withdrawal_credentials_prefix == ETH1_ADDRESS_WITHDRAWAL_AND_COINBASE_PREFIX):
          	  withdrawal_and_coinbase_address = state.validators[proposer_index].withdrawal_credentials[12:]
                return execution_payload.coinbase == withdrawal_and_coinbase_address
      
            return True
    • assert is_valid_coinbase(state, execution_payload) in process_execution_payload

UPD
The following modification of validator's logic is required:

execution_parent_hash = state.latest_execution_payload_header.block_hash
timestamp = compute_time_at_slot(state, state.slot)

proposer_index = get_beacon_proposer_index(state)
withdrawal_credentials_prefix = state.validators[proposer_index].withdrawal_credentials[:1]
if (withdrawal_credentials_prefix == ETH1_ADDRESS_WITHDRAWAL_AND_COINBASE_PREFIX):
    withdrawal_and_coinbase_address = state.validators[proposer_index].withdrawal_credentials[12:]
    return execution_engine.assemble_block(execution_parent_hash, timestamp, withdrawal_and_coinbase_address)
else:
    return execution_engine.assemble_block(execution_parent_hash, timestamp)

Co-authored-by: Mikhail Kalinin <noblesse.knight@gmail.com>
@jclapis
Copy link
Author

jclapis commented Jun 2, 2021

If this change is accepted then it implies the following subsequent changes made after the Merge:

* a version of `assembleBlock` with extra `coinbase` parameter -- implementation only

* following spec update and its further implementation:
  
  * ```python
      def is_valid_coinbase(state: BeaconState, execution_payload: ExecutionPayload) -> bool:
        proposer_index = get_beacon_proposer_index(state)
        withdrawal_credentials_prefix = state.validators[proposer_index].withdrawal_credentials[:1]
        if (withdrawal_credentials_prefix == ETH1_ADDRESS_WITHDRAWAL_AND_COINBASE_PREFIX):
        	withdrawal_and_coinbase_address = state.validators[proposer_index].withdrawal_credentials[12:]
          return execution_payload.coinbase == withdrawal_and_coinbase_address
    
        return True
    ```
  * `assert is_valid_coinbase(state, execution_payload)` in `process_execution_payload`

Thank you for this @mkalinin. After looking at this, I realize that the spec design as stated may not support all of the ways in which projects want to use it. For example, this won't fix the situation where you have many validators with many different withdrawal addresses all tied to the same execution client. To support that use case, you'd actually need the execution client to set the coinbase to whatever the withdrawal address is, not simply check for equality.

Could we even mandate that in the spec? How much more complexity would that modification add over what is currently proposed?

@mkalinin
Copy link
Collaborator

mkalinin commented Jun 3, 2021

Could we even mandate that in the spec? How much more complexity would that modification add over what is currently proposed?

I've forgot to add corresponding change in the validator's logic. Updated my original comment with it.

@MicahZoltu
Copy link
Contributor

I'm concerned that this change will incentivize making side-channel block producer payments.

Imagine the following:

A user gossips a transaction with nonce 1 @ 2 nanoeth/gas premium.
The user also submits the same transaction via side channel to miners with nonce 1 @ 1 nanoeth/gas premium.

If you are an honest block producer or solo block producer, you will pick the transaction with 2 nanoeth/gas premium.

If you are a pool block producer, you will pick the transaction with 1 nanoeth/gas premium because you get 100% of that, while you would only get a small percentage (your share of stake) for the 2 nanoeth/gas transaction.

The problem here is that we are basically incentivizing creating a side-channel payment system because people who use it get their transactions included for less than people who don't. You can still submit your transactions over gossip as well to make sure you get the widest reach, which allows this side-channel payment system to grow organically without needing to get buy-in from a large portion of validators or users up-front.

@isidorosp
Copy link

I'm concerned that this change will incentivize making side-channel block producer payments.
...
The problem here is that we are basically incentivizing creating a side-channel payment system because people who use it get their transactions included for less than people who don't. You can still submit your transactions over gossip as well to make sure you get the widest reach, which allows this side-channel payment system to grow organically without needing to get buy-in from a large portion of validators or users up-front.

Does this PR actually create (or increase) this incentive, though? I think the incentive can exist without it. Basically, it will occur if there is any attempt by a staking pool to "socialize" rewards across the pool but going rogue via side-payment is an option. The only thing this PR does is make it (markedly) easier for decentralized staking protocols to set up an "enforceable" reward splitting structure, and a bit more difficult for a node operator to circumvent such a mechanism. The problem of side-payments is kinda insurmountable and the only real way to address it is via some sort of permissioned entry and forced exit mechanism over the node operators, but I don't think that's something we really want in a truly decentralized staking protocol.

@MicahZoltu
Copy link
Contributor

Does this PR actually create (or increase) this incentive, though? I think the incentive can exist without it. Basically, it will occur if there is any attempt by a staking pool to "socialize" rewards across the pool but going rogue via side-payment is an option.

I think this cuts to the heart of the issue. Any attempt to socialize block production rewards by anyone other than the block producer can be pretty easily worked around. If we cannot solve the problem completely, we should instead embrace that reality and strongly discourage people from even trying to socialize block rewards. This is because any attempts at socialization of the block reward by anyone other than the block producer will result in side channel payment systems springing up, which is bad for the whole ecosystem.

@poupas
Copy link

poupas commented Jun 3, 2021

@MicahZoltu I would argue, as others have, that forcing malicious actors to use a complex MEV-extraction mechanism (requiring coordination/agreements/contracts) is a good thing. This is a considerable increase in complexity, and is far from the trivial change that is setting the coinbase address, which requires very low effort, for a potential very large gain. We could also argue that, eventually, the MEV-issue will be mitigated by L2 networks. This would leave the coinbase issue left to fix, which could be done right now.

But let's set that argument aside for a moment. There's an additional issue, orthogonal to the one discussed: not locking the coinbase address can be seen as a security design weakness in the current PoS protocol.

It's apparent that the PoS protocol designers have taken the care to create a robust protocol. E.g. creating split keys, in order to have separation of duties and increase security (validation and withdrawal). This is a good decision, as it removes economic incentives from bad actors to compromise validators: if a validator, or its validator key, is compromised, there is no actual financial gain to the attacker (apart from maybe ransom - but in that case the validator could just exit its validation duties).

This security model fails in one aspect: the coinbase address can be changed arbitrarily.

If the coinbase address is not tied a specific validator, an attacker could compromise a validator host, change the coinbase to one it controls, and receive priority fees. If the priority fees are as valuable as some have discussed, compromising a validator, or many, could be much more profitable than compromising a withdrawal key (due to accruing fees exceeding the validator balance). Basically, we have now made validators a valuable target, which I think goes against the goals of the original PoS protocol.

In conclusion, the 0x02 proposal may have two distinct benefits:

  1. Increase the cost for selfish node operators, and incentivise good behavior
  2. Harden the security of validators by closing a potential attack vector (manipulation of coinbase address)

@isidorosp
Copy link

I think this cuts to the heart of the issue. Any attempt to socialize block production rewards by anyone other than the block producer can be pretty easily worked around. If we cannot solve the problem completely, we should instead embrace that reality and strongly discourage people from even trying to socialize block rewards. This is because any attempts at socialization of the block reward by anyone other than the block producer will result in side channel payment systems springing up, which is bad for the whole ecosystem.

I don't think it's true that it can be "pretty easily worked around" (I think @poupas did a good job I think of examining the difference in complexity in changing a coinbase address vs DIY MEV vs arranging your own side-channels). Please also consider that there are a lot of node operators out there (especially solo operators and those interested in things like rocketpool) who are able to follow directions in a well-written guide, but that's about it.

I understand that coming from a dev perspective it's desirable that solutions should be complete where possible, but I don't agree that (paraphrased) "if we can't solve it completely then we shouldn't solve any facet of it at all". How are you going to discourage centralized & quasi-decentralized staking services from socializing rewards when it's economically beneficial for them to do so (given that they can control the pool of operators for their protocol)? The reality is that rewards are going to be socialized because that's what makes economic sense in the long run; miners did it, and validators will do it as well. The question is whether the protocol is willing to make changes to help level the playing field for services who want to offer competitive rewards by staking in a decentralized + trustless + permissionless manner.

In my opinion, the concern around network/ecosystem health from any possible increased incentives for side-channeling payments for block production should be weighed against the concern of a non-decentralized staking landscape, and the latter is more important, especially when considering the ethos of Ethereum in general.

@alonmuroch
Copy link
Contributor

“Day one” in this case would be just before merge, so users can upgrade credentials. Edit: Reading Mikhail’s comments in R&D, a one-time change from 0x0 to 0x1 is required for withdrawal, and could just as easily be “to 0x1 or 0x2”.

Changing from 0x1 to 0x2 is not currently in the cards, there are DoS concerns. Ditto for changing the address, in a case where 0x1/2 do not reference a contract.

If this PR is accepted and makes it into the specs, pools like RocketPool can use it for all mainnet deposits. Providers like Blox could use it going forward, and special-case 0x0 and 0x1 credentials: Blox controls the execution client, which means those rewards could go to a Blox-controlled contract for disbursement, or the execution client Blox uses gets changed to deal with those in a particular way.

Not being able to upgrade 0x0 to 0x2 will prevent existing validators from adopting SSV IMO

@yorickdowne
Copy link

yorickdowne commented Jun 15, 2021

This kind of mechanism would also be useful for "Ethereum client as a Service" providers such as Infura, Alchemy, Fiews. Renting an execution client from these services is common now, for many projects, and aaS for the consensus client is available now in an early implementation. Where do priority fee rewards go? One execution client, many different validators, with different owners. If the validator client can signal the address to be used, and that gets enforced by the chain, that would solve some issues there.

@darrenlangley
Copy link

We have spent a considerable amount of time analysing the options and we concur with the raised concerns.

We still feel that the 0x02 credential would be beneficial and we would use it, if implemented, but we understand that it would add complexity to consensus without fully solving the problem.

We have explored our options and we believe we have a crypto-economic mechanism that gives us the same protection as 0x02 and we are in collaboration with Flashbots to be part of the MEV solution.

We welcome anyone who is interested to take a look at our research repo:
https://github.com/rocket-pool/rocketpool-research

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
general:enhancement New feature or request general:RFC Request for Comments
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants