From 2fedaf082427503beb346d59e33de83194aee319 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 31 Oct 2018 03:44:18 -0400 Subject: [PATCH 01/21] Chain initialization and main chain block inclusion --- specs/beacon-chain.md | 67 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 6c967a8e4..28c47c56e 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -35,7 +35,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | `MIN_ONLINE_DEPOSIT_SIZE` | 2**4 (= 16) | ETH | | `GWEI_PER_ETH` | 10**9 | Gwei/ETH | | `MIN_COMMITTEE_SIZE` | 2**7 (= 128) | validators | -| `GENESIS_TIME` | **TBD** | seconds | +| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** | - | | `SLOT_DURATION` | 2**4 (= 16) | seconds | | `CYCLE_LENGTH` | 2**6 (= 64) | slots | ~17 minutes | | `MIN_VALIDATOR_SET_CHANGE_INTERVAL` | 2**8 (= 256) | slots | ~1.1 hours | @@ -44,6 +44,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days | | `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — | | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | +| `POW_HASH_VOTING_PERIOD` | 2**10 (=1024) | - | | `LOGOUT_MESSAGE` | `"LOGOUT"` | — | | `INITIAL_FORK_VERSION` | 0 | — | @@ -98,7 +99,7 @@ A `BeaconBlock` has the following fields: # Proposer RANDAO reveal 'randao_reveal': 'hash32', # Recent PoW chain reference (block hash) - 'pow_chain_reference': 'hash32', + 'candidate_pow_hash_chain_tip': 'hash32', # Skip list of previous beacon block hashes # i'th item is the most recent ancestor who's slot is a multiple of 2**i for i = 0, ..., 31 'ancestor_hashes': ['hash32'], @@ -209,6 +210,13 @@ The `CrystallizedState` has the following fields: 'deposits_penalized_in_period': ['uint32'], # Hash chain of validator set changes (for light clients to easily track deltas) 'validator_set_delta_hash_chain': 'hash32' + # Genesis time + 'genesis_time': 'hash32', + # PoW chain reference + 'known_pow_hash_chain_tip': 'hash32', + 'processed_pow_hash_chain_tip': 'hash32', + 'candidate_pow_hash_chain_tip': 'hash32', + 'candidate_pow_hash_chain_tip_votes': 'uint64', # Parameters relevant to hard forks / versioning. # Should be updated only by hard forks. 'pre_fork_version': 'uint32', @@ -485,13 +493,51 @@ def int_sqrt(n: int) -> int: return x ``` +### PoW chain contract + +The beacon chain is initialized when a condition is met inside a contract on the existing PoW chain. This contract's code in Vyper is as follows: + +```python +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) +``` + +The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. 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 of the form: + +```python +{ + '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. When the contract publishes a `ChainStart` log, this initializes the chain, calling `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). +* `genesis_time` equal to the `time` value published in the log +* `pow_hash_chain_tip` equal to the `hash_chain_tip` value published in the log ### On startup Run the following code: ```python -def on_startup(initial_validator_entries: List[Any]) -> Tuple[CrystallizedState, ActiveState]: +def on_startup(initial_validator_entries: List[Any], genesis_time: uint64, pow_hash_chain_tip: Hash32) -> Tuple[CrystallizedState, ActiveState]: # Induct validators validators = [] for pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, \ @@ -527,6 +573,11 @@ def on_startup(initial_validator_entries: List[Any]) -> Tuple[CrystallizedState, shard_and_committee_for_slots=x + x, deposits_penalized_in_period=[], validator_set_delta_hash_chain=bytes([0] * 32), # stub + genesis_time=genesis_time, + known_pow_hash_chain_tip=pow_hash_chain_tip, + processed_pow_hash_chain_tip=pow_hash_chain_tip, + candidate_pow_hash_chain_tip=bytes([0] * 32), + candidate_pow_hash_chain_tip_votes=0, pre_fork_version=INITIAL_FORK_VERSION, post_fork_version=INITIAL_FORK_VERSION, fork_slot_number=0 @@ -739,6 +790,16 @@ For each `SpecialRecord` `obj` in `active_state.pending_specials`: * Set `active_state.recent_block_hashes = active_state.recent_block_hashes[CYCLE_LENGTH:]` * Set `shard_and_committee_for_slots[:CYCLE_LENGTH] = shard_and_committee_for_slots[CYCLE_LENGTH:]` +### PoW chain related rules + +If `block.slot % POW_HASH_VOTING_PERIOD == 0`, then: + +* If `crystallized_state.candidate_hash_chain_tip_votes * 3 >= POW_HASH_VOTING_PERIOD * 2`, set `crystallized_state.hash_chain_tip = crystallized_state.candidate_hash_chain_tip` +* Set `crystallized_state.candidate_hash_chain_tip = block.candidate_pow_hash_chain_tip` +* Set `crystallized_state.candidate_hash_chain_tip_votes = 0` + +Otherwise, if `block.candidate_pow_hash_chain_tip = crystallized_state.candidate_pow_hash_chain_tip`, set `crystallized_state.candidate_hash_chain_tip_votes += 1`. + ### Validator set change A validator set change can happen after a state recalculation if all of the following criteria are satisfied: From eb1e8311c3ceda5b71fc11b73cc497177ca2cd60 Mon Sep 17 00:00:00 2001 From: Vitalik Buterin Date: Mon, 5 Nov 2018 07:37:07 -0500 Subject: [PATCH 02/21] Implemented withdrawal rate limit in place of wihdrawal delay --- specs/beacon-chain.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 220759ef3..8396effe3 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -42,7 +42,9 @@ The primary source of load on the beacon chain are "attestations". Attestations | `MIN_VALIDATOR_SET_CHANGE_INTERVAL` | 2**8 (= 256) | slots | ~1.1 hours | | `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours | | `SQRT_E_DROP_TIME` | 2**16 (= 65,536) | slots | ~12 days | -| `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days | +| `MIN_WITHDRAWAL_PERIOD` | 2**12 (= 4096) | slots | ~18 hours | +| `WITHDRAWALS_PER_CYCLE` | 8 | - | 4.3m ETH in ~6 months | +| `COLLECTIVE_PENALTY_CALCULATION_PERIOD` | 2**19 (= 524,288) | slots | ~3 months | | `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — | | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | | `LOGOUT_MESSAGE` | `"LOGOUT"` | — | @@ -210,6 +212,8 @@ The `CrystallizedState` has the following fields: 'deposits_penalized_in_period': ['uint32'], # Hash chain of validator set changes (for light clients to easily track deltas) 'validator_set_delta_hash_chain': 'hash32' + # Current sequence number for withdrawals + 'current_exit_seq': 'uint64', # Parameters relevant to hard forks / versioning. # Should be updated only by hard forks. 'pre_fork_version': 'uint32', @@ -238,6 +242,8 @@ A `ValidatorRecord` has the following fields: 'status': 'uint8', # Slot when validator exited (or 0) 'exit_slot': 'uint64' + # Sequence number when validator exited (or 0) + 'exit_seq': 'uint64' } ``` @@ -584,7 +590,8 @@ def add_validator(validators: List[ValidatorRecord], randao_last_change=current_slot, balance=DEPOSIT_SIZE * GWEI_PER_ETH, status=status, - exit_slot=0 + exit_slot=0, + exit_seq=0 ) index = min_empty_validator(validators) if index is None: @@ -601,9 +608,11 @@ def add_validator(validators: List[ValidatorRecord], def exit_validator(index, crystallized_state, penalize, current_slot): validator = crystallized_state.validators[index] validator.exit_slot = current_slot + validator.exit_seq = crystallized_state.current_exit_seq + crystallized_state.current_exit_seq += 1 if penalize: validator.status = PENALIZED - crystallized_state.deposits_penalized_in_period[current_slot // WITHDRAWAL_PERIOD] += validator.balance + crystallized_state.deposits_penalized_in_period[current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += validator.balance else: validator.status = PENDING_EXIT add_validator_set_change_record(crystallized_state, index, validator.pubkey, EXIT) @@ -783,7 +792,7 @@ def change_validators(validators: List[ValidatorRecord]) -> None: break # Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods - period_index = current_slot // WITHDRAWAL_PERIOD + period_index = current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD total_penalties = ( (crystallized_state.deposits_penalized_in_period[period_index]) + (crystallized_state.deposits_penalized_in_period[period_index - 1] if period_index >= 1 else 0) + @@ -791,15 +800,18 @@ def change_validators(validators: List[ValidatorRecord]) -> None: ) # Separate loop to withdraw validators that have been logged out for long enough, and # calculate their penalties if they were slashed - for i in range(len(validators)): - if validators[i].status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= validators[i].exit_slot + WITHDRAWAL_PERIOD: - if validators[i].status == PENALIZED: - validators[i].balance -= validators[i].balance * min(total_penalties * 3, total_balance) // total_balance - validators[i].status = WITHDRAWN - withdraw_amount = validators[i].balance - ... - # STUB: withdraw to shard chain + def withdrawable(v): + return v.status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= v.exit_slot + MIN_WITHDRAWAL_PERIOD + + withdrawable_validators = sorted(filter(withdrawable, validators), key=lambda v: v.exit_seq) + for v in withdrawable_validators[:WITHDRAWALS_PER_CYCLE]: + if v.status == PENALIZED: + v.balance -= v.balance * min(total_penalties * 3, total_balance) // total_balance + v.status = WITHDRAWN + withdraw_amount = v.balance + ... + # STUB: withdraw to shard chain ``` Finally: From 20358f8d18cc9240e5df12bb30a6fec7c439516e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 9 Nov 2018 09:23:33 -0500 Subject: [PATCH 03/21] Updated the PR to include deposits Includes https://github.com/ethereum/eth2.0-specs/issues/71#issuecomment-435837594 --- specs/beacon-chain.md | 48 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 28c47c56e..42455f2a6 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -42,6 +42,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours | | `SQRT_E_DROP_TIME` | 2**16 (= 65,536) | slots | ~12 days | | `WITHDRAWAL_PERIOD` | 2**19 (= 524,288) | slots | ~97 days | +| `DELETION_PERIOD` | 2**21 (= 2,097,152) | slots | ~1.06 years | | `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — | | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | | `POW_HASH_VOTING_PERIOD` | 2**10 (=1024) | - | @@ -98,8 +99,8 @@ A `BeaconBlock` has the following fields: 'slot': 'uint64', # Proposer RANDAO reveal 'randao_reveal': 'hash32', - # Recent PoW chain reference (block hash) - 'candidate_pow_hash_chain_tip': 'hash32', + # Recent PoW chain reference (receipt root) + 'candidate_pow_receipt_root': 'hash32', # Skip list of previous beacon block hashes # i'th item is the most recent ancestor who's slot is a multiple of 2**i for i = 0, ..., 31 'ancestor_hashes': ['hash32'], @@ -213,10 +214,9 @@ The `CrystallizedState` has the following fields: # Genesis time 'genesis_time': 'hash32', # PoW chain reference - 'known_pow_hash_chain_tip': 'hash32', - 'processed_pow_hash_chain_tip': 'hash32', - 'candidate_pow_hash_chain_tip': 'hash32', - 'candidate_pow_hash_chain_tip_votes': 'uint64', + 'known_pow_receipt_root': 'hash32', + 'candidate_pow_receipt_root': 'hash32', + 'candidate_pow_receipt_root_votes': 'uint64', # Parameters relevant to hard forks / versioning. # Should be updated only by hard forks. 'pre_fork_version': 'uint32', @@ -498,23 +498,32 @@ def int_sqrt(n: int) -> int: The beacon chain is initialized when a condition is met inside a contract on the existing PoW chain. This contract's code in Vyper is as follows: ```python -HashChainValue: event({prev_tip: bytes32, data: bytes[2048], value: wei_value, total_deposit_count: int128}) +HashChainValue: event({prev_tip: bytes32, data: bytes[2048], value: wei_value, time: timestamp, total_deposit_count: int128}) ChainStart: event({hash_chain_tip: bytes32, time: timestamp}) -hash_chain_tip: public(bytes32) +receipt_tree: bytes32[int128] total_deposit_count: int128 @payable @public def deposit(data: bytes[2048]): - log.HashChainValue(self.hash_chain_tip, data, msg.value, self.total_deposit_count) + log.HashChainValue(self.receipt_tree[1], data, msg.value, block.timestamp, self.total_deposit_count) + index:int128 = self.total_deposit_count + 2**32 + self.receipt_tree[index] = sha3(concat(data, as_bytes32(msg.value), as_bytes32(block.timestamp)) + for i in range(32): + index //= 2 + self.receipt_tree[index] = sha3(concat(self.receipt_tree[index * 2], self.receipt_tree[index * 2 + 1])) 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) + log.ChainStart(self.receipt_tree[1], block.timestamp) + +@public +@constant +def get_receipt_root() -> bytes32: + return self.receipt_tree[1] ``` -The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. 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 of the form: +The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. 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 `DepositParams` object of the form: ```python { @@ -627,8 +636,9 @@ def add_validator(validators: List[ValidatorRecord], current_slot: int) -> int: # if following assert fails, validator induction failed # move on to next validator registration log + signed_message = as_bytes32(pubkey) + as_bytes2(withdrawal_shard) + withdrawal_address + randao_commitment assert BLSVerify(pub=pubkey, - msg=hash(pubkey), + msg=hash(signed_message), sig=proof_of_possession) rec = ValidatorRecord( pubkey=pubkey, @@ -780,6 +790,7 @@ For each `SpecialRecord` `obj` in `active_state.pending_specials`: * **[covers logouts]**: If `obj.kind == LOGOUT`, interpret `data[0]` as a validator index as an `uint32` and `data[1]` as a signature. If `BLSVerify(pubkey=validators[data[0]].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=data[1])`, where `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`, and `validators[i].status == ACTIVE`, run `exit_validator(data[0], crystallized_state, penalize=False, current_slot=block.slot)` * **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, crystallized_state, penalize=True, current_slot=block.slot)` * **[covers RANDAO updates]**: If `obj.kind == RANDAO_REVEAL`, interpret `data[0]` as an integer and `data[1]` as a hash32. Set `validators[data[0]].randao_commitment = data[1]`. +* **[covers deposits]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as being a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(block.timestamp)` where `data` itself is a `DepositParams` object. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. #### Finally... @@ -811,7 +822,7 @@ A validator set change can happen after a state recalculation if all of the foll Then, run the following algorithm to update the validator set: ```python -def change_validators(validators: List[ValidatorRecord]) -> None: +def change_validators(validators: List[ValidatorRecord], current_slot) -> None: # The active validator set active_validators = get_active_validator_indices(validators) # The total balance of active validators @@ -855,15 +866,22 @@ def change_validators(validators: List[ValidatorRecord]) -> None: ) # Separate loop to withdraw validators that have been logged out for long enough, and # calculate their penalties if they were slashed - for i in range(len(validators)): + i = 0 + while i < len(validators): if validators[i].status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= validators[i].exit_slot + WITHDRAWAL_PERIOD: if validators[i].status == PENALIZED: validators[i].balance -= validators[i].balance * min(total_penalties * 3, total_balance) // total_balance validators[i].status = WITHDRAWN + validators[i].exit_slot = current_slot withdraw_amount = validators[i].balance ... # STUB: withdraw to shard chain + if validators[i].status == WITHDRAWN and current_slot >= validators[i].exit_slot + DELETION_PERIOD: + validators.pop(i) + else: + i += 1 + ``` Finally: From f7a8eb5a3e6be6ad8bb497d3e0b5f0995aa85cee Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 9 Nov 2018 15:31:50 -0500 Subject: [PATCH 04/21] Fixed bug in previous version Validators cannot simply be "popped out" of the list because it's super-important for validator indices to be immutable. Instead, the validator stays until a new run of `add_validator` swaps it out. --- specs/beacon-chain.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 42455f2a6..b68c98d42 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -616,9 +616,9 @@ This routine should be run for every validator that is inducted as part of a log First, a helper function: ```python -def min_empty_validator(validators: List[ValidatorRecord]): +def min_empty_validator(validators: List[ValidatorRecord], current_slot: int): for i, v in enumerate(validators): - if v.status == WITHDRAWN: + if v.status == WITHDRAWN and v.exit_slot <= current_slot - DELETION_PERIOD: return i return None ``` @@ -866,8 +866,7 @@ def change_validators(validators: List[ValidatorRecord], current_slot) -> None: ) # Separate loop to withdraw validators that have been logged out for long enough, and # calculate their penalties if they were slashed - i = 0 - while i < len(validators): + for i in range(len(validators)): if validators[i].status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= validators[i].exit_slot + WITHDRAWAL_PERIOD: if validators[i].status == PENALIZED: validators[i].balance -= validators[i].balance * min(total_penalties * 3, total_balance) // total_balance @@ -877,10 +876,6 @@ def change_validators(validators: List[ValidatorRecord], current_slot) -> None: withdraw_amount = validators[i].balance ... # STUB: withdraw to shard chain - if validators[i].status == WITHDRAWN and current_slot >= validators[i].exit_slot + DELETION_PERIOD: - validators.pop(i) - else: - i += 1 ``` From add815d8f8f69bc6228069ee83ed449e1a1589e3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 13 Nov 2018 08:57:24 -0500 Subject: [PATCH 05/21] Fixed a bunch of comments. --- specs/beacon-chain.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index b68c98d42..fea405f85 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -216,7 +216,7 @@ The `CrystallizedState` has the following fields: # PoW chain reference 'known_pow_receipt_root': 'hash32', 'candidate_pow_receipt_root': 'hash32', - 'candidate_pow_receipt_root_votes': 'uint64', + 'candidate_pow_receipt_root_votes': 'uint32', # Parameters relevant to hard forks / versioning. # Should be updated only by hard forks. 'pre_fork_version': 'uint32', @@ -523,7 +523,7 @@ def get_receipt_root() -> bytes32: return self.receipt_tree[1] ``` -The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. 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 `DepositParams` object of the form: +The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. When a user wishes to become a validator by moving 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 `DepositParams` object of the form: ```python { @@ -531,11 +531,11 @@ The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. When a user wishes to mov 'proof_of_possession': ['int256'], 'withdrawal_shard': 'int64', 'withdrawal_address`: 'bytes20', - 'randao_commitment`: 'hash32', + 'randao_commitment`: 'hash32' } ``` -If they wish to deposit more than 32 ETH, they would need to make multiple calls. When the contract publishes a `ChainStart` log, this initializes the chain, calling `on_startup` with: +If the user wishes to deposit more than 32 ETH, they would need to make multiple calls. When the contract publishes a `ChainStart` log, this initializes the chain, calling `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). * `genesis_time` equal to the `time` value published in the log @@ -640,6 +640,8 @@ def add_validator(validators: List[ValidatorRecord], assert BLSVerify(pub=pubkey, msg=hash(signed_message), sig=proof_of_possession) + # Pubkey uniqueness + assert pubkey not in [v.pubkey for v in validators] rec = ValidatorRecord( pubkey=pubkey, withdrawal_shard=withdrawal_shard, @@ -728,6 +730,8 @@ Additionally, verify and update the RANDAO reveal. This is done as follows: * Let `V = crystallized_state.validators[curblock_proposer_index]`. * Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_reveal) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment`, and set `active_state.randao_mix = xor(active_state.randao_mix, block.randao_reveal)` and append to `ActiveState.pending_specials` a `SpecialObject(kind=RANDAO_CHANGE, data=[bytes8(curblock_proposer_index), block.randao_reveal])`. +Finally, if `block.candidate_pow_hash_chain_tip = crystallized_state.candidate_pow_hash_chain_tip`, set `crystallized_state.candidate_hash_chain_tip_votes += 1`. + ### State recalculations (every `CYCLE_LENGTH` slots) Repeat while `slot - last_state_recalculation_slot >= CYCLE_LENGTH`: @@ -790,7 +794,7 @@ For each `SpecialRecord` `obj` in `active_state.pending_specials`: * **[covers logouts]**: If `obj.kind == LOGOUT`, interpret `data[0]` as a validator index as an `uint32` and `data[1]` as a signature. If `BLSVerify(pubkey=validators[data[0]].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=data[1])`, where `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`, and `validators[i].status == ACTIVE`, run `exit_validator(data[0], crystallized_state, penalize=False, current_slot=block.slot)` * **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, crystallized_state, penalize=True, current_slot=block.slot)` * **[covers RANDAO updates]**: If `obj.kind == RANDAO_REVEAL`, interpret `data[0]` as an integer and `data[1]` as a hash32. Set `validators[data[0]].randao_commitment = data[1]`. -* **[covers deposits]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as being a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(block.timestamp)` where `data` itself is a `DepositParams` object. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. +* **[covers deposits]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as being a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. #### Finally... @@ -801,16 +805,14 @@ For each `SpecialRecord` `obj` in `active_state.pending_specials`: * Set `active_state.recent_block_hashes = active_state.recent_block_hashes[CYCLE_LENGTH:]` * Set `shard_and_committee_for_slots[:CYCLE_LENGTH] = shard_and_committee_for_slots[CYCLE_LENGTH:]` -### PoW chain related rules +#### PoW chain related rules -If `block.slot % POW_HASH_VOTING_PERIOD == 0`, then: +If `last_state_recalculation_slot % POW_HASH_VOTING_PERIOD == 0`, then: * If `crystallized_state.candidate_hash_chain_tip_votes * 3 >= POW_HASH_VOTING_PERIOD * 2`, set `crystallized_state.hash_chain_tip = crystallized_state.candidate_hash_chain_tip` * Set `crystallized_state.candidate_hash_chain_tip = block.candidate_pow_hash_chain_tip` * Set `crystallized_state.candidate_hash_chain_tip_votes = 0` -Otherwise, if `block.candidate_pow_hash_chain_tip = crystallized_state.candidate_pow_hash_chain_tip`, set `crystallized_state.candidate_hash_chain_tip_votes += 1`. - ### Validator set change A validator set change can happen after a state recalculation if all of the following criteria are satisfied: @@ -822,7 +824,7 @@ A validator set change can happen after a state recalculation if all of the foll Then, run the following algorithm to update the validator set: ```python -def change_validators(validators: List[ValidatorRecord], current_slot) -> None: +def change_validators(validators: List[ValidatorRecord], current_slot: int) -> None: # The active validator set active_validators = get_active_validator_indices(validators) # The total balance of active validators From 9825cc98fe310bb7763755808b091c3f1182ae43 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 16 Nov 2018 08:16:35 +0900 Subject: [PATCH 06/21] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2a252848c..0223886a0 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -808,7 +808,7 @@ For each `SpecialRecord` `obj` in `state.pending_specials`: * **[covers `LOGOUT`]**: If `obj.kind == LOGOUT`, interpret `data[0]` as a `uint24` and `data[1]` as a `hash32`, where `validator_index = data[0]` and `signature = data[1]`. If `BLSVerify(pubkey=validators[validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=signature)`, where `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`, and `validators[validator_index].status == ACTIVE`, run `exit_validator(validator_index, state, penalize=False, current_slot=block.slot)` * **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, state, penalize=True, current_slot=block.slot)` * **[covers `RANDAO_CHANGE`]**: If `obj.kind == RANDAO_CHANGE`, interpret `data[0]` as a `uint24`, `data[1]` as a `hash32`, and `data[2]` as a `uint64`, where `proposer_index = data[0]`, `randao_commitment = data[1]`, and `randao_last_change = data[2]`. Set `validators[proposer_index].randao_commitment = randao_commitment`, and set `validators[proposer_index].randao_last_change = randao_last_change`. -* **[covers `DEPOSIT_PROOF`]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as a `hash32` Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the `int128` index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. +* **[covers `DEPOSIT_PROOF`]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the `int128` index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. #### Finally... From 65dc333549353df1d338cf7ad51a00b3cafa66a4 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 16 Nov 2018 07:54:03 -0500 Subject: [PATCH 07/21] Move specials into block processing, and clean up specification Moves the procedure for handling specials into the per-block processing loop. Cleans up the specification for handling them to be more unambiguous, and changes the formats to be more readable and simpler to implement. --- specs/core/0_beacon-chain.md | 187 ++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 81 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 273bf75f5..642cf4a0b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -171,7 +171,7 @@ A `SpecialRecord` has the following fields: # Kind 'kind': 'uint8', # Data - 'data': ['bytes'] + 'data': 'bytes' } ``` @@ -213,8 +213,6 @@ The `BeaconState` has the following fields: 'fork_slot_number': 'uint64', # Attestations not yet processed 'pending_attestations': [AttestationRecord], - # Specials not yet been processed - 'pending_specials': [SpecialRecord] # recent beacon block hashes needed to process attestations, older to newer 'recent_block_hashes': ['hash32'], # RANDAO state @@ -503,7 +501,70 @@ def int_sqrt(n: int) -> int: return x ``` -### On startup +### Routine for adding a validator + +This routine should be run for every validator that is inducted as part of a log created on the PoW chain [TODO: explain where to check for these logs]. The status of the validators added after genesis is `PENDING_ACTIVATION`. These logs should be processed in the order in which they are emitted by the PoW chain. + +First, a helper function: + +```python +def min_empty_validator(validators: List[ValidatorRecord]): + for i, v in enumerate(validators): + if v.status == WITHDRAWN: + return i + return None +``` + +Now, to add a validator: + +```python +def add_validator(validators: List[ValidatorRecord], + pubkey: int, + proof_of_possession: bytes, + withdrawal_shard: int, + withdrawal_address: Address, + randao_commitment: Hash32, + status: int, + current_slot: int) -> int: + # if following assert fails, validator induction failed + # move on to next validator registration log + assert BLSVerify(pub=pubkey, + msg=hash(pubkey), + sig=proof_of_possession) + rec = ValidatorRecord( + pubkey=pubkey, + withdrawal_shard=withdrawal_shard, + withdrawal_address=withdrawal_address, + randao_commitment=randao_commitment, + randao_last_change=current_slot, + balance=DEPOSIT_SIZE * GWEI_PER_ETH, + status=status, + exit_slot=0 + ) + index = min_empty_validator(validators) + if index is None: + validators.append(rec) + return len(validators) - 1 + else: + validators[index] = rec + return index +``` + +### Routine for removing a validator + +```python +def exit_validator(index, state, penalize, current_slot): + validator = state.validators[index] + validator.exit_slot = current_slot + if penalize: + validator.status = PENALIZED + state.deposits_penalized_in_period[current_slot // WITHDRAWAL_PERIOD] += validator.balance + else: + validator.status = PENDING_EXIT + add_validator_set_change_record(state, index, validator.pubkey, EXIT) +``` + +## On startup Run the following code: @@ -558,72 +619,7 @@ def on_startup(initial_validator_entries: List[Any]) -> BeaconState: return state ``` -The `add_validator` routine is defined below. - -### Routine for adding a validator - -This routine should be run for every validator that is inducted as part of a log created on the PoW chain [TODO: explain where to check for these logs]. The status of the validators added after genesis is `PENDING_ACTIVATION`. These logs should be processed in the order in which they are emitted by the PoW chain. - -First, a helper function: - -```python -def min_empty_validator(validators: List[ValidatorRecord]): - for i, v in enumerate(validators): - if v.status == WITHDRAWN: - return i - return None -``` - -Now, to add a validator: - -```python -def add_validator(validators: List[ValidatorRecord], - pubkey: int, - proof_of_possession: bytes, - withdrawal_shard: int, - withdrawal_address: Address, - randao_commitment: Hash32, - status: int, - current_slot: int) -> int: - # if following assert fails, validator induction failed - # move on to next validator registration log - assert BLSVerify(pub=pubkey, - msg=hash(pubkey), - sig=proof_of_possession) - rec = ValidatorRecord( - pubkey=pubkey, - withdrawal_shard=withdrawal_shard, - withdrawal_address=withdrawal_address, - randao_commitment=randao_commitment, - randao_last_change=current_slot, - balance=DEPOSIT_SIZE * GWEI_PER_ETH, - status=status, - exit_slot=0 - ) - index = min_empty_validator(validators) - if index is None: - validators.append(rec) - return len(validators) - 1 - else: - validators[index] = rec - return index -``` - -## Routine for removing a validator - -```python -def exit_validator(index, state, penalize, current_slot): - validator = state.validators[index] - validator.exit_slot = current_slot - if penalize: - validator.status = PENALIZED - state.deposits_penalized_in_period[current_slot // WITHDRAWAL_PERIOD] += validator.balance - else: - validator.status = PENDING_EXIT - add_validator_set_change_record(state, index, validator.pubkey, EXIT) -``` - -### Per-block processing +## Per-block processing This procedure should be carried out every beacon block. @@ -669,7 +665,7 @@ For each one of these attestations: * Let `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`. * Verify that `aggregate_sig` verifies using the group pubkey generated and the serialized form of `AttestationSignedData(fork_version, slot, shard, parent_hashes, shard_block_hash, last_crosslinked_hash, shard_block_combined_data_root, justified_slot)` as the message. -Extend the list of `AttestationRecord` objects in the `state` with those included in the block, ordering the new additions in the same order as they came in the block. Similarly extend the list of `SpecialRecord` objects in the `state` with those included in the block. +Extend the list of `AttestationRecord` objects in the `state` with those included in the block, ordering the new additions in the same order as they came in the block. Let `curblock_proposer_index` be the validator index of the `block.slot % len(get_shards_and_committees_for_slot(state, block.slot)[0].committee)`'th attester in `get_shards_and_committees_for_slot(state, block.slot)[0]`, and `parent_proposer_index` be the validator index of the parent block, calculated similarly. Verify that an attestation from the `parent_proposer_index`'th validator is part of the first (ie. item 0 in the array) `AttestationRecord` object; this attester can be considered to be the proposer of the parent block. In general, when a beacon block is produced, it is broadcasted at the network layer along with the attestation from its proposer. @@ -677,9 +673,46 @@ Additionally, verify and update the RANDAO reveal. This is done as follows: * Let `repeat_hash(x, n) = x if n == 0 else repeat_hash(hash(x), n-1)`. * Let `V = state.validators[curblock_proposer_index]`. -* Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_change) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment`, and set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)` and append to `state.pending_specials` a `SpecialObject(kind=RANDAO_CHANGE, data=[bytes8(curblock_proposer_index), block.randao_reveal, bytes8(block.slot)])`. +* Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_change) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment`, and set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)` and set `V.randao_commitment = block.randao_reveal`, and set `V.randao_last_change = block.slot`. -### State recalculations (every `CYCLE_LENGTH` slots) +### Process penalties, logouts and other special objects + +For each `SpecialRecord` `obj` in `block.specials`, verify that its `kind` is one of the below values, and that `obj.data` deserializes according to the format for the given `kind`, then process it. The word "verify" when used below means that if the given verification check fails, the block containing that `SpecialRecord` is invalid. + +#### LOGOUT + +```python +{ + 'validator_index': 'uint64', + 'signature': '[uint256]' +} +``` + +Verify that `BLSVerify(pubkey=validators[data.validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=data.signature)`, where `fork_version = pre_fork_version if block.slot < fork_slot_number else post_fork_version`, and `validators[validator_index].status == ACTIVE`. Run `exit_validator(data.validator_index, state, penalize=False, current_slot=block.slot)`. + +#### CASPER_SLASHING + +```python +{ + 'vote1_aggsig_indices': '[uint24]', + 'vote1_data': AttestationSignedData, + 'vote1_aggsig': '[uint256]', + 'vote2_aggsig_indices': '[uint24]', + 'vote2_data': AttestationSignedData, + 'vote2_aggsig': '[uint256]', +} +``` + +Verify that: + +* For each `aggsig`, `BLSVerify(pubkey=aggregate_pubkey([validators[i].pubkey for i in aggsig_indices]), msg=vote_data, sig=aggsig)` passes. +* `vote1_data != vote2_data` +* Let `intersection = [x for x in vote1_aggsig_indices if x in vote2_aggsig_indices]`. Verify that `len(intersection) >= 1`. +* `vote1_data.justified_slot < vote2_data.justified_slot < vote2_data.slot <= vote1_data.slot` + +For each validator index `v` in `intersection`, if `state.validators[v].status` does not equal `PENALIZED`, then run `exit_validator(v, state, penalize=True, current_slot=block.slot)` + +## State recalculations (every `CYCLE_LENGTH` slots) Repeat while `slot - last_state_recalculation_slot >= CYCLE_LENGTH`: @@ -734,14 +767,6 @@ For every shard number `shard` for which a crosslink committee exists in the cyc * Participating validators gain `B // reward_quotient * (2 * total_balance_of_v_participating - total_balance_of_v) // total_balance_of_v`. * Non-participating validators lose `B // reward_quotient`. -#### Process penalties, logouts and other special objects - -For each `SpecialRecord` `obj` in `state.pending_specials`: - -* **[covers `LOGOUT`]**: If `obj.kind == LOGOUT`, interpret `data[0]` as a `uint24` and `data[1]` as a `hash32`, where `validator_index = data[0]` and `signature = data[1]`. If `BLSVerify(pubkey=validators[validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=signature)`, where `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`, and `validators[validator_index].status == ACTIVE`, run `exit_validator(validator_index, state, penalize=False, current_slot=block.slot)` -* **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, state, penalize=True, current_slot=block.slot)` -* **[covers `RANDAO_CHANGE`]**: If `obj.kind == RANDAO_CHANGE`, interpret `data[0]` as a `uint24`, `data[1]` as a `hash32`, and `data[2]` as a `uint64`, where `proposer_index = data[0]`, `randao_commitment = data[1]`, and `randao_last_change = data[2]`. Set `validators[proposer_index].randao_commitment = randao_commitment`, and set `validators[proposer_index].randao_last_change = randao_last_change`. - ### Validator set change A validator set change can happen after a state recalculation if all of the following criteria are satisfied: From c9c85e8645025db7c79d0e348de9da587798e29e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 16 Nov 2018 10:48:57 -0500 Subject: [PATCH 08/21] Added max specials count. --- specs/core/0_beacon-chain.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 642cf4a0b..67db53b4d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -47,6 +47,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | 2**16 (= 65,536) | slots | ~12 days | | `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — | | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | +| `MAX_SPECIALS_PER_BLOCK` | 2**4 (= 16) | - | | `LOGOUT_MESSAGE` | `"LOGOUT"` | — | | `INITIAL_FORK_VERSION` | 0 | — | @@ -677,6 +678,8 @@ Additionally, verify and update the RANDAO reveal. This is done as follows: ### Process penalties, logouts and other special objects +Verify that there are at most `MAX_SPECIALS_PER_BLOCK` objects in `block.specials`. + For each `SpecialRecord` `obj` in `block.specials`, verify that its `kind` is one of the below values, and that `obj.data` deserializes according to the format for the given `kind`, then process it. The word "verify" when used below means that if the given verification check fails, the block containing that `SpecialRecord` is invalid. #### LOGOUT From 57c2deefbbcc0485b239112f04b90d61fe0cd9dc Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 16 Nov 2018 11:41:59 -0500 Subject: [PATCH 09/21] Added description of genesis block --- specs/core/0_beacon-chain.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0223886a0..65155190c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -562,7 +562,21 @@ If the user wishes to deposit more than 32 ETH, they would need to make multiple ### On startup -Run the following code: +A valid block with slot `0` (the "genesis block") has the following values. Other validity rules (eg. requiring a signature) do not apply. + +```python +{ + 'slot': 0, + 'randao_reveal': bytes32(0), + 'candidate_pow_receipt_root': bytes32(0), + 'ancestor_hashes': [bytes32(0) for i in range(32)], + 'state_root': STARTUP_STATE_ROOT, + 'attestations': [], + 'specials': [] +} +``` + +`STARTUP_STATE_ROOT` is the root of the initial state, computed by running the following code: ```python def on_startup(initial_validator_entries: List[Any], genesis_time: uint64, pow_hash_chain_tip: Hash32) -> BeaconState: @@ -808,7 +822,7 @@ For each `SpecialRecord` `obj` in `state.pending_specials`: * **[covers `LOGOUT`]**: If `obj.kind == LOGOUT`, interpret `data[0]` as a `uint24` and `data[1]` as a `hash32`, where `validator_index = data[0]` and `signature = data[1]`. If `BLSVerify(pubkey=validators[validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=signature)`, where `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`, and `validators[validator_index].status == ACTIVE`, run `exit_validator(validator_index, state, penalize=False, current_slot=block.slot)` * **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, state, penalize=True, current_slot=block.slot)` * **[covers `RANDAO_CHANGE`]**: If `obj.kind == RANDAO_CHANGE`, interpret `data[0]` as a `uint24`, `data[1]` as a `hash32`, and `data[2]` as a `uint64`, where `proposer_index = data[0]`, `randao_commitment = data[1]`, and `randao_last_change = data[2]`. Set `validators[proposer_index].randao_commitment = randao_commitment`, and set `validators[proposer_index].randao_last_change = randao_last_change`. -* **[covers `DEPOSIT_PROOF`]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the `int128` index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. +* **[covers `DEPOSIT_PROOF`]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the `int128` index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify the Merkle branch. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. #### Finally... From eb24f9482de7997efe63d90f881d2bdce5ea21e3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 16 Nov 2018 12:20:59 -0500 Subject: [PATCH 10/21] int128 -> int64 --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 65155190c..b30c41d66 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -822,7 +822,7 @@ For each `SpecialRecord` `obj` in `state.pending_specials`: * **[covers `LOGOUT`]**: If `obj.kind == LOGOUT`, interpret `data[0]` as a `uint24` and `data[1]` as a `hash32`, where `validator_index = data[0]` and `signature = data[1]`. If `BLSVerify(pubkey=validators[validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=signature)`, where `fork_version = pre_fork_version if slot < fork_slot_number else post_fork_version`, and `validators[validator_index].status == ACTIVE`, run `exit_validator(validator_index, state, penalize=False, current_slot=block.slot)` * **[covers `NO_DBL_VOTE`, `NO_SURROUND`, `NO_DBL_PROPOSE` slashing conditions]:** If `obj.kind == CASPER_SLASHING`, interpret `data[0]` as a list of concatenated `uint32` values where each value represents an index into `validators`, `data[1]` as the data being signed and `data[2]` as an aggregate signature. Interpret `data[3:6]` similarly. Verify that both signatures are valid, that the two signatures are signing distinct data, and that they are either signing the same slot number, or that one surrounds the other (ie. `source1 < source2 < target2 < target1`). Let `indices` be the list of indices in both signatures; verify that its length is at least 1. For each validator index `v` in `indices`, if its `status` does not equal `PENALIZED`, then run `exit_validator(v, state, penalize=True, current_slot=block.slot)` * **[covers `RANDAO_CHANGE`]**: If `obj.kind == RANDAO_CHANGE`, interpret `data[0]` as a `uint24`, `data[1]` as a `hash32`, and `data[2]` as a `uint64`, where `proposer_index = data[0]`, `randao_commitment = data[1]`, and `randao_last_change = data[2]`. Set `validators[proposer_index].randao_commitment = randao_commitment`, and set `validators[proposer_index].randao_last_change = randao_last_change`. -* **[covers `DEPOSIT_PROOF`]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the `int128` index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify the Merkle branch. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. +* **[covers `DEPOSIT_PROOF`]**: If `obj.kind == DEPOSIT_PROOF`, interpret `data[0]` as a Merkle branch from a deposit object to the `known_pow_receipt_root` (hashes in order lower to higher, concatenated), `data[1]` as being the `int64` index in the tree, and `data[2]` as being the deposit object itself, having the format `concat(data, as_bytes32(msg.value), as_bytes32(timestamp)` where `data` itself is a `DepositParams`. Verify the Merkle branch. Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. Run `add_validator(validators, data.pubkey, data.proof_of_possession, data.withdrawal_shard, data.withdrawal_address, data.randao_commitment, PENDING_ACTIVATION, block.slot)`. #### Finally... From 1664414852142f4d869588b0f5a4e67295519889 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 16 Nov 2018 19:41:09 -0500 Subject: [PATCH 11/21] Fixed as per hww's comments --- specs/core/0_beacon-chain.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 67db53b4d..83701888b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -612,7 +612,6 @@ def on_startup(initial_validator_entries: List[Any]) -> BeaconState: post_fork_version=INITIAL_FORK_VERSION, fork_slot_number=0, pending_attestations=[], - pending_specials=[], recent_block_hashes=[bytes([0] * 32) for _ in range(CYCLE_LENGTH * 2)], randao_mix=bytes([0] * 32) # stub ) @@ -674,7 +673,8 @@ Additionally, verify and update the RANDAO reveal. This is done as follows: * Let `repeat_hash(x, n) = x if n == 0 else repeat_hash(hash(x), n-1)`. * Let `V = state.validators[curblock_proposer_index]`. -* Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_change) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment`, and set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)` and set `V.randao_commitment = block.randao_reveal`, and set `V.randao_last_change = block.slot`. +* Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_change) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment` +* Set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)`, `V.randao_commitment = block.randao_reveal`, `V.randao_last_change = block.slot` ### Process penalties, logouts and other special objects @@ -697,21 +697,21 @@ Verify that `BLSVerify(pubkey=validators[data.validator_index].pubkey, msg=hash( ```python { - 'vote1_aggsig_indices': '[uint24]', + 'vote1_aggregate_sig_indices': '[uint24]', 'vote1_data': AttestationSignedData, - 'vote1_aggsig': '[uint256]', - 'vote2_aggsig_indices': '[uint24]', + 'vote1_aggregate_sig': '[uint256]', + 'vote1_aggregate_sig_indices': '[uint24]', 'vote2_data': AttestationSignedData, - 'vote2_aggsig': '[uint256]', + 'vote1_aggregate_sig': '[uint256]', } ``` -Verify that: +Perform the following checks: -* For each `aggsig`, `BLSVerify(pubkey=aggregate_pubkey([validators[i].pubkey for i in aggsig_indices]), msg=vote_data, sig=aggsig)` passes. -* `vote1_data != vote2_data` -* Let `intersection = [x for x in vote1_aggsig_indices if x in vote2_aggsig_indices]`. Verify that `len(intersection) >= 1`. -* `vote1_data.justified_slot < vote2_data.justified_slot < vote2_data.slot <= vote1_data.slot` +* For each `aggregate_sig`, verify that `BLSVerify(pubkey=aggregate_pubkey([validators[i].pubkey for i in aggregate_sig_indices]), msg=vote_data, sig=aggsig)` passes. +* Verify that `vote1_data != vote2_data`. +* Let `intersection = [x for x in vote1_aggregate_sig_indices if x in vote2_aggregate_sig_indices]`. Verify that `len(intersection) >= 1`. +* Verify that `vote1_data.justified_slot < vote2_data.justified_slot < vote2_data.slot <= vote1_data.slot`. For each validator index `v` in `intersection`, if `state.validators[v].status` does not equal `PENALIZED`, then run `exit_validator(v, state, penalize=True, current_slot=block.slot)` From 06cf704e839b3a9b0fe35a96e938671cc1a95d22 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 17 Nov 2018 13:52:49 +0900 Subject: [PATCH 12/21] fix typo in slashing special --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 83701888b..d89424fb8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -700,9 +700,9 @@ Verify that `BLSVerify(pubkey=validators[data.validator_index].pubkey, msg=hash( 'vote1_aggregate_sig_indices': '[uint24]', 'vote1_data': AttestationSignedData, 'vote1_aggregate_sig': '[uint256]', - 'vote1_aggregate_sig_indices': '[uint24]', + 'vote2_aggregate_sig_indices': '[uint24]', 'vote2_data': AttestationSignedData, - 'vote1_aggregate_sig': '[uint256]', + 'vote2_aggregate_sig': '[uint256]', } ``` From 10c375244b56dd17c9e80f2882be23d6ee62350e Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 17 Nov 2018 07:51:25 -0500 Subject: [PATCH 13/21] Cleaned up LOGOUT --- specs/core/0_beacon-chain.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d89424fb8..2debc291b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -690,8 +690,12 @@ For each `SpecialRecord` `obj` in `block.specials`, verify that its `kind` is on 'signature': '[uint256]' } ``` +Perform the following checks: -Verify that `BLSVerify(pubkey=validators[data.validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=data.signature)`, where `fork_version = pre_fork_version if block.slot < fork_slot_number else post_fork_version`, and `validators[validator_index].status == ACTIVE`. Run `exit_validator(data.validator_index, state, penalize=False, current_slot=block.slot)`. +* Let `fork_version = pre_fork_version if block.slot < fork_slot_number else post_fork_version`. Verify that `BLSVerify(pubkey=validators[data.validator_index].pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(fork_version)), sig=data.signature)` +* Verify that `validators[validator_index].status == ACTIVE`. + +Run `exit_validator(data.validator_index, state, penalize=False, current_slot=block.slot)`. #### CASPER_SLASHING From 5dc66c328c9b0d361dbde8052f2f682c77c4fe20 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 18 Nov 2018 07:06:46 -0500 Subject: [PATCH 14/21] oops --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 104b44398..6099ac966 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -51,7 +51,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | `MAX_VALIDATOR_CHURN_QUOTIENT` | 2**5 (= 32) | — | | `POW_HASH_VOTING_PERIOD` | 2**10 (=1024) | - | | `POW_CONTRACT_MERKLE_TREE_DEPTH` | 2**5 (=32) | - | - `MAX_SPECIALS_PER_BLOCK` | 2**4 (= 16) | - | +| `MAX_SPECIALS_PER_BLOCK` | 2**4 (= 16) | - | | `LOGOUT_MESSAGE` | `"LOGOUT"` | — | | `INITIAL_FORK_VERSION` | 0 | — | From d889eec555d32f5749c87ee53b0f89dbbb3bba05 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 18 Nov 2018 07:09:50 -0500 Subject: [PATCH 15/21] Oops, accidental duplication --- specs/core/0_beacon-chain.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6099ac966..4c4245b8c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -946,15 +946,6 @@ For every shard number `shard` for which a crosslink committee exists in the cyc * Participating validators gain `B // reward_quotient * (2 * total_balance_of_v_participating - total_balance_of_v) // total_balance_of_v`. * Non-participating validators lose `B // reward_quotient`. -#### Finally... - -* For any validator with index `v` with balance less than `MIN_ONLINE_DEPOSIT_SIZE` and status `ACTIVE`, run `exit_validator(v, crystallized_state, penalize=False, current_slot=block.slot)` -* Set `crystallized_state.last_state_recalculation_slot += CYCLE_LENGTH` -* Remove all attestation records older than slot `crystallized_state.last_state_recalculation_slot` -* Empty the `active_state.pending_specials` list -* Set `active_state.recent_block_hashes = active_state.recent_block_hashes[CYCLE_LENGTH:]` -* Set `shard_and_committee_for_slots[:CYCLE_LENGTH] = shard_and_committee_for_slots[CYCLE_LENGTH:]` - #### PoW chain related rules If `last_state_recalculation_slot % POW_HASH_VOTING_PERIOD == 0`, then: From 3bdd56b0f86f52bdec11e63beef34b50272baeed Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 18 Nov 2018 21:02:26 -0500 Subject: [PATCH 16/21] Fix bugs in #146 (#147) * Fix bugs in #146 * Update 0_beacon-chain.md * cleanup deposit params --- specs/core/0_beacon-chain.md | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4c4245b8c..a804f0099 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -212,7 +212,7 @@ The `BeaconState` has the following fields: # Hash chain of validator set changes (for light clients to easily track deltas) 'validator_set_delta_hash_chain': 'hash32' # Genesis time - 'genesis_time': 'hash32', + 'genesis_time': 'uint64', # PoW chain reference 'known_pow_receipt_root': 'hash32', 'candidate_pow_receipt_root': 'hash32', @@ -517,24 +517,29 @@ def int_sqrt(n: int) -> int: The beacon chain is initialized when a condition is met inside a contract on the existing PoW chain. This contract's code in Vyper is as follows: ```python -HashChainValue: event({prev_tip: bytes32, data: bytes[2048], value: wei_value, time: timestamp, total_deposit_count: int128}) -ChainStart: event({hash_chain_tip: bytes32, time: timestamp}) +HashChainValue: event({prev_tip: bytes32, data: bytes[2064], total_deposit_count: int128}) +ChainStart: event({hash_chain_tip: bytes32, time: bytes[8]}) receipt_tree: bytes32[int128] total_deposit_count: int128 @payable @public -def deposit(data: bytes[2048]): - log.HashChainValue(self.receipt_tree[1], data, msg.value, block.timestamp, self.total_deposit_count) +def deposit(deposit_params: bytes[2048]): index:int128 = self.total_deposit_count + 2**POW_CONTRACT_MERKLE_TREE_DEPTH - self.receipt_tree[index] = sha3(concat(data, as_bytes32(msg.value), as_bytes32(block.timestamp)) + msg_gwei_bytes8: bytes[8] = slice(as_bytes32(msg.value / 10**9), 24, 8) + timestamp_bytes8: bytes[8] = slice(s_bytes32(block.timestamp), 24, 8) + deposit_data: bytes[2064] = concat(deposit_params, msg_gwei_bytes8, timestamp_bytes8) + + log.HashChainValue(self.receipt_tree[1], deposit_data, self.total_deposit_count) + + self.receipt_tree[index] = sha3(deposit_data) for i in range(POW_CONTRACT_MERKLE_TREE_DEPTH): index //= 2 self.receipt_tree[index] = sha3(concat(self.receipt_tree[index * 2], self.receipt_tree[index * 2 + 1])) self.total_deposit_count += 1 if self.total_deposit_count == 16384: - log.ChainStart(self.receipt_tree[1], block.timestamp) + log.ChainStart(self.receipt_tree[1], timestamp_bytes8) @public @constant @@ -542,7 +547,7 @@ def get_receipt_root() -> bytes32: return self.receipt_tree[1] ``` -The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. When a user wishes to become a validator by moving 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 `DepositParams` object of the form: +The contract is at address `DEPOSIT_CONTRACT_ADDRESS`. When a user wishes to become a validator by moving 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 `deposit_params` a SimpleSerialize'd `DepositParams` object of the form: ```python { @@ -865,14 +870,14 @@ For each validator index `v` in `intersection`, if `state.validators[v].status` 'merkle_branch': '[hash32]', 'merkle_tree_index': 'uint64', 'deposit_data': { - 'params': DepositParams, - 'msg_value': 'uint256', - 'timestamp': 'uint256' + 'deposit_params': DepositParams, + 'msg_value': 'uint64', + 'timestamp': 'uint64' } } ``` -Note that `deposit_data` in serialized form should be the `DepositParams` followed by 32 bytes for the `msg.value` and 32 bytes for the `timestamp`, or exactly the data the hash of which was placed into the Merkle tree in the PoW contract. +Note that `deposit_data` in serialized form should be the `DepositParams` followed by 8 bytes for the `msg_value` and 8 bytes for the `timestamp`, or exactly the `deposit_data` in the PoW contract of which the hash was placed into the Merkle tree. Use the following procedure to verify the `merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=POW_CONTRACT_MERKLE_TREE_DEPTH` and `root=state.known_pow_receipt_root`: @@ -887,9 +892,9 @@ def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, return value == root ``` -Verify that `msg.value == DEPOSIT_SIZE` and `block.slot - (timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. +Verify that `deposit_data.msg_value == DEPOSIT_SIZE` and `block.slot - (deposit_data.timestamp - state.genesis_time) // SLOT_DURATION < DELETION_PERIOD`. -Run `add_validator(validators, deposit_data.params.pubkey, deposit_data.params.proof_of_possession, deposit_data.params.withdrawal_shard, data.withdrawal_address, deposit_data.params.randao_commitment, PENDING_ACTIVATION, block.slot)`. +Run `add_validator(validators, deposit_data.deposit_params.pubkey, deposit_data.deposit_params.proof_of_possession, deposit_data.deposit_params.withdrawal_shard, data.deposit_params.withdrawal_address, deposit_data.deposit_params.randao_commitment, PENDING_ACTIVATION, block.slot)`. ## State recalculations (every `CYCLE_LENGTH` slots) From c961b3dca87d2eea6fe1ce5568c0fd65348f6271 Mon Sep 17 00:00:00 2001 From: Ben Edgington Date: Mon, 19 Nov 2018 02:05:53 +0000 Subject: [PATCH 17/21] Add ToDo for state_root specification (#137) --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a804f0099..43a46dbc1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1096,6 +1096,7 @@ Note: This spec is ~65% complete. * [ ] Specify the logic for proofs of custody, including slashing conditions * [ ] Specify BLSVerify and rework the spec for BLS12-381 throughout * [ ] Specify the constraints for `SpecialRecord`s ([issue 43](https://github.com/ethereum/eth2.0-specs/issues/43)) +* [ ] Specify the calculation and validation of `BeaconBlock.state_root` * [ ] Undergo peer review, security audits and formal verification **Documentation** From 415ac7a3d7928f0186686bd16a41b1165f87a8a0 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 19 Nov 2018 11:51:23 +0900 Subject: [PATCH 18/21] Clean up leftover of CrystallizedState --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 43a46dbc1..6bbfd2142 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -818,7 +818,7 @@ Additionally, verify and update the RANDAO reveal. This is done as follows: * Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_change) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment` * Set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)`, `V.randao_commitment = block.randao_reveal`, `V.randao_last_change = block.slot` -Finally, if `block.candidate_pow_hash_chain_tip = crystallized_state.candidate_pow_hash_chain_tip`, set `crystallized_state.candidate_hash_chain_tip_votes += 1`. +Finally, if `block.candidate_pow_hash_chain_tip = state.candidate_pow_hash_chain_tip`, set `state.candidate_hash_chain_tip_votes += 1`. ### Process penalties, logouts and other special objects @@ -955,9 +955,9 @@ For every shard number `shard` for which a crosslink committee exists in the cyc If `last_state_recalculation_slot % POW_HASH_VOTING_PERIOD == 0`, then: -* If `crystallized_state.candidate_hash_chain_tip_votes * 3 >= POW_HASH_VOTING_PERIOD * 2`, set `crystallized_state.hash_chain_tip = crystallized_state.candidate_hash_chain_tip` -* Set `crystallized_state.candidate_hash_chain_tip = block.candidate_pow_hash_chain_tip` -* Set `crystallized_state.candidate_hash_chain_tip_votes = 0` +* If `state.candidate_hash_chain_tip_votes * 3 >= POW_HASH_VOTING_PERIOD * 2`, set `state.hash_chain_tip = state.candidate_hash_chain_tip` +* Set `state.candidate_hash_chain_tip = block.candidate_pow_hash_chain_tip` +* Set `state.candidate_hash_chain_tip_votes = 0` ### Validator set change From 2a077fbeccffc4819e303e99aabcfb8ae5bd71bf Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 19 Nov 2018 12:16:37 +0900 Subject: [PATCH 19/21] init current_exit_seq in on_startup --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index fb7acbcdb..03a677e7f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -628,6 +628,7 @@ def on_startup(initial_validator_entries: List[Any], genesis_time: uint64, pow_h deposits_penalized_in_period=[], next_shuffling_seed=b'\x00'*32, validator_set_delta_hash_chain=bytes([0] * 32), # stub + current_exit_seq=0, genesis_time=genesis_time, known_pow_hash_chain_tip=pow_hash_chain_tip, processed_pow_hash_chain_tip=pow_hash_chain_tip, From c3e2c2a46d75e7dcc1ee86f69b72226888161377 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 19 Nov 2018 11:51:23 +0900 Subject: [PATCH 20/21] Clean up leftover of CrystallizedState --- specs/core/0_beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 03a677e7f..6b6ec433f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -828,7 +828,7 @@ Additionally, verify and update the RANDAO reveal. This is done as follows: * Verify that `repeat_hash(block.randao_reveal, (block.slot - V.randao_last_change) // RANDAO_SLOTS_PER_LAYER + 1) == V.randao_commitment` * Set `state.randao_mix = xor(state.randao_mix, block.randao_reveal)`, `V.randao_commitment = block.randao_reveal`, `V.randao_last_change = block.slot` -Finally, if `block.candidate_pow_hash_chain_tip = crystallized_state.candidate_pow_hash_chain_tip`, set `crystallized_state.candidate_hash_chain_tip_votes += 1`. +Finally, if `block.candidate_pow_hash_chain_tip = state.candidate_pow_hash_chain_tip`, set `state.candidate_hash_chain_tip_votes += 1`. ### Process penalties, logouts and other special objects @@ -965,9 +965,9 @@ For every shard number `shard` for which a crosslink committee exists in the cyc If `last_state_recalculation_slot % POW_HASH_VOTING_PERIOD == 0`, then: -* If `crystallized_state.candidate_hash_chain_tip_votes * 3 >= POW_HASH_VOTING_PERIOD * 2`, set `crystallized_state.hash_chain_tip = crystallized_state.candidate_hash_chain_tip` -* Set `crystallized_state.candidate_hash_chain_tip = block.candidate_pow_hash_chain_tip` -* Set `crystallized_state.candidate_hash_chain_tip_votes = 0` +* If `state.candidate_hash_chain_tip_votes * 3 >= POW_HASH_VOTING_PERIOD * 2`, set `state.hash_chain_tip = state.candidate_hash_chain_tip` +* Set `state.candidate_hash_chain_tip = block.candidate_pow_hash_chain_tip` +* Set `state.candidate_hash_chain_tip_votes = 0` ### Validator set change From 54eeb498311024b9fc2254ee38fe39265ea1d032 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 19 Nov 2018 12:48:12 +0900 Subject: [PATCH 21/21] crystallized_state -> state --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6b6ec433f..9a15eb5bb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -707,8 +707,8 @@ def add_validator(validators: List[ValidatorRecord], def exit_validator(index, state, penalize, current_slot): validator = state.validators[index] validator.exit_slot = current_slot - validator.exit_seq = crystallized_state.current_exit_seq - crystallized_state.current_exit_seq += 1 + validator.exit_seq = state.current_exit_seq + state.current_exit_seq += 1 if penalize: validator.status = PENALIZED state.deposits_penalized_in_period[current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += validator.balance