High/low balance separation

See #685 for reasoning
This commit is contained in:
vbuterin 2019-03-07 01:38:03 -06:00 committed by GitHub
parent ec12460b8b
commit 0759e170a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 72 additions and 23 deletions

View File

@ -64,6 +64,10 @@
- [`get_epoch_start_slot`](#get_epoch_start_slot) - [`get_epoch_start_slot`](#get_epoch_start_slot)
- [`is_active_validator`](#is_active_validator) - [`is_active_validator`](#is_active_validator)
- [`get_active_validator_indices`](#get_active_validator_indices) - [`get_active_validator_indices`](#get_active_validator_indices)
- [`get_balance`](#get_balance)
- [`set_balance`](#set_balance)
- [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_balance)
- [`get_permuted_index`](#get_permuted_index) - [`get_permuted_index`](#get_permuted_index)
- [`split`](#split) - [`split`](#split)
- [`get_epoch_committee_count`](#get_epoch_committee_count) - [`get_epoch_committee_count`](#get_epoch_committee_count)
@ -205,10 +209,10 @@ Code snippets appearing in `this style` are to be interpreted as Python code.
| Name | Value | Unit | | Name | Value | Unit |
| - | - | :-: | | - | - | :-: |
| `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei | | `MIN_DEPOSIT_AMOUNT` | `2**0 * 10**9` (= 2,000,000,000) | Gwei |
| `MAX_DEPOSIT_AMOUNT` | `2**5 * 10**9` (= 32,000,000,000) | Gwei | | `MAX_DEPOSIT_AMOUNT` | `2**5 * 10**9` (= 32,000,000,000) | Gwei |
| `FORK_CHOICE_BALANCE_INCREMENT` | `2**0 * 10**9` (= 1,000,000,000) | Gwei |
| `EJECTION_BALANCE` | `2**4 * 10**9` (= 16,000,000,000) | Gwei | | `EJECTION_BALANCE` | `2**4 * 10**9` (= 16,000,000,000) | Gwei |
| `HIGH_BALANCE_INCREMENT` | `10 ** 9` (= 1,000,000,000) | Gwei |
### Initial values ### Initial values
@ -516,7 +520,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
# Validator registry # Validator registry
'validator_registry': [Validator], 'validator_registry': [Validator],
'validator_balances': ['uint64'], 'low_balances': ['uint32'],
'validator_registry_update_epoch': 'uint64', 'validator_registry_update_epoch': 'uint64',
# Randomness and committees # Randomness and committees
@ -570,6 +574,8 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git
'initiated_exit': 'bool', 'initiated_exit': 'bool',
# Was the validator slashed # Was the validator slashed
'slashed': 'bool', 'slashed': 'bool',
# Rounded balance
'high_balance': 'uint32'
} }
``` ```
@ -749,6 +755,45 @@ def get_active_validator_indices(validators: List[Validator], epoch: Epoch) -> L
return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)]
``` ```
### `get_balance`
```python
def get_balance(state: BeaconState, index: int) -> int:
return (
state.validator_registry[index].high_balance * HIGH_BALANCE_INCREMENT +
state.low_balances[index]
)
```
#### `set_balance`
````python
def set_balance(state: BeaconState, index: int, new_balance: int) -> None:
validator = state.validator_registry[index]
HALF_INCREMENT = HIGH_BALANCE_INCREMENT // 2
if (
validator.rounded_balance * HIGH_BALANCE_INCREMENT > new_balance or
validator.rounded_balance * HIGH_BALANCE_INCREMENT + HALF_INCREMENT * 3 < new_balance
):
validator.rounded_balance = new_balance // HIGH_BALANCE_INCREMENT
state.validator_fractional_balances[index] = (
new_balance - validator.rounded_balance * HIGH_BALANCE_INCREMENT
)
````
#### `increase_balance`
````python
def increase_balance(state: BeaconState, index: int, delta: int) -> None:
set_balance(state, index, get_balance(state, index) + delta)
````
#### `decrease_balance`
````python
def decrease_balance(state: BeaconState, index: int, delta: int) -> None:
set_balance(state, index, get_balance(state, index) - delta)
````
### `get_permuted_index` ### `get_permuted_index`
```python ```python
@ -1105,7 +1150,7 @@ def get_effective_balance(state: BeaconState, index: ValidatorIndex) -> Gwei:
""" """
Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
""" """
return min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT) return min(get_balance(state, index), MAX_DEPOSIT_AMOUNT)
``` ```
### `get_total_balance` ### `get_total_balance`
@ -1351,17 +1396,18 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
withdrawable_epoch=FAR_FUTURE_EPOCH, withdrawable_epoch=FAR_FUTURE_EPOCH,
initiated_exit=False, initiated_exit=False,
slashed=False, slashed=False,
high_balance=0
) )
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.
state.validator_registry.append(validator) state.validator_registry.append(validator)
state.validator_balances.append(amount) state.low_balances.append(0)
set_balance(state, len(state.validator_registry)-1, amount)
else: else:
# Increase balance by deposit amount # Increase balance by deposit amount
index = validator_pubkeys.index(pubkey) index = validator_pubkeys.index(pubkey)
assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials
increase_balance(state, index, amount)
state.validator_balances[index] += amount
``` ```
### Routines for updating validator status ### Routines for updating validator status
@ -1426,8 +1472,8 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None:
whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_index = get_beacon_proposer_index(state, state.slot)
whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT
state.validator_balances[whistleblower_index] += whistleblower_reward increase_balance(state, whistleblower_index, whistleblower_reward)
state.validator_balances[index] -= whistleblower_reward decrease_balance(state, index, whistleblower_reward)
validator.slashed = True validator.slashed = True
validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
``` ```
@ -1545,7 +1591,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
# Validator registry # Validator registry
validator_registry=[], validator_registry=[],
validator_balances=[], low_balances=[],
validator_registry_update_epoch=GENESIS_EPOCH, validator_registry_update_epoch=GENESIS_EPOCH,
# Randomness and committees # Randomness and committees
@ -1657,9 +1703,12 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock)
for validator_index in active_validator_indices for validator_index in active_validator_indices
] ]
# Use the rounded-balance-with-hysteresis supplied by the protocol for fork
# choice voting. This reduces the number of recomputations that need to be
# made for optimized implementations that precompute and save data
def get_vote_count(block: BeaconBlock) -> int: def get_vote_count(block: BeaconBlock) -> int:
return sum( return sum(
get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT start_state.validator_registry[validator_index].high_balance
for validator_index, target in attestation_targets for validator_index, target in attestation_targets
if get_ancestor(store, target, block.slot) == block if get_ancestor(store, target, block.slot) == block
) )
@ -1956,12 +2005,12 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
Note that this function mutates ``state``. Note that this function mutates ``state``.
""" """
# Verify the amount and fee aren't individually too big (for anti-overflow purposes) # Verify the amount and fee aren't individually too big (for anti-overflow purposes)
assert state.validator_balances[transfer.sender] >= max(transfer.amount, transfer.fee) assert get_balance(state, transfer.sender) >= max(transfer.amount, transfer.fee)
# Verify that we have enough ETH to send, and that after the transfer the balance will be either # Verify that we have enough ETH to send, and that after the transfer the balance will be either
# exactly zero or at least MIN_DEPOSIT_AMOUNT # exactly zero or at least MIN_DEPOSIT_AMOUNT
assert ( assert (
state.validator_balances[transfer.sender] == transfer.amount + transfer.fee or get_balance(state, transfer.sender) == transfer.amount + transfer.fee or
state.validator_balances[transfer.sender] >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT get_balance(state, transfer.sender) >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT
) )
# A transfer is valid in only one slot # A transfer is valid in only one slot
assert state.slot == transfer.slot assert state.slot == transfer.slot
@ -1983,9 +2032,9 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER) domain=get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER)
) )
# Process the transfer # Process the transfer
state.validator_balances[transfer.sender] -= transfer.amount + transfer.fee decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
state.validator_balances[transfer.recipient] += transfer.amount increase_balance(state, transfer.recipient, transfer.amount)
state.validator_balances[get_beacon_proposer_index(state, state.slot)] += transfer.fee increase_balance(state, get_beacon_proposer_index(state, state.slot), transfer.fee)
``` ```
### Per-epoch processing ### Per-epoch processing
@ -2320,10 +2369,10 @@ def apply_rewards(state: BeaconState) -> None:
deltas1 = get_justification_and_finalization_deltas(state) deltas1 = get_justification_and_finalization_deltas(state)
deltas2 = get_crosslink_deltas(state) deltas2 = get_crosslink_deltas(state)
for i in range(len(state.validator_registry)): for i in range(len(state.validator_registry)):
state.validator_balances[i] = max( set_balance(state, i, max(
0, 0,
state.validator_balances[i] + deltas1[0][i] + deltas2[0][i] - deltas1[1][i] - deltas2[1][i] get_balance(state, i) + deltas1[0][i] + deltas2[0][i] - deltas1[1][i] - deltas2[1][i]
) ))
``` ```
#### Ejections #### Ejections
@ -2337,7 +2386,7 @@ def process_ejections(state: BeaconState) -> None:
and eject active validators with balance below ``EJECTION_BALANCE``. and eject active validators with balance below ``EJECTION_BALANCE``.
""" """
for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)): for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)):
if state.validator_balances[index] < EJECTION_BALANCE: if get_balance(state, index) < EJECTION_BALANCE:
exit_validator(state, index) exit_validator(state, index)
``` ```
@ -2380,7 +2429,7 @@ def update_validator_registry(state: BeaconState) -> None:
# Activate validators within the allowable balance churn # Activate validators within the allowable balance churn
balance_churn = 0 balance_churn = 0
for index, validator in enumerate(state.validator_registry): for index, validator in enumerate(state.validator_registry):
if validator.activation_epoch == FAR_FUTURE_EPOCH and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: if validator.activation_epoch == FAR_FUTURE_EPOCH and get_balance(state, index) >= MAX_DEPOSIT_AMOUNT:
# Check the balance churn would be within the allowance # Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index) balance_churn += get_effective_balance(state, index)
if balance_churn > max_balance_churn: if balance_churn > max_balance_churn:
@ -2461,7 +2510,7 @@ def process_slashings(state: BeaconState) -> None:
get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance, get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance,
get_effective_balance(state, index) // MIN_PENALTY_QUOTIENT get_effective_balance(state, index) // MIN_PENALTY_QUOTIENT
) )
state.validator_balances[index] -= penalty decrease_balance(state, index, penalty)
``` ```
```python ```python