Simple eth1 withdrawals (beacon-chain centric)

Great proposal!
@djrtwo how is the withdrawal initiated on the beacon chain side? with what key/ tx?

process_bls_set_withdrawal_address seems somewhat specific compared to a more general-purpose version that accepted an arbitrary 32-byte value rather than an Ethereum 1 address. The latter would result in simpler code as well as allowing for further withdrawal methods in future. Is this specificity intentional, to lock down the functionality to ETH1 addresses only?

^ automatically initiated within process_epoch

My interpretation was that this method was written before the suggestion to update withdrawal credentials in place - agree that a general-purpose process_bls_set_withdrawal_credentials method would work better in that case.

I am a bit confused how the Merkle root of the ETH2 chain is going to be available on ETH1?

This assumes that ETH1 is merged into beacon chain, correct ?:))

Okay, first reaction to this proposal: it feels like we’re not taking advantage of the fact that withdrawal credentials are BLS signatures. There’s a chance to enormously compress withdrawal data here, which could be super useful depending on how the eth1-beaconchain docking ends up working. Wouldn’t it be nice to be able to aggregate the signatures for withdrawals? Maybe it doesn’t matter, but that’s just where my mind goes. I want to try and put more thought into that, maybe others can too. That’s more of a sidenote though.

2nd reaction: how can trustless pools use this method to make a safe deposit? You have to be able to commit to the withdrawal contract before depositing. But if you’re not even on the beacon chain, you can’t use this method afaict. Or did I miss something?

3rd reaction, @djrtwo let me explain why I had room for updating the withdrawal address in DSWC, and the large number of use cases which depend on this capability.

There are several different aspects to this, so I’ll go through them one by one.

Partial withdrawals
As @jgm has already pointed out in his other thread, there are strong reasons why validators will want to withdraw amounts above MAX_EFFECTIVE_BALANCE without withdrawing the rest of the validator balance, and why we should let them do this without having to make a full exit (churn, etc.). This means that (especially if we were to go with my suggested “single bit” modification to his proposal) withdrawal addresses are not going to be single use, and they should have the option of being updateable.

Split keys

One BLS key does not imply one entity. There are a myriad of situations where split keys might specifically not want to leave the withdrawal address un-set, but might also wish to change it in the future. If one of a set of keyholders wants to quit, for example, they can just give/sell their keys to the other keyholders, but the withdrawal contract which would have presumably divided the proceeds will then need to be updated. As new, more advanced contracts get developed specifically to handle as-yet-unprocessed withdrawals, keyholders might want to upgrade provided that they can all agree. If there is a staking service which does the validating they might want to share ownership of the withdrawal key with the client so that they can update the policy under which withdrawals are governed; if there is a DAO the keyholders of the DAO might want the ability to change their minds; etc. It’s also important to note that until the EVM supports BLS operations this is the only way to take advantage of BLS functionality, since you can’t just write BLS support into an EVM contract.

Loss/theft of credentials

Right now we have the lovely property that for most participants in the beacon chain a single mnemonic backup protects both their validator and withdrawal credentials. Given the length of time it might be before withdrawals become possible, someone who owns their own validator outright and is not trying to make a commitment to any other party might want to set their withdrawal address to a cold wallet or something to provide a nice level of “safe” redundancy. Then if they lose all their validator/withdrawal credentials they will be eventually exited for inactivity and recover at least some of their funds. However if they retain control over their mnemonic and lose access instead to the withdrawal address over that time, they can simply restore their withdrawal credentials from their mnemonic and set a new one.

Obviously, even taking the three above considerations into account we still absolutely need the ability to set a non-revocable address for trustless pooling and other use cases which do not have a specific set of keyholders who can be trusted to control withdrawals.

We don’t need to know the deployed address of the system withdrawal contract ahead of time, just its ABI. We can write and deploy a “receiver” contract for a validator with a method which accepts the system withdrawal contract address, then calls the defined method on it using address.call.

That’s not the problem I’m describing. The problem I’m describing is that users of trustless pools want to ensure the eth1 address which their funds will be withdrawn to is fixed and unchangeable at the time they deposit. Or perhaps it is implicit in this specification that the deposit contract would accept the new “0x01” Eth1Address withdrawal credential? If so then this could be specified there during the deposit.

Yes, the deposit contract will accept any 32-byte array for withdrawal credentials. We could deposit using a 0x01 prefix today if we wanted; it’s just not particularly useful until there is a commitment to supporting that withdrawal type.

2 Likes

Gotcha. Might be good for @djrtwo to explicitly mention that in the proposal, just to remove ambiguity.

1 Like

That’s what I mean about the following:

Specify Eth1AddressPrefix0x01 – withdrawal prefix that allows to specifies the last 20 bytes of the credentials to be a 20-byte eth1-addr

There is no verification in the deposit contract or on eth2 beacon chain side so this can be specified in a minor version spec release and immediately built upon. That’s the primary purpose of the above proposal. To essentially show a viable end-to-end process that would enable 0x01 withdrawals today. In fact, most of the beacon-chain mechanics in this post aren’t even necessary. The most important component is simply how the eth1 chain would see such a withdrawal TX in the future.

You could do aggregation in, e.g. BLSSetWithdrawalAddress, but much of the efficiency is lost because (1) there is no apriori set know and (2) the message signed is not shared

Because of (1), you couldn’t just use a bitfield in the aggregated message and would instead have to explicitly specify validator indices (similar to attester slashings).

And because of (2), you would need to include the actual message signed for each.

You’d end up in a marginal block data savings and a marginal verification cost saving, but at a higher complexity and an obfuscation as to which message(s) failed to verify in the event of a bad block being created.

Similar to how deposits are not currently aggregated on chain, I don’t expect the savings vs complexity to make sense here.

1 Like

[I think this question might have been asked before you realized I was suggesting that 0x01 deposits should be able to be sent immediately. The logic to resolve 0x00 deposits to 0x01 deposits is just to show that alternate withdrawal workflow]

Trustless in which sense? The selection of the withdrawal addr?

There are many iterations of design here. One example:

The withdrawal contract is created first. Then funds are pooled that can only be attached to a full 32-ETH deposit including the withdrawal contract as the withdrawal_creds. Then a 32-ETH deposit is made through this proxy contract, verifications are done against the deposit-data, and the 32ETH goes to the deposit contract.

The major downside to this design is that the BLS signature cannot be verified on chain. That said, there are ways around this. For example, a proposed deposit-data could go into this contract and only when N-of-M participants signal that it is valid, does the TX get sent. (with some expiration on the whole thing so funds aren’t in limbo forever).

1 Like

As for these alternative usecases for continually updating credentials, many can be handled via an eth1 contract. Once you update or initially set your credentials to a contract, logic in that contract can handle any further updates or theft/loss fallbacks.

The partial withdrawals usecase is certainly worth considering more deeply.
First and foremost, we need to identify whether having an independent transfer functionality is actually worth the complexity/load beyond this happening through validator churn. I see the value, but want to think about the options here a bit more.

2 Likes

If a trustless pool specifies a child contract as the target withdraw address for validator X, how can it differentiate between the Beacon Chain’s transfer of validator funds vs me just sending some ETH to it?

In the design here, before there are beacon chain reads on eth1, it could not tell the difference.

I ask you though, if it could tell the difference, would that make a meaningful difference in 80%+ of use cases being designed for?

It’s useful for staking pools to know that a validator lifecycle is completed to distribute the funds in a non-linear way. For example, if the final balance of the validator is lower than the starting balance you may want to make a party absorb the loss while distributing the rest to another party.

For that to happen you must know that the entire balance of the validator has already been transferred to the withdrawal account to trigger the above logic. Otherwise, someone could send 1ETH to the withdrawal address and claim the validator got slashed. How could the decentralized staking pool contracts tell the difference?


A possible way to achieve that would be for the validator balance transfer to happen when the withdrawal address itself calls the system contract. Then the contract could in one transaction

  • Call system contract
  • Compute its own balance diff
  • Call the staking pool manager contract to mark its status and “done” and trigger accounting logic according to the above diff

A plus would be for the call to the system contract to revert if the withdrawal receipt has already been consumed.

1 Like

Just wanted to say thanks for putting some attention to the theft of credentials, sadly I fall into that category, I had a withdrawal key and wallet key compromised/stolen :frowning:

Could there be a way to enable an option where withdrawals can only take place from the validator, for example, only the host could initiate the withdrawal from the node, but external withdrawals could not gain access to the validator and funds with the withdrawal key. if my validator could have this option enabled then my host could withdraw the funds for me and I could create a new cold wallet and ask them to send the funds there so I could move to a new node, I am in this for the long haul and intend on keeping the node, sadly now suffering from anxiety and regret.

I would be eternally grateful if ETH devs can keep working on a way to stop stolen keys from being used, I am sure there is a way, I am just worried that some Devs dont see it as an issue, please dont forget people like me, I invested a lot and would be devastated to lose my funds when we get to that point and am 110% behind the project.

Thanks for listening

Somewhat basic question but didn’t find an explicit mention so thought to bring it up. If our ETH1 withdrawal key is actually a smart contract address, will this work? This is an example address that we’re thinking to use:

https://etherscan.io/address/0x09035d939859d2b0473a94fb4f164a8ef09d18e1

It looks like a forwarder smart contract, right?

Based on my knowledge, as long as the ETH1 contract can receive ETH it should work. Mainly make sure no specific function call is needed and if you can withdraw from your smart contract if needed.