---
# System prepended metadata

title: Hard forking Beacon State
tags: [hard_fork_1]

---

---
tags: hard_fork_1
---

# Hard forking Beacon State

## Goal
* Background
* What issues do we need to solve?
* What solutions do we have?

## Background
Hardfork 1 is coming this summer. One of the biggest foreseeable issue is there will be two versions of beacon state. One for pre fork and one for post fork. Everything that uses the beacon state will have to adapt to the two variations. Example:
```
Before epoch N: use beacon state v1
After epoch N: use beacon state v2
```
Such changes if not planned carefully will become technical debts and cause lingering issues throughout Prysm's life time.
Assuming there's one hard fork per year, this topic is something we'll revisit time and time, it's important that we document our thoughts, feedbacks and solutions during this process.

## Refresher
Currently beacon state looks like:
```python=
class BeaconState(Container):
    # Versioning
    genesis_time: uint64
    genesis_validators_root: Root
    slot: Slot
    fork: Fork
    # History
    latest_block_header: BeaconBlockHeader
    block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
    state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
    historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT]
    # Eth1
    eth1_data: Eth1Data
    eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]
    eth1_deposit_index: uint64
    # Registry
    validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
    balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
    # Randomness
    randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]
    # Slashings
    slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR]  # Per-epoch sums of slashed effective balances
    # Attestations
    previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH]
    current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH]
    # Finality
    justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH]  # Bit set for every recent justified epoch
    previous_justified_checkpoint: Checkpoint  # Previous epoch snapshot
    current_justified_checkpoint: Checkpoint
    finalized_checkpoint: Checkpoint
```

After hard fork, the beacon state will looke like:
```python=
class BeaconState(Container):
    # Versioning
    genesis_time: uint64
    genesis_validators_root: Root
    slot: Slot
    fork: Fork
    # History
    latest_block_header: BeaconBlockHeader
    block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
    state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
    historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT]
    # Eth1
    eth1_data: Eth1Data
    eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]
    eth1_deposit_index: uint64
    # Registry
    validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
    balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
    # Randomness
    randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]
    # Slashings
    slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR]  # Per-epoch sums of slashed effective balances
    # Participation
    previous_epoch_participation: List[ValidatorFlag, VALIDATOR_REGISTRY_LIMIT]
    current_epoch_participation: List[ValidatorFlag, VALIDATOR_REGISTRY_LIMIT]
    # Finality
    justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH]  # Bit set for every recent justified epoch
    previous_justified_checkpoint: Checkpoint
    current_justified_checkpoint: Checkpoint
    finalized_checkpoint: Checkpoint
    # Light client sync committees
    current_sync_committee: SyncCommittee
    next_sync_committee: SyncCommittee
```

As we can see, there's going to be *replacement* and *extension*.
* `current/previous_epoch_attestations` which is `List[PendingAttestation]` is getting replaced by `current/previous_epoch_participation` which is `List[ValidatorFlag]`
* The beacon state is also getting extended by new fields such as `current_sync_committee` and `next_sync_committee`

For designing, we should aim to design for both cases that is `replacement` and `extension`

## Solution
The most ideal solution is to implement a layer of abstraction with some wrappers around the two beacon state types. Since protobuf has getter/setter methods, we could use an interface which both states conform to. (e.g. `BeaconState` interface) 
We want to avoid: `db.SaveStateV1(*BeaconStateV1)` and `db.SaveStateV2(*BeaconStateV2)`

We want `db.SaveState(BeaconState)` where `BeaconState` is an interface:
```go
type BeaconState interface {
  Validators() []*ethpb.Validator
  ...
}

type BeaconStateV2 interface {
  BeaconState
  Eth1State() *ethpb.Eth1State // some new methods/fields added in the hard fork
}
```

Within `SaveState`, the function can use reflect and detect whether it's v1 or v2 then save it to the appropiate bucket.

## Affected area
`BeaconState` is heavily used across Prysm, this will impact not only in DB but in every service and every storage unit. Some notable ones:
* Core processing block using parent state
* Sync receiving beacon chain objects
* RPC server for serving validators
* RPC server for serving beacon block explorers
* Database for storage
* Cache for storage

## Side note
`BeaconBlockBody` will also be different post-hardfork. Whichever mechanism that came up with here, it should be reusable for the others.

## Proposal
1.) First, let's choose the location. I'm putting it under`beacon-chain/state/types` I think this is the best location given beacon state is not used in other pkgs such as `validator`, `slasher` and `shared`. It also lives next to it's implementation `beacon-chain/state`.
2.) We'll use folders `v0`, `v1`... to distingush beacon state internal changes due to network upgrade aka. hard fork.
3.) Each version `v0`, `v1`... has its own unique `struct.go`
```go=
package v0

type BeaconState struct {
	GenesisTime                 uint64
	Slot                        types.Slot
	GenesisValidatorsRoot       [32]byte
	BlockRoots                  [][32]byte
	Validators                  []*v1alpha1.Validator
	PreviousJustifiedCheckpoint *v1alpha1.Checkpoint
}
```
4.) The `BeaconState` from above implements the interface that's defined in `interface.go` under `beacon-chain/state/types`. Which looks like:
```go=
package types

type BeaconStateVO interface {
	GenesisTime() uint64
	SetGenesisTime(time uint64)
}

type BeaconStateV1 interface {
	BeaconStateVO
	// Post HF1 state getters and setters.
}
```
5.) In `beacon-chain/state/` pkg, we replace pb with the interface
```go=
type BeaconState struct {
	state                 *pbp2p.BeaconState
	lock                  sync.RWMutex
	dirtyFields           map[fieldIndex]interface{}
    ...
```

```go=
type BeaconState struct {
	state                 stateTypes.BeaconStateVO
	lock                  sync.RWMutex
	dirtyFields           map[fieldIndex]interface{}
    ...
```

6.) Then we'll replace everywhere that was using pb's getter/setter
```go=
func (b *BeaconState) genesisTime() uint64 {
	if !b.HasInnerState() {
		return 0
	}
	return b.state.GenesisTime
}
```
```go=
func (b *BeaconState) genesisTime() uint64 {
	if !b.HasInnerState() {
		return 0
	}

	return b.state.GenesisTime()
}
```

The project structure looks like the following:
![](https://i.imgur.com/Et7eVoC.png =200x500)

## Open questions
1.) For root are we using []byte or [32]byte?
```go=
type BeaconState struct {
	GenesisValidatorsRoot       [32]byte //or []byte
	BlockRoots                  [][32]byte //or [][]byte
}
```

2.) What do we do with function that returns the underlying beacon state pb object? Do we just return the underlying native struct object?
```go=
// InnerStateUnsafe returns the pointer value of the underlying
// beacon state proto object, bypassing immutability. Use with care.
func (b *BeaconState) InnerStateUnsafe() *pbp2p.BeaconState {
	if b == nil {
		return nil
	}
	return b.state
}
```