On-chain contract
We create a deposit contract on the blockchain, with roughly the following code:
HashChainValue: event({prev_tip: bytes32, data: bytes[2048], value: wei_value, total_deposit_count: int128})
ChainStart: event({hash_chain_tip: bytes32, time: timestamp})
hash_chain_tip: public(bytes32)
total_deposit_count: int128
@payable
@public
def deposit(data: bytes[2048]):
log.HashChainValue(self.hash_chain_tip, data, msg.value, self.total_deposit_count)
self.total_deposit_count += 1
self.hash_chain_tip = sha3(concat(self.hash_chain_tip, data, as_bytes32(msg.value), as_bytes32(self.total_deposit_count)))
if self.total_deposit_count == 16384:
log.ChainStart(self.hash_chain_tip, block.timestamp)
When a user wishes to move their ETH from the 1.0 chain to the 2.0 chain, they should call the deposit
function, sending along 32 ETH and providing as data
a SimpleSerialize'd object with the following arguments (in order):
pubkey
: int256
proof_of_possession
: [int256]
withdrawal_shard
: int64
withdrawal_address
: bytes20
randao_commitment
: hash32
If they wish to deposit more than 32 ETH, they would need to make multiple calls.
[Governance note: when publishing the final version of this contract, it may be desirable to issue some kind of formal EIP that "enshrines" its privileged status, ąnd encourage CarbonVotes and other polls to vote on it, which if successful wouuld make clear to the community that this contract is "part of the protocol" and so the community is responsible for providing ongoing protocol improvements that eventually unlock any ETH sent into this contract]
Chain initialization
When a ChainStart
log is published, this initializes the chain, setting the following parameters:
POW_CHAIN_HASH_ROOT
(new parameter) = hash_chain_tip
GENESIS_TIME
= time
PROCESSED_HASH_ROOT
(new parameter) = hash_chain_tip
It runs on_startup
with initial_validator_entries
equal to the list of data records published as HashChainValue logs so far, in the order in which they were published (oldest to newest).
Chain updating
Define a validator's "view" as being the value obtained by calling DEPOSIT_CONTRACT_ADDRESS.get_hash_chain_tip()
from the post-state of the block 512 blocks behind the current head of the PoW chain. Define a "valid view" (defined subjectively from the PoW of a validator) as a value which is a descendant of POW_CHAIN_HASH_ROOT
and cannot be obtained by calling DEPOSIT_CONTRACT_ADDRESS.get_hash_chain_tip()
from the post-state of a block that is part of the canonical PoW chain at least 512 blocks behind the head. Note that any valid view should be either equal to or an ancestor of the validator's view.
Blocks will have a new data field, hash_chain_tip_vote: hash32
, which proposers are expected to fill with the following algorithm:
- Let slot B be the last slot during which the
POW_CHAIN_HASH_ROOT
changed.
- If all blocks since slot B contained a
hash_chain_tip_vote
that was either equal to the POW_CHAIN_HASH_ROOT
or was an invalid view, vote the validator's view.
- If there was at least one valid view published as a
hash_chain_tip_vote
since slot B, copy the first valid view.
Note that assuming >= 50% honest, this algorithm will converge to all honest proposers voting the same value, which is a descendant of the POW_CHAIN_HASH_ROOT
. If the same value is voted for in >= 683 of the last 1024 blocks, set the POW_CHAIN_HASH_ROOT
to this value.
Note that this is a vote, not a consensus rule; blocks with incorrect votes should not be rejected.
Deposit processing
Add a new type of SpecialObject
, which consists of the entire hash-linked-list of HashChainValue logs since the previous PROCESSED_HASH_ROOT
up to the POW_CHAIN_HASH_ROOT
. If a valid such hash-linked-list is submitted, then we run add_validator
with the given values for each record, and set PROCESSED_HASH_ROOT = POW_CHAIN_HASH_ROOT
.
This does mean that deposit processing is not "automatic", in that deposits are not automatically read from the PoW chain; only a hash is automatically read, and the rest of the data must be manually imported by some block proposer. This is by design, to limit the amount of in-consensus communication between the PoW chain and the beacon chain required to a single hash value.