taiko-mono
taiko-mono copied to clipboard
feat: no ETH premint on L2
Describe the feature request
Ethereum Shanghai fork will allow staking withdrawal. ETH will be topped directly to user's Ether balance without going through transactions. We may want to use that same mechanism to replace the Bridge's current approach.
See https://eips.ethereum.org/EIPS/eip-4895
With EIP 4895, each Withdrawal is a tuple of 4 items:
- a monotonically increasing index, starting from 0, as a uint64 value that increments by 1 per withdrawal to uniquely identify each withdrawal
- the validator_index of the validator, as a uint64 value, on the consensus layer the withdrawal corresponds to
- a recipient for the withdrawn ether address as a 20-byte value
- a nonzero amount of ether given in Gwei (1e9 wei) as a uint64 value.
Our L1-to-L2 Ether transfer shall be saved in to a queue of Withdrawal object:
struct Withdrawal {
uint64 index;
uint64 validaorId; // maybe we can get rid of this and use a constant value 1
address receipient;
uint64 amount
}
Assuming each L2 block must process up to 10 withdrawals, then on L1, we may split the queue into:
uint64 nextWithdrawalToInclude;
Withdrawal[] withdrawals;
function proposeBlock(BlockMetadata memory meta) {
...
meta.withdrawalRoots = calculateWIthdrawalRoots(maxWithdrawals);
...
}
function calculateWIthdrawalRoots(uint256 maxWithdrawals) returns (bytes32) {
// we calculate the root, which may be pretty expensive. If it is indeed the case,
// we can calculate a kecack hash of the withdrawal slice and verify the hash
// matches the withdrawalsRoot using a ZKP
}
The python code for calculateWIthdrawalRoots
is like this;
def compute_trie_root_from_indexed_data(data):
trie = Trie.from([(i, obj) for i, obj in enumerate(data)])
return trie.root
Created this PR for discussion https://github.com/taikoxyz/taiko-mono/pull/13577. This is to me a very priority.
If we want to use the same mechanism as the beacon chain push withdrawals, we need:
- a Withdrawals queue / Bridging queue
- A cheap way to "push" the withdrawals to the L2 contract (JSONRPC methods?)
- A way to verify the withdrawals on L1 so that L2 doesn't need to prove anything and mint trustlessly (WithdrawalsRoot)
- A vault to hold the bridged ETH on L1 (?) (EtherVault)
The mechanism is automatic for EIP4895, the protocol sweeps the set of all validators and checks that they have a flag enabled and builds the queue accordingly. They can't set custom amounts, so we'll have to do it a bit differently.
It is also completely trustless, the verification is handled on beacon chain before the Withdrawal is pushed to the execution layer, probably verify the WithdrawalsRoot.
Lets use the following terms for clarity:
- Ether Deposit (Eth deposit for short): this is the ETH2 withdrawal-style deposit of Ether from L1 to L2 (note that this is no counterparty ETH Withdrawal from L2 to L1 using the same mechinism)
- L1-to-L2 Bridge Ether Transfer (L1toL2 transfer in short)
- L2-to-L1 Bridge Ether Transfer (L2toL1 transfer in short)
Without Ether premint on L2, our bridge and third party bridges will all work since Ether Deposit will mint new Ether on L2. Once accounts on L2 has Ether, they can be withdrawn by using L2toL1 transfers. Once these L2toL1 transfers are popular, L1toL2 transfer will be enabled. But there is a restriction:
$$\sum(L1toL2Transfer) \le \sum(L2toL1Transfer)$$
If users really want to deploy large amount of Ether using the bridge, we need premint Ether as a liquidity buffer on L2. Unless we don't do it at all, otherwise, preminting what we preminted on alpha-2 testnet seems to be fine.