Remove eth2 genesis in favour of genesis trigger
This commit is contained in:
parent
6f82480df2
commit
d1e589f11f
File diff suppressed because one or more lines are too long
|
@ -1,8 +1,5 @@
|
||||||
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
|
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
|
||||||
FULL_DEPOSIT_AMOUNT: constant(uint256) = 32000000000 # Gwei
|
|
||||||
CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant(uint256) = 65536 # 2**16
|
|
||||||
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
|
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
|
||||||
SECONDS_PER_DAY: constant(uint256) = 86400
|
|
||||||
MAX_64_BIT_VALUE: constant(uint256) = 18446744073709551615 # 2**64 - 1
|
MAX_64_BIT_VALUE: constant(uint256) = 18446744073709551615 # 2**64 - 1
|
||||||
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
||||||
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
||||||
|
@ -16,13 +13,10 @@ Deposit: event({
|
||||||
signature: bytes[96],
|
signature: bytes[96],
|
||||||
merkle_tree_index: bytes[8],
|
merkle_tree_index: bytes[8],
|
||||||
})
|
})
|
||||||
Eth2Genesis: event({deposit_root: bytes32, deposit_count: bytes[8], time: bytes[8]})
|
|
||||||
|
|
||||||
zerohashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
zerohashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||||
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||||
deposit_count: uint256
|
deposit_count: uint256
|
||||||
full_deposit_count: uint256
|
|
||||||
chainStarted: public(bool)
|
|
||||||
|
|
||||||
|
|
||||||
@public
|
@public
|
||||||
|
@ -30,7 +24,6 @@ def __init__():
|
||||||
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
||||||
self.zerohashes[i+1] = sha256(concat(self.zerohashes[i], self.zerohashes[i]))
|
self.zerohashes[i+1] = sha256(concat(self.zerohashes[i], self.zerohashes[i]))
|
||||||
|
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@constant
|
@constant
|
||||||
def to_little_endian_64(value: uint256) -> bytes[8]:
|
def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||||
|
@ -47,7 +40,6 @@ def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||||
|
|
||||||
return slice(convert(y, bytes32), start=24, len=8)
|
return slice(convert(y, bytes32), start=24, len=8)
|
||||||
|
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@constant
|
@constant
|
||||||
def get_deposit_root() -> bytes32:
|
def get_deposit_root() -> bytes32:
|
||||||
|
@ -124,17 +116,3 @@ def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||||
signature,
|
signature,
|
||||||
self.to_little_endian_64(index),
|
self.to_little_endian_64(index),
|
||||||
)
|
)
|
||||||
|
|
||||||
if deposit_amount >= FULL_DEPOSIT_AMOUNT:
|
|
||||||
self.full_deposit_count += 1
|
|
||||||
if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD:
|
|
||||||
timestamp_day_boundary: uint256 = (
|
|
||||||
as_unitless_number(block.timestamp) -
|
|
||||||
as_unitless_number(block.timestamp) % SECONDS_PER_DAY +
|
|
||||||
2 * SECONDS_PER_DAY
|
|
||||||
)
|
|
||||||
new_deposit_root: bytes32 = self.get_deposit_root()
|
|
||||||
log.Eth2Genesis(new_deposit_root,
|
|
||||||
self.to_little_endian_64(self.deposit_count),
|
|
||||||
self.to_little_endian_64(timestamp_day_boundary))
|
|
||||||
self.chainStarted = True
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ from .utils import (
|
||||||
# Constants
|
# Constants
|
||||||
MIN_DEPOSIT_AMOUNT = 1000000000 # Gwei
|
MIN_DEPOSIT_AMOUNT = 1000000000 # Gwei
|
||||||
FULL_DEPOSIT_AMOUNT = 32000000000 # Gwei
|
FULL_DEPOSIT_AMOUNT = 32000000000 # Gwei
|
||||||
CHAIN_START_FULL_DEPOSIT_THRESHOLD = 65536 # 2**16
|
|
||||||
DEPOSIT_CONTRACT_TREE_DEPTH = 32
|
DEPOSIT_CONTRACT_TREE_DEPTH = 32
|
||||||
TWO_TO_POWER_OF_TREE_DEPTH = 2**DEPOSIT_CONTRACT_TREE_DEPTH
|
TWO_TO_POWER_OF_TREE_DEPTH = 2**DEPOSIT_CONTRACT_TREE_DEPTH
|
||||||
|
|
||||||
|
@ -63,28 +62,14 @@ def registration_contract(w3, tester):
|
||||||
return registration_deployed
|
return registration_deployed
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def chain_start_full_deposit_thresholds():
|
|
||||||
return [randint(1, 5), randint(6, 10), randint(11, 15)]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[0, 1, 2])
|
@pytest.fixture(params=[0, 1, 2])
|
||||||
def modified_registration_contract(
|
def modified_registration_contract(
|
||||||
request,
|
request,
|
||||||
w3,
|
w3,
|
||||||
tester,
|
tester):
|
||||||
chain_start_full_deposit_thresholds):
|
|
||||||
# Set CHAIN_START_FULL_DEPOSIT_THRESHOLD to different threshold t
|
|
||||||
registration_code = get_deposit_contract_code()
|
registration_code = get_deposit_contract_code()
|
||||||
t = str(chain_start_full_deposit_thresholds[request.param])
|
contract_bytecode = compiler.compile_code(registration_code)['bytecode']
|
||||||
modified_registration_code = re.sub(
|
contract_abi = compiler.mk_full_signature(registration_code)
|
||||||
r'CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant\(uint256\) = [0-9]+',
|
|
||||||
'CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant(uint256) = ' + t,
|
|
||||||
registration_code,
|
|
||||||
)
|
|
||||||
assert modified_registration_code != registration_code
|
|
||||||
contract_bytecode = compiler.compile_code(modified_registration_code)['bytecode']
|
|
||||||
contract_abi = compiler.mk_full_signature(modified_registration_code)
|
|
||||||
registration = w3.eth.contract(
|
registration = w3.eth.contract(
|
||||||
abi=contract_abi,
|
abi=contract_abi,
|
||||||
bytecode=contract_bytecode)
|
bytecode=contract_bytecode)
|
||||||
|
@ -94,11 +79,6 @@ def modified_registration_contract(
|
||||||
address=tx_receipt.contractAddress,
|
address=tx_receipt.contractAddress,
|
||||||
abi=contract_abi
|
abi=contract_abi
|
||||||
)
|
)
|
||||||
setattr(
|
|
||||||
registration_deployed,
|
|
||||||
'chain_start_full_deposit_threshold',
|
|
||||||
chain_start_full_deposit_thresholds[request.param]
|
|
||||||
)
|
|
||||||
return registration_deployed
|
return registration_deployed
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -184,53 +184,3 @@ def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input
|
||||||
leaf_nodes.append(hash_tree_root_result)
|
leaf_nodes.append(hash_tree_root_result)
|
||||||
root = compute_merkle_root(leaf_nodes)
|
root = compute_merkle_root(leaf_nodes)
|
||||||
assert root == registration_contract.functions.get_deposit_root().call()
|
assert root == registration_contract.functions.get_deposit_root().call()
|
||||||
|
|
||||||
|
|
||||||
def test_chain_start(modified_registration_contract, w3, assert_tx_failed, deposit_input):
|
|
||||||
t = getattr(modified_registration_contract, 'chain_start_full_deposit_threshold')
|
|
||||||
# CHAIN_START_FULL_DEPOSIT_THRESHOLD is set to t
|
|
||||||
min_deposit_amount = MIN_DEPOSIT_AMOUNT * eth_utils.denoms.gwei # in wei
|
|
||||||
full_deposit_amount = FULL_DEPOSIT_AMOUNT * eth_utils.denoms.gwei
|
|
||||||
log_filter = modified_registration_contract.events.Eth2Genesis.createFilter(
|
|
||||||
fromBlock='latest',
|
|
||||||
)
|
|
||||||
|
|
||||||
index_not_full_deposit = randint(0, t - 1)
|
|
||||||
for i in range(t):
|
|
||||||
if i == index_not_full_deposit:
|
|
||||||
# Deposit with value below FULL_DEPOSIT_AMOUNT
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": min_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
# Eth2Genesis event should not be triggered
|
|
||||||
assert len(logs) == 0
|
|
||||||
else:
|
|
||||||
# Deposit with value FULL_DEPOSIT_AMOUNT
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": full_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
# Eth2Genesis event should not be triggered
|
|
||||||
assert len(logs) == 0
|
|
||||||
|
|
||||||
# Make 1 more deposit with value FULL_DEPOSIT_AMOUNT to trigger Eth2Genesis event
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": full_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
assert len(logs) == 1
|
|
||||||
timestamp = int(w3.eth.getBlock(w3.eth.blockNumber)['timestamp'])
|
|
||||||
timestamp_day_boundary = timestamp + (86400 - timestamp % 86400) + 86400
|
|
||||||
log = logs[0]['args']
|
|
||||||
assert log['deposit_root'] == modified_registration_contract.functions.get_deposit_root().call()
|
|
||||||
assert int.from_bytes(log['time'], byteorder='little') == timestamp_day_boundary
|
|
||||||
assert modified_registration_contract.functions.chainStarted().call() is True
|
|
||||||
|
|
||||||
# Make 1 deposit with value FULL_DEPOSIT_AMOUNT and
|
|
||||||
# check that Eth2Genesis event is not triggered
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": full_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
assert len(logs) == 0
|
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
- [`initiate_validator_exit`](#initiate_validator_exit)
|
- [`initiate_validator_exit`](#initiate_validator_exit)
|
||||||
- [`slash_validator`](#slash_validator)
|
- [`slash_validator`](#slash_validator)
|
||||||
- [Genesis](#genesis)
|
- [Genesis](#genesis)
|
||||||
- [`Eth2Genesis`](#eth2genesis)
|
- [Genesis trigger](#genesis-trigger)
|
||||||
- [Genesis state](#genesis-state)
|
- [Genesis state](#genesis-state)
|
||||||
- [Genesis block](#genesis-block)
|
- [Genesis block](#genesis-block)
|
||||||
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
||||||
|
@ -370,12 +370,12 @@ class PendingAttestation(Container):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Eth1Data(Container):
|
class Eth1Data(Container):
|
||||||
|
# Block hash
|
||||||
|
block_hash: Bytes32
|
||||||
# Root of the deposit tree
|
# Root of the deposit tree
|
||||||
deposit_root: Bytes32
|
deposit_root: Bytes32
|
||||||
# Total number of deposits
|
# Total number of deposits
|
||||||
deposit_count: uint64
|
deposit_count: uint64
|
||||||
# Block hash
|
|
||||||
block_hash: Bytes32
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `HistoricalBatch`
|
#### `HistoricalBatch`
|
||||||
|
@ -1156,20 +1156,45 @@ def slash_validator(state: BeaconState,
|
||||||
|
|
||||||
## Genesis
|
## Genesis
|
||||||
|
|
||||||
### `Eth2Genesis`
|
### Genesis trigger
|
||||||
|
|
||||||
When enough deposits of size `MAX_EFFECTIVE_BALANCE` have been made to the deposit contract an `Eth2Genesis` log is emitted triggering the genesis of the beacon chain. Let:
|
Whenever the deposit contract emits a `Deposit` log call the function `is_genesis_trigger(deposits: List[Deposit], timestamp: uint64) -> bool` where:
|
||||||
|
|
||||||
* `eth2genesis` be the object corresponding to `Eth2Genesis`
|
* `deposits` is the list of all deposits, ordered chronologically, up to and including the deposit triggering the latest `Deposit` log
|
||||||
* `genesis_eth1_data` be object of type `Eth1Data` where
|
* `timestamp` is the Unix timestamp in the Ethereum 1.0 block that emitted the latest `Deposit` log
|
||||||
* `genesis_eth1_data.deposit_root = eth2genesis.deposit_root`
|
|
||||||
* `genesis_eth1_data.deposit_count = eth2genesis.deposit_count`
|
When `is_genesis_trigger(deposits, timestamp) == True` for the first time let:
|
||||||
* `genesis_eth1_data.block_hash` is the hash of the Ethereum 1.0 block that emitted the `Eth2Genesis` log
|
|
||||||
* `genesis_deposits` be the object of type `List[Deposit]` with deposits ordered chronologically up to and including the deposit that triggered the `Eth2Genesis` log
|
* `genesis_deposits = deposits`
|
||||||
|
* `genesis_time = timestamp % SECONDS_PER_DAY + 2 * SECONDS_PER_DAY` where `SECONDS_PER_DAY = 86400`
|
||||||
|
* `genesis_eth1_data` be the object of type `Eth1Data` where:
|
||||||
|
* `genesis_eth1_data.block_hash` is the block hash corresponding to `deposits`
|
||||||
|
* `genesis_eth1_data.deposit_root` is the deposit root corresponding to `deposits`
|
||||||
|
* `genesis_eth1_data.deposit_count = len(genesis_deposits)`
|
||||||
|
|
||||||
|
*Note*: The function `is_genesis_trigger` has yet to be agreed by the community, and can be updated as necessary. We define the following testing placeholder:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def is_genesis_trigger(deposits: List[Deposit], timestamp: uint64) -> bool:
|
||||||
|
# Process deposits
|
||||||
|
state = BeaconState()
|
||||||
|
for deposit in deposits:
|
||||||
|
process_deposit(state, deposit)
|
||||||
|
|
||||||
|
# Count active validators at genesis
|
||||||
|
active_validator_count = 0
|
||||||
|
for validator in state.validator_registry:
|
||||||
|
if validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
|
||||||
|
active_validator_count += 1
|
||||||
|
|
||||||
|
# Check effective balance to trigger genesis
|
||||||
|
GENESIS_EFFECTIVE_BALANCE = 2**21
|
||||||
|
return active_validator_count * MAX_EFFECTIVE_BALANCE == GENESIS_EFFECTIVE_BALANCE
|
||||||
|
```
|
||||||
|
|
||||||
### Genesis state
|
### Genesis state
|
||||||
|
|
||||||
Let `genesis_state = get_genesis_beacon_state(genesis_deposits, eth2genesis.genesis_time, genesis_eth1_data)`.
|
Let `genesis_state = get_genesis_beacon_state(genesis_deposits, genesis_time, genesis_eth1_data)`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis_eth1_data: Eth1Data) -> BeaconState:
|
def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis_eth1_data: Eth1Data) -> BeaconState:
|
||||||
|
@ -1246,7 +1271,7 @@ def process_slot(state: BeaconState) -> None:
|
||||||
|
|
||||||
### Epoch processing
|
### Epoch processing
|
||||||
|
|
||||||
Note: the `# @LabelHere` lines below are placeholders to show that code will be inserted here in a future phase.
|
*Note*: the `# @LabelHere` lines below are placeholders to show that code will be inserted here in a future phase.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_epoch(state: BeaconState) -> None:
|
def process_epoch(state: BeaconState) -> None:
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
- [Table of contents](#table-of-contents)
|
- [Table of contents](#table-of-contents)
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Constants](#constants)
|
- [Constants](#constants)
|
||||||
- [Gwei values](#gwei-values)
|
|
||||||
- [Contract](#contract)
|
- [Contract](#contract)
|
||||||
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
|
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
|
||||||
- [Arguments](#arguments)
|
- [Arguments](#arguments)
|
||||||
|
@ -17,7 +16,6 @@
|
||||||
- [Amount](#amount)
|
- [Amount](#amount)
|
||||||
- [Event logs](#event-logs)
|
- [Event logs](#event-logs)
|
||||||
- [`Deposit` logs](#deposit-logs)
|
- [`Deposit` logs](#deposit-logs)
|
||||||
- [`Eth2Genesis` log](#eth2genesis-log)
|
|
||||||
- [Vyper code](#vyper-code)
|
- [Vyper code](#vyper-code)
|
||||||
|
|
||||||
<!-- /TOC -->
|
<!-- /TOC -->
|
||||||
|
@ -28,19 +26,12 @@ This document represents the specification for the beacon chain deposit contract
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
### Gwei values
|
|
||||||
|
|
||||||
| Name | Value | Unit |
|
|
||||||
| - | - | - |
|
|
||||||
| `FULL_DEPOSIT_AMOUNT` | `32 * 10**9` | Gwei |
|
|
||||||
|
|
||||||
### Contract
|
### Contract
|
||||||
|
|
||||||
| Name | Value |
|
| Name | Value |
|
||||||
| - | - |
|
| - | - |
|
||||||
| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** |
|
| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** |
|
||||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
||||||
| `CHAIN_START_FULL_DEPOSIT_THRESHOLD` | `2**16` (= 65,536) |
|
|
||||||
|
|
||||||
## Ethereum 1.0 deposit contract
|
## Ethereum 1.0 deposit contract
|
||||||
|
|
||||||
|
@ -62,7 +53,6 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat
|
||||||
#### Amount
|
#### Amount
|
||||||
|
|
||||||
* A valid deposit amount should be at least `MIN_DEPOSIT_AMOUNT` in Gwei.
|
* A valid deposit amount should be at least `MIN_DEPOSIT_AMOUNT` in Gwei.
|
||||||
* A deposit with an amount greater than or equal to `FULL_DEPOSIT_AMOUNT` in Gwei is considered as a full deposit.
|
|
||||||
|
|
||||||
## Event logs
|
## Event logs
|
||||||
|
|
||||||
|
@ -70,16 +60,6 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat
|
||||||
|
|
||||||
Every Ethereum 1.0 deposit, of size at least `MIN_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract.
|
Every Ethereum 1.0 deposit, of size at least `MIN_DEPOSIT_AMOUNT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract.
|
||||||
|
|
||||||
### `Eth2Genesis` log
|
|
||||||
|
|
||||||
When `CHAIN_START_FULL_DEPOSIT_THRESHOLD` of full deposits have been made, the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_genesis_beacon_state` function (defined [here](./0_beacon-chain.md#genesis-state)) where:
|
|
||||||
|
|
||||||
* `genesis_time` equals `time` in the `Eth2Genesis` log
|
|
||||||
* `latest_eth1_data.deposit_root` equals `deposit_root` in the `Eth2Genesis` log
|
|
||||||
* `latest_eth1_data.deposit_count` equals `deposit_count` in the `Eth2Genesis` log
|
|
||||||
* `latest_eth1_data.block_hash` equals the hash of the block that included the log
|
|
||||||
* `genesis_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `Eth2Genesis` log, processed in the order in which they were emitted (oldest to newest)
|
|
||||||
|
|
||||||
## Vyper code
|
## Vyper code
|
||||||
|
|
||||||
The source for the Vyper contract lives in a [separate repository](https://github.com/ethereum/deposit_contract) at [https://github.com/ethereum/deposit_contract/blob/master/deposit_contract/contracts/validator_registration.v.py](https://github.com/ethereum/deposit_contract/blob/master/deposit_contract/contracts/validator_registration.v.py).
|
The source for the Vyper contract lives in a [separate repository](https://github.com/ethereum/deposit_contract) at [https://github.com/ethereum/deposit_contract/blob/master/deposit_contract/contracts/validator_registration.v.py](https://github.com/ethereum/deposit_contract/blob/master/deposit_contract/contracts/validator_registration.v.py).
|
||||||
|
|
Loading…
Reference in New Issue