Merge pull request #112 from ethereum/vitalik9
Implementation of alternate withdrawal mechanism (issue #91)
This commit is contained in:
commit
a274432889
|
@ -44,7 +44,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 |
|
| `MIN_VALIDATOR_SET_CHANGE_INTERVAL` | 2**8 (= 256) | slots | ~1.1 hours |
|
||||||
| `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours |
|
| `RANDAO_SLOTS_PER_LAYER` | 2**12 (= 4096) | slots | ~18 hours |
|
||||||
| `SQRT_E_DROP_TIME` | 2**16 (= 65,536) | slots | ~12 days |
|
| `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 |
|
||||||
| `DELETION_PERIOD` | 2**21 (= 2,097,152) | slots | ~1.06 years |
|
| `DELETION_PERIOD` | 2**21 (= 2,097,152) | slots | ~1.06 years |
|
||||||
| `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | 2**16 (= 65,536) | slots | ~12 days |
|
| `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | 2**16 (= 65,536) | slots | ~12 days |
|
||||||
| `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — |
|
| `BASE_REWARD_QUOTIENT` | 2**15 (= 32,768) | — |
|
||||||
|
@ -211,6 +213,8 @@ The `BeaconState` has the following fields:
|
||||||
'deposits_penalized_in_period': ['uint32'],
|
'deposits_penalized_in_period': ['uint32'],
|
||||||
# Hash chain of validator set changes (for light clients to easily track deltas)
|
# Hash chain of validator set changes (for light clients to easily track deltas)
|
||||||
'validator_set_delta_hash_chain': 'hash32'
|
'validator_set_delta_hash_chain': 'hash32'
|
||||||
|
# Current sequence number for withdrawals
|
||||||
|
'current_exit_seq': 'uint64',
|
||||||
# Genesis time
|
# Genesis time
|
||||||
'genesis_time': 'uint64',
|
'genesis_time': 'uint64',
|
||||||
# PoW chain reference
|
# PoW chain reference
|
||||||
|
@ -251,6 +255,8 @@ A `ValidatorRecord` has the following fields:
|
||||||
'status': 'uint8',
|
'status': 'uint8',
|
||||||
# Slot when validator exited (or 0)
|
# Slot when validator exited (or 0)
|
||||||
'exit_slot': 'uint64'
|
'exit_slot': 'uint64'
|
||||||
|
# Sequence number when validator exited (or 0)
|
||||||
|
'exit_seq': 'uint64'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -622,6 +628,7 @@ def on_startup(initial_validator_entries: List[Any], genesis_time: uint64, pow_h
|
||||||
deposits_penalized_in_period=[],
|
deposits_penalized_in_period=[],
|
||||||
next_shuffling_seed=b'\x00'*32,
|
next_shuffling_seed=b'\x00'*32,
|
||||||
validator_set_delta_hash_chain=bytes([0] * 32), # stub
|
validator_set_delta_hash_chain=bytes([0] * 32), # stub
|
||||||
|
current_exit_seq=0,
|
||||||
genesis_time=genesis_time,
|
genesis_time=genesis_time,
|
||||||
known_pow_hash_chain_tip=pow_hash_chain_tip,
|
known_pow_hash_chain_tip=pow_hash_chain_tip,
|
||||||
processed_pow_hash_chain_tip=pow_hash_chain_tip,
|
processed_pow_hash_chain_tip=pow_hash_chain_tip,
|
||||||
|
@ -682,7 +689,8 @@ def add_validator(validators: List[ValidatorRecord],
|
||||||
randao_last_change=current_slot,
|
randao_last_change=current_slot,
|
||||||
balance=DEPOSIT_SIZE * GWEI_PER_ETH,
|
balance=DEPOSIT_SIZE * GWEI_PER_ETH,
|
||||||
status=status,
|
status=status,
|
||||||
exit_slot=0
|
exit_slot=0,
|
||||||
|
exit_seq=0
|
||||||
)
|
)
|
||||||
index = min_empty_validator(validators)
|
index = min_empty_validator(validators)
|
||||||
if index is None:
|
if index is None:
|
||||||
|
@ -699,9 +707,11 @@ def add_validator(validators: List[ValidatorRecord],
|
||||||
def exit_validator(index, state, penalize, current_slot):
|
def exit_validator(index, state, penalize, current_slot):
|
||||||
validator = state.validators[index]
|
validator = state.validators[index]
|
||||||
validator.exit_slot = current_slot
|
validator.exit_slot = current_slot
|
||||||
|
validator.exit_seq = state.current_exit_seq
|
||||||
|
state.current_exit_seq += 1
|
||||||
if penalize:
|
if penalize:
|
||||||
validator.status = PENALIZED
|
validator.status = PENALIZED
|
||||||
state.deposits_penalized_in_period[current_slot // WITHDRAWAL_PERIOD] += validator.balance
|
state.deposits_penalized_in_period[current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += validator.balance
|
||||||
else:
|
else:
|
||||||
validator.status = PENDING_EXIT
|
validator.status = PENDING_EXIT
|
||||||
add_validator_set_change_record(state, index, validator.pubkey, EXIT)
|
add_validator_set_change_record(state, index, validator.pubkey, EXIT)
|
||||||
|
@ -1006,7 +1016,7 @@ def change_validators(validators: List[ValidatorRecord], current_slot: int) -> N
|
||||||
break
|
break
|
||||||
|
|
||||||
# Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods
|
# 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 = (
|
total_penalties = (
|
||||||
(state.deposits_penalized_in_period[period_index]) +
|
(state.deposits_penalized_in_period[period_index]) +
|
||||||
(state.deposits_penalized_in_period[period_index - 1] if period_index >= 1 else 0) +
|
(state.deposits_penalized_in_period[period_index - 1] if period_index >= 1 else 0) +
|
||||||
|
@ -1014,17 +1024,20 @@ def change_validators(validators: List[ValidatorRecord], current_slot: int) -> N
|
||||||
)
|
)
|
||||||
# Separate loop to withdraw validators that have been logged out for long enough, and
|
# Separate loop to withdraw validators that have been logged out for long enough, and
|
||||||
# calculate their penalties if they were slashed
|
# 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
|
|
||||||
validators[i].exit_slot = current_slot
|
|
||||||
|
|
||||||
withdraw_amount = validators[i].balance
|
def withdrawable(v):
|
||||||
...
|
return v.status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= v.exit_slot + MIN_WITHDRAWAL_PERIOD
|
||||||
# STUB: withdraw to shard chain
|
|
||||||
|
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
|
||||||
|
v.exit_slot = current_slot
|
||||||
|
|
||||||
|
withdraw_amount = v.balance
|
||||||
|
...
|
||||||
|
# STUB: withdraw to shard chain
|
||||||
```
|
```
|
||||||
|
|
||||||
* Set `state.validator_set_change_slot = state.last_state_recalculation_slot`
|
* Set `state.validator_set_change_slot = state.last_state_recalculation_slot`
|
||||||
|
|
Loading…
Reference in New Issue