# Fork choice tests

The aim of the fork choice tests is to provide test coverage of the various components of the fork choice.

## Test case format

### `meta.yaml`

```yaml
description: string    -- Optional. Description of test case, purely for debugging purposes.
bls_setting: int       -- see general test-format spec.
```

### `anchor_state.ssz_snappy`

An SSZ-snappy encoded `BeaconState`, the state to initialize store with `get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock)` helper.

### `anchor_block.ssz_snappy`

An SSZ-snappy encoded `BeaconBlock`, the block to initialize store with `get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock)` helper.

### `steps.yaml`

The steps to execute in sequence. There may be multiple items of the following types:

#### `on_tick` execution step

The parameter that is required for executing `on_tick(store, time)`.

```yaml
{
    tick: int       -- to execute `on_tick(store, time)`.
    valid: bool     -- optional, default to `true`.
                       If it's `false`, this execution step is expected to be invalid.
}
```

After this step, the `store` object may have been updated.

#### `on_attestation` execution step

The parameter that is required for executing `on_attestation(store, attestation)`.

```yaml
{
    attestation: string  -- the name of the `attestation_<32-byte-root>.ssz_snappy` file.
                            To execute `on_attestation(store, attestation)` with the given attestation.
    valid: bool          -- optional, default to `true`.
                            If it's `false`, this execution step is expected to be invalid.
}
```
The file is located in the same folder (see below).

After this step, the `store` object may have been updated.

#### `on_block` execution step

The parameter that is required for executing `on_block(store, block)`.

```yaml
{
    block: string  -- the name of the `block_<32-byte-root>.ssz_snappy` file.
                      To execute `on_block(store, block)` with the given attestation.
    valid: bool    -- optional, default to `true`.
                      If it's `false`, this execution step is expected to be invalid.
}  
```
The file is located in the same folder (see below).

After this step, the `store` object may have been updated.

#### `on_merge_block` execution step

Adds `PowBlock` data which is required for executing `on_block(store, block)`.
```yaml
{
    pow_block: string  -- the name of the `pow_block_<32-byte-root>.ssz_snappy` file.
                          To be used in `get_pow_block` lookup
}
```
The file is located in the same folder (see below).
PowBlocks should be used as return values for `get_pow_block(hash: Hash32) -> PowBlock` function if hashes match.

#### `on_attester_slashing` execution step

The parameter that is required for executing `on_attester_slashing(store, attester_slashing)`.

```yaml
{
    attester_slashing: string  -- the name of the `attester_slashing_<32-byte-root>.ssz_snappy` file.
                            To execute `on_attester_slashing(store, attester_slashing)` with the given attester slashing.
    valid: bool          -- optional, default to `true`.
                            If it's `false`, this execution step is expected to be invalid.
}
```
The file is located in the same folder (see below).

After this step, the `store` object may have been updated.

#### `on_payload_info` execution step

Optional step for optimistic sync tests.

```yaml
{
    block_hash: string,             -- Encoded 32-byte value of payload's block hash.
    payload_status: {
        status: string,             -- Enum, "VALID" | "INVALID" | "SYNCING" | "ACCEPTED" | "INVALID_BLOCK_HASH".
        latest_valid_hash: string,    -- Encoded 32-byte value of the latest valid block hash, may be `null`.
        validation_error: string,    -- Message providing additional details on the validation error, may be `null`.
    }
}
```

This step sets the [`payloadStatus`](https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#PayloadStatusV1)
value that Execution Layer client mock returns in responses to the following Engine API calls:
* [`engine_newPayloadV1(payload)`](https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_newpayloadv1) if `payload.blockHash == payload_info.block_hash`
* [`engine_forkchoiceUpdatedV1(forkchoiceState, ...)`](https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_forkchoiceupdatedv1) if `forkchoiceState.headBlockHash == payload_info.block_hash`

*Note:* Status of a payload must be *initialized* via `on_payload_info` before the corresponding `on_block` execution step.

*Note:* Status of the same payload may be updated for several times throughout the test.

#### Checks step

The checks to verify the current status of `store`.

```yaml
checks: {<store_attibute>: value}  -- the assertions.
```

`<store_attibute>` is the field member or property of [`Store`](../../../specs/phase0/fork-choice.md#store) object that maintained by client implementation. Currently, the possible fields included:

```yaml
head: {
    slot: int,
    root: string,             -- Encoded 32-byte value from get_head(store)
}
time: int                     -- store.time
genesis_time: int             -- store.genesis_time
justified_checkpoint: {
    epoch: int,               -- Integer value from store.justified_checkpoint.epoch
    root: string,             -- Encoded 32-byte value from store.justified_checkpoint.root
}
finalized_checkpoint: {
    epoch: int,               -- Integer value from store.finalized_checkpoint.epoch
    root: string,             -- Encoded 32-byte value from store.finalized_checkpoint.root
}
best_justified_checkpoint: {
    epoch: int,               -- Integer value from store.best_justified_checkpoint.epoch
    root: string,             -- Encoded 32-byte value from store.best_justified_checkpoint.root
}
proposer_boost_root: string   -- Encoded 32-byte value from store.proposer_boost_root
```

For example:
```yaml
- checks:
    time: 192
    head: {slot: 32, root: '0xdaa1d49d57594ced0c35688a6da133abb086d191a2ebdfd736fad95299325aeb'}
    justified_checkpoint: {epoch: 3, root: '0xc25faab4acab38d3560864ca01e4d5cc4dc2cd473da053fbc03c2669143a2de4'}
    finalized_checkpoint: {epoch: 2, root: '0x40d32d6283ec11c53317a46808bc88f55657d93b95a1af920403187accf48f4f'}
    best_justified_checkpoint: {epoch: 3, root: '0xc25faab4acab38d3560864ca01e4d5cc4dc2cd473da053fbc03c2669143a2de4'}
    proposer_boost_root: '0xdaa1d49d57594ced0c35688a6da133abb086d191a2ebdfd736fad95299325aeb'
```

*Note*: Each `checks` step may include one or multiple items. Each item has to be checked against the current store.

### `attestation_<32-byte-root>.ssz_snappy`

`<32-byte-root>` is the hash tree root of the given attestation.

Each file is an SSZ-snappy encoded `Attestation`.

### `block_<32-byte-root>.ssz_snappy`

`<32-byte-root>` is the hash tree root of the given block.

Each file is an SSZ-snappy encoded `SignedBeaconBlock`.

## Condition

1. Deserialize `anchor_state.ssz_snappy` and `anchor_block.ssz_snappy` to initialize the local store object by with `get_forkchoice_store(anchor_state, anchor_block)` helper.
2. Iterate sequentially through `steps.yaml`
    - For each execution, look up the corresponding ssz_snappy file. Execute the corresponding helper function on the current store.
        - For the `on_block` execution step: if `len(block.message.body.attestations) > 0`, execute each attestation with `on_attestation(store, attestation)` after executing `on_block(store, block)`.
    - For each `checks` step, the assertions on the current store must be satisfied.
