Remove eth2 genesis in favour of genesis trigger

This commit is contained in:
Justin Drake 2019-06-08 19:00:50 +01:00
parent 6f82480df2
commit d1e589f11f
6 changed files with 42 additions and 129 deletions

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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).