Files
consensus-specs/docs/docs/new-feature.md
Hsiao-Wei Wang f1aabcd718 Refactor setup.py (#3393)
* Refactor setup.py

* Update doc with the new file path

* Rearrange spec_builders

* Update doc
2023-06-26 18:01:56 +08:00

7.7 KiB

How to add a new feature proposal in consensus-specs

Table of Contents

A. Make it executable for linter checks

1. Create a folder under ./specs/_features

For example, if it's an EIP-9999 CL spec, you can create a ./specs/_features/eip9999 folder.

2. Choose the "previous fork" to extend: usually, use the scheduled or the latest mainnet fork version.

For example, if the latest fork is Capella, use ./specs/capella content as your "previous fork".

3. Write down your proposed beacon-chain.md change

  • You can either use Beacon Chain Spec Template, or make a copy of the latest fork content and then edit it.
  • Tips:
    • We use doctoc tool to generate the table of content.
      cd consensus-specs
      doctoc specs
      
    • The differences between "Constants", "Configurations", and "Presets":
      • Constants: The constant that should never be changed.
      • Configurations: The settings that we may change for different networks.
      • Presets: The settings that we may change for testing.
    • Readability and simplicity are more important than efficiency and optimization.
      • Use simple Python rather than the fancy Python dark magic.

4. Add fork.md

You can refer to the previous fork's fork.md file.

5. Make it executable

  • Update Pyspec constants.py with the new feature name.
  • Update helpers for setup.py for building the spec:
    • Update pysetup/constants.py with the new feature name as Pyspec constants.py defined.
    • Update pysetup/spec_builders/__init__.py. Implement a new <FEATURE_NAME>SpecBuilder in pysetup/spec_builders/<FEATURE_NAME>.py with the new feature name. e.g., EIP9999SpecBuilder. Append it to the spec_builders list.
    • Update pysetup/md_doc_paths.py: add the path of the new markdown files in get_md_doc_paths function if needed.

B: Make it executable for pytest and test generator

1. [Optional] Add light-client/* docs if you updated the content of BeaconBlock

  • You can refer to the previous fork's light-client/* file.
  • Add the path of the new markdown files in pysetup/md_doc_paths.py's get_md_doc_paths function.

2. Add the mainnet and minimal presets and update the configs

  • Add presets: presets/mainnet/<new-feature-name>.yaml and presets/minimal/<new-feature-name>.yaml
  • Update configs: configs/mainnet.yaml and configs/minimal.yaml

3. Update context.py

  • Update spec_targets by adding <NEW_FEATURE>
from eth2spec.eip9999 import mainnet as spec_eip9999_mainnet, minimal as spec_eip9999_minimal

...

spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = {
    MINIMAL: {
        ...
        EIP9999: spec_eip9999_minimal,
    },
    MAINNET: {
        ...
        EIP9999: spec_eip9999_mainnet
    },
}

4. Update constants.py

  • Add <NEW_FEATURE> to ALL_PHASES and TESTGEN_FORKS

5. Update genesis.py:

We use create_genesis_state to create the default state in tests.

  • Update create_genesis_state by adding fork_version setting:
def create_genesis_state(spec, validator_balances, activation_threshold):
    ...
    if spec.fork == ALTAIR:
        current_version = spec.config.ALTAIR_FORK_VERSION
    ...
    elif spec.fork == EIP9999:
        # Add the previous fork version of given fork
        previous_version = spec.config.<PREVIOUS_FORK_VERSION>
        current_version = spec.config.EIP9999_FORK_VERSION
  • If the given feature changes BeaconState fields, you have to set the initial values by adding:
def create_genesis_state(spec, validator_balances, activation_threshold):
    ...
    if is_post_eip9999(spec):
        state.<NEW_FIELD> = <value>

    return state
  • If the given feature changes ExecutionPayload fields, you have to set the initial values by updating get_sample_genesis_execution_payload_header helper.

6. To add fork transition tests, update fork_transition.py

def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate=None, operation_dict=None):
    ...

    if post_spec.fork == ALTAIR:
        state = post_spec.upgrade_to_altair(state)
    ...
    elif post_spec.fork == EIP9999:
        state = post_spec.upgrade_to_eip9999(state)

    ...

    if post_spec.fork == ALTAIR:
        assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
        assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
    ...
    elif post_spec.fork == EIP9999:
        assert state.fork.previous_version == post_spec.config.<PREVIOUS_FORK_VERSION>
        assert state.fork.current_version == post_spec.config.EIP9999_FORK_VERSION

    ...

7. Update CI configurations

Others

Bonus

  • Add validator.md if honest validator behavior changes with the new feature.

Need help?

You can tag spec elves for cleaning up your PR. 🧚