2019-03-28 22:54:39 +00:00
# Ethereum 2.0 Phase 1 -- Custody Game
2019-05-06 15:30:32 +00:00
**Notice**: This document is a work-in-progress for researchers and implementers.
2019-03-28 22:54:39 +00:00
## Table of contents
2019-12-10 14:17:06 +00:00
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE - RUN doctoc TO UPDATE -->
2019-03-28 22:54:39 +00:00
2019-12-10 14:17:06 +00:00
- [Introduction ](#introduction )
- [Constants ](#constants )
- [Misc ](#misc )
- [Time parameters ](#time-parameters )
- [Max operations per block ](#max-operations-per-block )
- [Reward and penalty quotients ](#reward-and-penalty-quotients )
- [Signature domain types ](#signature-domain-types )
- [Data structures ](#data-structures )
2020-01-03 14:49:23 +00:00
- [New Beacon Chain operations ](#new-beacon-chain-operations )
- [`CustodySlashing` ](#custodyslashing )
- [`SignedCustodySlashing` ](#signedcustodyslashing )
2019-12-10 14:17:06 +00:00
- [`CustodyKeyReveal` ](#custodykeyreveal )
- [`EarlyDerivedSecretReveal` ](#earlyderivedsecretreveal )
- [Helpers ](#helpers )
- [`legendre_bit` ](#legendre_bit )
2020-01-03 14:49:23 +00:00
- [`custody_atoms` ](#custody_atoms )
- [`compute_custody_bit` ](#compute_custody_bit )
2019-12-10 14:17:06 +00:00
- [`get_randao_epoch_for_custody_period` ](#get_randao_epoch_for_custody_period )
- [`get_custody_period_for_validator` ](#get_custody_period_for_validator )
- [Per-block processing ](#per-block-processing )
2020-01-03 14:49:23 +00:00
- [Custody Game Operations ](#custody-game-operations )
2019-12-10 14:17:06 +00:00
- [Custody key reveals ](#custody-key-reveals )
- [Early derived secret reveals ](#early-derived-secret-reveals )
2020-01-03 14:49:23 +00:00
- [Custody Slashings ](#custody-slashings )
2019-12-10 14:17:06 +00:00
- [Per-epoch processing ](#per-epoch-processing )
2020-01-03 14:49:23 +00:00
- [Handling of reveal deadlines ](#handling-of-reveal-deadlines )
- [Final updates ](#final-updates )
2019-12-10 14:17:06 +00:00
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2019-03-28 22:54:39 +00:00
## Introduction
2019-05-06 15:30:32 +00:00
This document details the beacon chain additions and changes in Phase 1 of Ethereum 2.0 to support the shard data custody game, building upon the [Phase 0 ](0_beacon-chain.md ) specification.
2019-03-28 22:54:39 +00:00
## Constants
### Misc
2019-11-20 03:15:15 +00:00
| Name | Value | Unit |
2019-09-15 21:12:36 +00:00
| - | - |
2019-08-11 17:05:17 +00:00
| `BLS12_381_Q` | `4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787` |
| `MINOR_REWARD_QUOTIENT` | `2**8` (= 256) |
2019-11-20 03:15:15 +00:00
| `BYTES_PER_CUSTODY_ATOM` | `48` | bytes |
2019-03-28 22:54:39 +00:00
### Time parameters
| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
2019-05-03 09:30:55 +00:00
| `RANDAO_PENALTY_EPOCHS` | `2**1` (= 2) | epochs | 12.8 minutes |
| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `2**14` | epochs | ~73 days |
| `EPOCHS_PER_CUSTODY_PERIOD` | `2**11` (= 2,048) | epochs | ~9 days |
| `CUSTODY_PERIOD_TO_RANDAO_PADDING` | `2**11` (= 2,048) | epochs | ~9 days |
| `MAX_REVEAL_LATENESS_DECREMENT` | `2**7` (= 128) | epochs | ~14 hours |
2019-03-28 22:54:39 +00:00
2019-03-31 14:54:46 +00:00
### Max operations per block
2019-03-28 22:54:39 +00:00
| Name | Value |
| - | - |
| `MAX_CUSTODY_KEY_REVEALS` | `2**4` (= 16) |
2019-05-03 09:30:55 +00:00
| `MAX_EARLY_DERIVED_SECRET_REVEALS` | `1` |
2019-11-20 03:15:15 +00:00
| `MAX_CUSTODY_SLASHINGS` | `1` |
2019-03-28 22:54:39 +00:00
2019-05-03 09:30:55 +00:00
### Reward and penalty quotients
2019-05-12 20:46:17 +00:00
| Name | Value |
| - | - |
2019-05-03 09:30:55 +00:00
| `EARLY_DERIVED_SECRET_REVEAL_SLOT_REWARD_MULTIPLE` | `2**1` (= 2) |
2019-06-30 20:12:02 +00:00
### Signature domain types
The following types are defined, mapping into `DomainType` (little endian):
2019-03-28 22:54:39 +00:00
| Name | Value |
| - | - |
2020-01-03 14:49:23 +00:00
| `DOMAIN_CUSTODY_BIT_SLASHING` | `DomainType('0x83000000')` |
2019-06-19 00:14:13 +00:00
2019-03-28 22:54:39 +00:00
## Data structures
2019-11-20 03:15:15 +00:00
### New Beacon Chain operations
2019-03-28 22:54:39 +00:00
2019-11-20 03:15:15 +00:00
#### `CustodySlashing`
2019-03-28 22:54:39 +00:00
```python
2019-11-20 03:15:15 +00:00
class CustodySlashing(Container):
2020-01-09 23:00:10 +00:00
# Attestation.custody_bits_blocks[data_index][committee.index(malefactor_index)] is the target custody bit to check.
2019-11-20 03:15:15 +00:00
# (Attestation.data.shard_transition_root as ShardTransition).shard_data_roots[data_index] is the root of the data.
data_index: uint64
malefactor_index: ValidatorIndex
2020-01-03 14:29:23 +00:00
malefactor_secret: BLSSignature
2019-11-20 03:15:15 +00:00
whistleblower_index: ValidatorIndex
shard_transition: ShardTransition
2019-06-05 13:29:26 +00:00
attestation: Attestation
2019-12-05 19:36:48 +00:00
data: ByteList[MAX_SHARD_BLOCK_CHUNKS * SHARD_BLOCK_CHUNK_SIZE]
2019-12-05 20:49:52 +00:00
```
#### `SignedCustodySlashing`
```python
class SignedCustodySlashing(Container):
message: CustodySlashing
2019-06-15 22:25:37 +00:00
signature: BLSSignature
2019-03-28 22:54:39 +00:00
```
2019-12-05 20:49:52 +00:00
2019-03-28 22:54:39 +00:00
#### `CustodyKeyReveal`
```python
2019-06-05 13:29:26 +00:00
class CustodyKeyReveal(Container):
2019-05-03 09:30:55 +00:00
# Index of the validator whose key is being revealed
2019-06-13 00:08:19 +00:00
revealer_index: ValidatorIndex
2019-05-03 09:30:55 +00:00
# Reveal (masked signature)
2019-06-15 22:25:37 +00:00
reveal: BLSSignature
2019-05-03 09:30:55 +00:00
```
#### `EarlyDerivedSecretReveal`
Represents an early (punishable) reveal of one of the derived secrets, where derived secrets are RANDAO reveals and custody reveals (both are part of the same domain).
```python
2019-06-05 13:29:26 +00:00
class EarlyDerivedSecretReveal(Container):
2019-05-03 09:30:55 +00:00
# Index of the validator whose key is being revealed
2019-06-15 22:25:37 +00:00
revealed_index: ValidatorIndex
2019-05-03 09:30:55 +00:00
# RANDAO epoch of the key that is being revealed
2019-06-13 00:08:19 +00:00
epoch: Epoch
2019-05-03 09:30:55 +00:00
# Reveal (masked signature)
2019-06-15 22:25:37 +00:00
reveal: BLSSignature
2019-05-03 09:30:55 +00:00
# Index of the validator who revealed (whistleblower)
2019-06-15 22:25:37 +00:00
masker_index: ValidatorIndex
2019-05-03 09:30:55 +00:00
# Mask used to hide the actual reveal signature (prevent reveal from being stolen)
2019-11-12 20:29:58 +00:00
mask: Bytes32
2019-03-28 22:54:39 +00:00
```
## Helpers
2019-08-11 17:05:17 +00:00
### `legendre_bit`
Returns the Legendre symbol `(a/q)` normalizes as a bit (i.e. `((a/q) + 1) // 2` ). In a production implementation, a well-optimized library (e.g. GMP) should be used for this.
2019-06-27 11:41:22 +00:00
```python
2019-08-11 17:05:17 +00:00
def legendre_bit(a: int, q: int) -> int:
if a >= q:
return legendre_bit(a % q, q)
if a == 0:
2019-09-23 18:07:10 +00:00
return 0
2019-08-11 17:05:17 +00:00
assert(q > a > 0 and q % 2 == 1)
t = 1
n = q
while a != 0:
while a % 2 == 0:
a //= 2
r = n % 8
if r == 3 or r == 5:
t = -t
a, n = n, a
if a % 4 == n % 4 == 3:
t = -t
a %= n
if n == 1:
return (t + 1) // 2
else:
return 0
```
2019-11-20 03:15:15 +00:00
### `custody_atoms`
2019-08-11 17:05:17 +00:00
2019-11-20 03:15:15 +00:00
Given one set of data, return the custody atoms: each atom will be combined with one legendre bit.
2019-08-11 17:05:17 +00:00
```python
2019-11-20 03:15:15 +00:00
def get_custody_atoms(bytez: bytes) -> Sequence[bytes]:
bytez += b'\x00' * (-len(bytez) % BYTES_PER_CUSTODY_ATOM) # right-padding
return [bytez[i:i + BYTES_PER_CUSTODY_ATOM]
for i in range(0, len(bytez), BYTES_PER_CUSTODY_ATOM)]
2019-06-27 11:41:22 +00:00
```
2019-11-20 03:15:15 +00:00
### `compute_custody_bit`
2019-03-28 22:54:39 +00:00
```python
2019-11-20 03:43:32 +00:00
def compute_custody_bit(key: BLSSignature, data: bytes) -> bit:
2019-12-20 06:41:46 +00:00
full_G2_element = bls.signature_to_G2(key)
2019-08-11 17:05:17 +00:00
s = full_G2_element[0].coeffs
2019-11-20 03:15:15 +00:00
bits = [legendre_bit((i + 1) * s[i % 2] + int.from_bytes(atom, "little"), BLS12_381_Q)
for i, atom in enumerate(get_custody_atoms(data))]
# XOR all atom bits
2019-11-20 03:43:32 +00:00
return bit(sum(bits) % 2)
2019-03-28 22:54:39 +00:00
```
2019-05-03 09:30:55 +00:00
### `get_randao_epoch_for_custody_period`
2019-03-28 22:54:39 +00:00
```python
2019-06-30 18:00:22 +00:00
def get_randao_epoch_for_custody_period(period: uint64, validator_index: ValidatorIndex) -> Epoch:
2019-05-03 09:30:55 +00:00
next_period_start = (period + 1) * EPOCHS_PER_CUSTODY_PERIOD - validator_index % EPOCHS_PER_CUSTODY_PERIOD
2019-06-13 00:08:19 +00:00
return Epoch(next_period_start + CUSTODY_PERIOD_TO_RANDAO_PADDING)
2019-05-03 09:30:55 +00:00
```
2019-08-11 17:05:17 +00:00
### `get_custody_period_for_validator`
2019-05-03 09:30:55 +00:00
2019-05-07 11:13:22 +00:00
```python
2019-11-18 22:07:50 +00:00
def get_custody_period_for_validator(validator_index: ValidatorIndex, epoch: Epoch) -> int:
2019-05-03 09:30:55 +00:00
'''
2019-06-28 15:19:59 +00:00
Return the reveal period for a given validator.
2019-05-03 09:30:55 +00:00
'''
return (epoch + validator_index % EPOCHS_PER_CUSTODY_PERIOD) // EPOCHS_PER_CUSTODY_PERIOD
2019-03-28 22:54:39 +00:00
```
2019-04-01 02:20:43 +00:00
2019-03-28 22:54:39 +00:00
## Per-block processing
2019-11-15 22:42:28 +00:00
### Custody Game Operations
2019-03-28 22:54:39 +00:00
2019-11-15 22:42:28 +00:00
```python
def process_custody_game_operations(state: BeaconState, body: BeaconBlockBody) -> None:
2019-11-20 03:43:32 +00:00
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
2019-11-15 22:42:28 +00:00
for operation in operations:
fn(state, operation)
2019-11-16 10:13:47 +00:00
for_ops(body.custody_key_reveals, process_custody_key_reveal)
for_ops(body.early_derived_secret_reveals, process_early_derived_secret_reveal)
2019-11-20 03:15:15 +00:00
for_ops(body.custody_slashings, process_custody_slashing)
2019-11-15 22:42:28 +00:00
```
2019-03-28 22:54:39 +00:00
2019-05-03 09:30:55 +00:00
#### Custody key reveals
2019-03-28 22:54:39 +00:00
```python
2019-06-28 15:19:59 +00:00
def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> None:
2019-05-03 09:30:55 +00:00
"""
Process ``CustodyKeyReveal`` operation.
Note that this function mutates ``state``.
"""
2019-06-09 19:41:21 +00:00
revealer = state.validators[reveal.revealer_index]
2019-08-11 17:05:17 +00:00
epoch_to_sign = get_randao_epoch_for_custody_period(revealer.next_custody_secret_to_reveal, reveal.revealer_index)
2019-05-03 09:30:55 +00:00
2019-11-18 22:07:50 +00:00
custody_reveal_period = get_custody_period_for_validator(reveal.revealer_index, get_current_epoch(state))
assert revealer.next_custody_secret_to_reveal < custody_reveal_period
2019-05-03 09:30:55 +00:00
# Revealed validator is active or exited, but not withdrawn
assert is_slashable_validator(revealer, get_current_epoch(state))
# Verify signature
2019-12-17 13:33:37 +00:00
domain = get_domain(state, DOMAIN_RANDAO, epoch_to_sign)
2020-01-03 07:18:34 +00:00
signing_root = compute_signing_root(epoch_to_sign, domain)
assert bls.Verify(revealer.pubkey, signing_root, reveal.reveal)
2019-05-03 09:30:55 +00:00
# Decrement max reveal lateness if response is timely
2019-08-11 17:05:17 +00:00
if epoch_to_sign + EPOCHS_PER_CUSTODY_PERIOD >= get_current_epoch(state):
if revealer.max_reveal_lateness >= MAX_REVEAL_LATENESS_DECREMENT:
revealer.max_reveal_lateness -= MAX_REVEAL_LATENESS_DECREMENT
else:
revealer.max_reveal_lateness = 0
else:
revealer.max_reveal_lateness = max(
revealer.max_reveal_lateness,
get_current_epoch(state) - epoch_to_sign - EPOCHS_PER_CUSTODY_PERIOD
)
2019-05-03 09:30:55 +00:00
# Process reveal
2019-08-11 17:05:17 +00:00
revealer.next_custody_secret_to_reveal += 1
2019-05-03 09:30:55 +00:00
2019-11-20 03:15:15 +00:00
# Reward Block Proposer
2019-06-30 17:42:24 +00:00
proposer_index = get_beacon_proposer_index(state)
2019-06-13 00:08:19 +00:00
increase_balance(
state,
proposer_index,
Gwei(get_base_reward(state, reveal.revealer_index) // MINOR_REWARD_QUOTIENT)
)
2019-05-03 09:30:55 +00:00
```
2019-05-23 17:34:39 +00:00
#### Early derived secret reveals
2019-05-03 09:30:55 +00:00
```python
2019-06-28 15:19:59 +00:00
def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerivedSecretReveal) -> None:
2019-05-03 09:30:55 +00:00
"""
Process ``EarlyDerivedSecretReveal`` operation.
Note that this function mutates ``state``.
"""
2019-06-09 19:41:21 +00:00
revealed_validator = state.validators[reveal.revealed_index]
2019-05-20 07:47:44 +00:00
derived_secret_location = reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
2019-05-03 09:30:55 +00:00
assert reveal.epoch >= get_current_epoch(state) + RANDAO_PENALTY_EPOCHS
assert reveal.epoch < get_current_epoch ( state ) + EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
2019-06-20 21:02:22 +00:00
assert not revealed_validator.slashed
2019-05-20 07:47:44 +00:00
assert reveal.revealed_index not in state.exposed_derived_secrets[derived_secret_location]
2019-05-03 09:30:55 +00:00
# Verify signature correctness
2019-06-09 19:41:21 +00:00
masker = state.validators[reveal.masker_index]
2019-05-03 09:30:55 +00:00
pubkeys = [revealed_validator.pubkey, masker.pubkey]
2019-12-17 13:33:37 +00:00
domain = get_domain(state, DOMAIN_RANDAO, reveal.epoch)
2020-01-07 12:07:09 +00:00
signing_roots = [compute_signing_root(root, domain) for root in [hash_tree_root(reveal.epoch), reveal.mask]]
2020-01-07 19:52:20 +00:00
assert bls.AggregateVerify(zip(pubkeys, signing_roots), reveal.reveal)
2019-03-28 22:54:39 +00:00
2019-05-03 09:30:55 +00:00
if reveal.epoch >= get_current_epoch(state) + CUSTODY_PERIOD_TO_RANDAO_PADDING:
# Full slashing when the secret was revealed so early it may be a valid custody
# round key
slash_validator(state, reveal.revealed_index, reveal.masker_index)
2019-03-28 22:54:39 +00:00
else:
2019-05-05 11:10:39 +00:00
# Only a small penalty proportional to proposer slot reward for RANDAO reveal
2019-05-03 09:30:55 +00:00
# that does not interfere with the custody period
2019-05-05 11:10:39 +00:00
# The penalty is proportional to the max proposer reward
2019-05-03 09:30:55 +00:00
# Calculate penalty
max_proposer_slot_reward = (
2019-05-20 07:47:44 +00:00
get_base_reward(state, reveal.revealed_index)
* SLOTS_PER_EPOCH
// len(get_active_validator_indices(state, get_current_epoch(state)))
// PROPOSER_REWARD_QUOTIENT
)
2019-06-13 00:08:19 +00:00
penalty = Gwei(
2019-05-20 07:47:44 +00:00
max_proposer_slot_reward
* EARLY_DERIVED_SECRET_REVEAL_SLOT_REWARD_MULTIPLE
* (len(state.exposed_derived_secrets[derived_secret_location]) + 1)
2019-05-03 09:30:55 +00:00
)
# Apply penalty
2019-06-30 17:42:24 +00:00
proposer_index = get_beacon_proposer_index(state)
2019-05-03 09:30:55 +00:00
whistleblower_index = reveal.masker_index
2019-06-28 13:35:26 +00:00
whistleblowing_reward = Gwei(penalty // WHISTLEBLOWER_REWARD_QUOTIENT)
2019-06-13 00:08:19 +00:00
proposer_reward = Gwei(whistleblowing_reward // PROPOSER_REWARD_QUOTIENT)
2019-05-03 09:30:55 +00:00
increase_balance(state, proposer_index, proposer_reward)
increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward)
decrease_balance(state, reveal.revealed_index, penalty)
2019-05-05 11:10:39 +00:00
# Mark this derived secret as exposed so validator cannot be punished repeatedly
2019-05-20 07:47:44 +00:00
state.exposed_derived_secrets[derived_secret_location].append(reveal.revealed_index)
2019-03-28 22:54:39 +00:00
```
2019-11-20 03:15:15 +00:00
#### Custody Slashings
2019-03-28 22:54:39 +00:00
```python
2019-12-05 20:49:52 +00:00
def process_custody_slashing(state: BeaconState, signed_custody_slashing: SignedCustodySlashing) -> None:
custody_slashing = signed_custody_slashing.message
2019-11-20 03:15:15 +00:00
attestation = custody_slashing.attestation
# Any signed custody-slashing should result in at least one slashing.
# If the custody bits are valid, then the claim itself is slashed.
malefactor = state.validators[custody_slashing.malefactor_index]
whistleblower = state.validators[custody_slashing.whistleblower_index]
domain = get_domain(state, DOMAIN_CUSTODY_BIT_SLASHING, get_current_epoch(state))
2019-12-05 20:49:52 +00:00
assert bls_verify(whistleblower.pubkey, hash_tree_root(custody_slashing), signed_custody_slashing.signature, domain)
2019-11-20 03:15:15 +00:00
# Verify that the whistleblower is slashable
assert is_slashable_validator(whistleblower, get_current_epoch(state))
# Verify that the claimed malefactor is slashable
assert is_slashable_validator(malefactor, get_current_epoch(state))
2019-03-28 22:54:39 +00:00
# Verify the attestation
2019-11-20 03:15:15 +00:00
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
2019-04-01 02:20:43 +00:00
2019-11-20 03:15:15 +00:00
# TODO: custody_slashing.data is not chunked like shard blocks yet, result is lots of padding.
2019-03-28 22:54:39 +00:00
2019-11-20 03:15:15 +00:00
# TODO: can do a single combined merkle proof of data being attested.
# Verify the shard transition is indeed attested by the attestation
shard_transition = custody_slashing.shard_transition
assert hash_tree_root(shard_transition) == attestation.shard_transition_root
# Verify that the provided data matches the shard-transition
shard_chunk_roots = shard_transition.shard_data_roots[custody_slashing.data_index]
assert hash_tree_root(custody_slashing.data) == chunks_to_body_root(shard_chunk_roots)
2019-03-28 22:54:39 +00:00
2019-11-20 03:43:32 +00:00
# Verify existence and participation of claimed malefactor
2019-06-28 11:23:22 +00:00
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
2019-11-20 03:15:15 +00:00
assert custody_slashing.malefactor_index in attesters
2019-11-20 03:48:00 +00:00
# Verify the malefactor custody key
epoch_to_sign = get_randao_epoch_for_custody_period(
2019-11-20 04:11:19 +00:00
get_custody_period_for_validator(custody_slashing.malefactor_index, attestation.data.target.epoch),
2019-11-20 03:48:00 +00:00
custody_slashing.malefactor_index,
)
domain = get_domain(state, DOMAIN_RANDAO, epoch_to_sign)
2020-01-09 13:56:06 +00:00
signing_root = compute_signing_root(epoch_to_sign, domain)
assert bls.Verify(malefactor.pubkey, signing_root, custody_slashing.malefactor_secret)
2019-11-20 03:48:00 +00:00
2019-11-20 03:15:15 +00:00
# Get the custody bit
2020-01-09 23:00:10 +00:00
custody_bits = attestation.custody_bits_blocks[custody_slashing.data_index]
2019-11-20 03:43:32 +00:00
committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index)
claimed_custody_bit = custody_bits[committee.index(custody_slashing.malefactor_index)]
2019-11-20 03:15:15 +00:00
# Compute the custody bit
2020-01-03 14:29:23 +00:00
computed_custody_bit = compute_custody_bit(custody_slashing.malefactor_secret, custody_slashing.data)
2019-11-20 03:15:15 +00:00
# Verify the claim
if claimed_custody_bit != computed_custody_bit:
# Slash the malefactor, reward the other committee members
slash_validator(state, custody_slashing.malefactor_index)
2019-11-20 04:11:19 +00:00
others_count = len(committee) - 1
whistleblower_reward = Gwei(malefactor.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT // others_count)
2019-11-20 03:15:15 +00:00
for attester_index in attesters:
if attester_index != custody_slashing.malefactor_index:
increase_balance(state, attester_index, whistleblower_reward)
# No special whisteblower reward: it is expected to be an attester. Others are free to slash too however.
else:
# The claim was false, the custody bit was correct. Slash the whistleblower that induced this work.
slash_validator(state, custody_slashing.whistleblower_index)
2019-03-28 22:54:39 +00:00
```
## Per-epoch processing
2019-11-20 03:15:15 +00:00
### Handling of reveal deadlines
2019-05-03 09:30:55 +00:00
2019-11-15 20:11:42 +00:00
Run `process_reveal_deadlines(state)` after `process_registry_updates(state)` :
2019-05-03 09:30:55 +00:00
2019-05-07 11:13:22 +00:00
```python
2019-05-03 09:30:55 +00:00
def process_reveal_deadlines(state: BeaconState) -> None:
2019-11-20 04:11:19 +00:00
epoch = get_current_epoch(state)
2019-06-09 19:41:21 +00:00
for index, validator in enumerate(state.validators):
2019-11-20 04:11:19 +00:00
if get_custody_period_for_validator(ValidatorIndex(index), epoch) > validator.next_custody_secret_to_reveal:
2019-06-13 00:08:19 +00:00
slash_validator(state, ValidatorIndex(index))
2019-05-03 09:30:55 +00:00
```
2019-11-20 03:15:15 +00:00
### Final updates
2019-11-15 20:11:42 +00:00
After `process_final_updates(state)` , additional updates are made for the custody game:
2019-05-03 09:30:55 +00:00
```python
2019-11-15 20:11:42 +00:00
def process_custody_final_updates(state: BeaconState) -> None:
2019-05-03 09:30:55 +00:00
# Clean up exposed RANDAO key reveals
2019-11-20 03:15:15 +00:00
state.exposed_derived_secrets[get_current_epoch(state) % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS] = []
2019-05-03 09:30:55 +00:00
```