diff --git a/.github/workflows/jupyter-book.yml b/.github/workflows/jupyter-book.yml new file mode 100644 index 00000000..cea17ff2 --- /dev/null +++ b/.github/workflows/jupyter-book.yml @@ -0,0 +1,61 @@ +name: deploy-book + +# Run this when the master or main branch changes +on: + branchs: [ main ] + # push: + # branches: [ main ] + # pull_request: + # branches: [ main ] + # If your git repository has the Jupyter Book within some-subfolder next to + # unrelated files, you can make this run only if a file within that specific + # folder has been modified. + # + # paths: + # - some-subfolder/** + +# This job installs dependencies, builds the book, and pushes it to `gh-pages` +jobs: + deploy-book: + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + steps: + - uses: actions/checkout@v3 + + # Install dependencies + - name: Set up Python 3.9 + uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - name: Install dependencies + run: | + pip install -r requirements.txt + + # (optional) Cache your executed notebooks between runs + # if you have config: + # execute: + # execute_notebooks: cache + - name: Cache executed notebooks + uses: actions/cache@v3 + with: + path: docs/_build/.jupyter_cache + key: jupyter-book-cache-${{ hashFiles('requirements.txt') }} + + # Build the book + - name: Build the book + run: | + make build-docs + + # Upload the book's HTML as an artifact + - name: Upload artifact + uses: actions/upload-pages-artifact@v2 + with: + path: "docs/_build/html" + + # Deploy the book's HTML to GitHub Pages + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 20e39fd5..bff05ffd 100644 --- a/.gitignore +++ b/.gitignore @@ -106,13 +106,13 @@ celerybeat.pid *.sage.py # Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ +.env* +.venv* +env*/ +venv*/ +ENV*/ +env*.bak/ +venv*.bak/ # Spyder project settings .spyderproject @@ -142,6 +142,7 @@ scratchpad/ *.nbconvert.ipynb *.dat drafts/ +_autosummary/ # Masterclass exam **/*answers.ipynb \ No newline at end of file diff --git a/Makefile b/Makefile index 91f2bcb8..e1bb3a27 100644 --- a/Makefile +++ b/Makefile @@ -20,15 +20,14 @@ test: execute-notebooks # Run Pytest tests python3 -m pytest -m "not api_test" tests -build-docs: docs-pdoc docs-jupyter-book +build-docs: docs-jupyter-book docs-pdoc: pdoc --html model -o docs --force docs-jupyter-book: jupyter-book clean docs - jupyter-book build --config docs/_config.yml --toc docs/_toc.yml --path-output docs . - cp -r ./docs/model ./docs/_build/html/docs/model + python build_docs.py serve-docs: gunicorn -w 4 -b 127.0.0.1:5000 docs.server:app diff --git a/build_docs.py b/build_docs.py new file mode 100644 index 00000000..67b4516f --- /dev/null +++ b/build_docs.py @@ -0,0 +1,5 @@ +from jupyter_book.cli.main import main + +# See https://stackoverflow.com/questions/74367920/sphinx-recursive-autosummary-not-importing-modules +# jupyter-book build --config docs/_config.yml --toc docs/_toc.yml --path-output docs . +main(["build", "--config", "docs/_config.yml", "--toc", "docs/_toc.yml", "--path-output", "docs", "."]) diff --git a/docs/_config.yml b/docs/_config.yml index 587a8d55..dbd42eb5 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,9 +1,27 @@ -title: "CADLabs Ethereum Research Model Documentation" -# logo: media/_.png +title: "CADLabs Ethereum Economic Model" +logo: docs/logo.png only_build_toc_files: true execute: - execute_notebooks: "off" + # execute_notebooks: off + execute_notebooks: cache +parse: + myst_enable_extensions: + # don't forget to list any other extensions you want enabled, + # including those that are enabled by default! + - amsmath + - dollarmath + - html_image + - attrs_inline sphinx: + extra_extensions: + - 'sphinx.ext.autodoc' + - 'sphinx.ext.autosummary' + - 'sphinx.ext.napoleon' + - 'sphinx.ext.viewcode' config: + # html_theme: pydata_sphinx_theme + templates_path: ['docs/_templates/autosummary'] + add_module_names: True + autosummary_generate: True html_js_files: - - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js + - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js \ No newline at end of file diff --git a/docs/_templates/autosummary/custom-class-templates.rst b/docs/_templates/autosummary/custom-class-templates.rst new file mode 100644 index 00000000..cd11e449 --- /dev/null +++ b/docs/_templates/autosummary/custom-class-templates.rst @@ -0,0 +1,32 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :show-inheritance: + :inherited-members: + + {% block methods %} + .. automethod:: __init__ + + {% if methods %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + {% for item in methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Attributes') }} + + .. autosummary:: + {% for item in attributes %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} \ No newline at end of file diff --git a/docs/_templates/autosummary/custom-module-template.rst b/docs/_templates/autosummary/custom-module-template.rst new file mode 100644 index 00000000..93be64df --- /dev/null +++ b/docs/_templates/autosummary/custom-module-template.rst @@ -0,0 +1,66 @@ +{{ fullname | escape | underline}} + +.. automodule:: {{ fullname }} + + {% block attributes %} + {% if attributes %} + .. rubric:: Module Attributes + + .. autosummary:: + :toctree: + {% for item in attributes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block functions %} + {% if functions %} + .. rubric:: {{ _('Functions') }} + + .. autosummary:: + :toctree: + {% for item in functions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block classes %} + {% if classes %} + .. rubric:: {{ _('Classes') }} + + .. autosummary:: + :toctree: + :template: custom-class-template.rst + {% for item in classes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block exceptions %} + {% if exceptions %} + .. rubric:: {{ _('Exceptions') }} + + .. autosummary:: + :toctree: + {% for item in exceptions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + +{% block modules %} +{% if modules %} +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: custom-module-template.rst + :recursive: +{% for item in modules %} + {{ item }} +{%- endfor %} +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/docs/_toc.yml b/docs/_toc.yml index 4f66b815..b42e26cf 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -1,13 +1,19 @@ format: jb-book -root: docs/landing-page +root: README parts: + - caption: Model Overview + chapters: + # - file: README + # title: "Model README" + - file: ASSUMPTIONS + - file: ROADMAP + - file: CHANGELOG - caption: Model Specification chapters: - file: docs/model_specification/mathematical_specification - caption: Documentation chapters: - - url: https://cadlabs.gitlab.io/cadlabs-ethereum-model-docs/docs/index.html - title: Model Code Documentation + - file: docs/code.rst - caption: Experiment Notebooks chapters: - file: experiments/notebooks/0_README diff --git a/docs/code.rst b/docs/code.rst new file mode 100644 index 00000000..bb862508 --- /dev/null +++ b/docs/code.rst @@ -0,0 +1,15 @@ +Model Code Documentation +================= + +.. autosummary:: + :toctree: _autosummary + :recursive: + + model.parts + model.constants + model.state_update_blocks + model.state_variables + model.stochastic_processes + model.system_parameters + model.types + model.utils \ No newline at end of file diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 00000000..49540b6a --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,21 @@ +# Introduction + +A modular dynamical-systems model of Ethereum's validator economics, based on the open-source Python library [radCAD](https://github.com/CADLabs/radCAD), an extension to [cadCAD](https://cadcad.org). + +This open-source model was developed in collaboration with the Ethereum Robust Incentives Group and funded by an Ethereum ESP (Ecosystem Support Program) grant. While originally scoped with purely modelling-educational intent as part of the cadCAD Edu online course "[cadCAD Masterclass: Ethereum Validator Economics](https://www.cadcad.education/course/masterclass-ethereum)", it has evolved to become a highly versatile, customizable and extensible research model that includes a list of [model extension ideas](#Model-Extension-Roadmap). The model is focused on epoch- and population-level Ethereum validator economics across different deployment types and – at least in its initial setup – abstracts from slot- and agent-level dynamics. Please see [Model Assumptions](ASSUMPTIONS.md) for further context. + +* GitHub repo: [CADLabs Ethereum Economic Model](https://github.com/CADLabs/ethereum-economic-model) +* Latest model release version: [Subgraph / v1.1.7](https://github.com/CADLabs/ethereum-economic-model/releases/tag/v1.1.7) +* Implements the official Ethereum [Altair](https://github.com/ethereum/eth2.0-specs#altair) spec updates in the [Blue Loop / v1.1.0-alpha.7](https://github.com/ethereum/eth2.0-specs/releases/tag/v1.1.0-alpha.7) release + +## Model Features + +* Configurable to reflect protocol behaviour at different points in time of the development roadmap (referred to as "upgrade stages"): + * Post Beacon Chain launch, pre EIP-1559, pre PoS (validators receive PoS incentives, EIP-1559 disabled, and PoW still in operation) + * Post Beacon Chain launch, post EIP-1559, pre PoS (validators receive PoS incentives, EIP-1559 enabled with miners receiving priority fees, and PoW still in operation) + * Post Beacon Chain launch, post EIP-1559, post PoS (validators receive PoS incentives, EIP-1559 enabled with validators receiving priority fees, and PoW deprecated) +* Flexible calculation granularity: By default, State Variables, System Metrics, and System Parameters are calculated at epoch level and aggregated daily (~= 225 epochs). Users can easily change epoch aggregation using the delta-time (`dt`) parameter. The model can be extended for slot-level granularity and analysis if that is desired (see [Model Extension Roadmap](#Model-Extension-Roadmap)). +* Supports [state-space analysis](https://en.wikipedia.org/wiki/State-space_representation) (i.e. simulation of system state over time) and [phase-space analysis](https://en.wikipedia.org/wiki/Phase_space) (i.e. generation of all unique system states in a given experimental setup). +* Customizable processes to set important variables such as ETH price, ETH staked, and EIP-1559 transaction pricing. +* Modular model structure for convenient extension and modification. This allows different user groups to refactor the model for different purposes, rapidly test new incentive mechanisms, or update the model as Ethereum implements new protocol improvements. +* References to official [Eth2 specs](https://github.com/ethereum/eth2.0-specs) in Policy and State Update Function logic. This enables seamless onboarding of protocol developers and allows the more advanced cadCAD user to dig into the underlying protocol design that inspired the logic. diff --git a/docs/landing-page.md b/docs/landing-page.md deleted file mode 100644 index d1797ed3..00000000 --- a/docs/landing-page.md +++ /dev/null @@ -1,7 +0,0 @@ -# Landing Page - -Welcome to the CADLabs Ethereum Research Model! - -Get started by visiting: -* The CADLabs Ethereum Research Model GitHub repo: https://github.com/CADLabs/ethereum-model -* The model code documentation generated from Python docstrings: Model Documentation diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 00000000..4bb7b98a Binary files /dev/null and b/docs/logo.png differ diff --git a/docs/model/constants.html b/docs/model/constants.html deleted file mode 100644 index af8e9e08..00000000 --- a/docs/model/constants.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - -model.constants API documentation - - - - - - - - - - - -
-
-
-

Module model.constants

-
-
-

Constants used in the model e.g. number of epochs in a year, Gwei in 1 Ether

-
- -Expand source code - -
"""
-Constants used in the model e.g. number of epochs in a year, Gwei in 1 Ether
-"""
-
-gwei = 1e9
-wei = 1e18
-slots_per_epoch = 32
-epochs_per_day = 225
-epochs_per_month = 6750
-epochs_per_year = 82180
-pow_blocks_per_epoch = 32.0 * 12 / 13
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/index.html b/docs/model/index.html deleted file mode 100644 index 42f73f18..00000000 --- a/docs/model/index.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - -model API documentation - - - - - - - - - - - -
-
-
-

Namespace model

-
-
-
-
-

Sub-modules

-
-
model.constants
-
-

Constants used in the model e.g. number of epochs in a year, Gwei in 1 Ether

-
-
model.parts
-
-
-
-
experiments.simulation_configuration
-
-

Simulation configuration such as the number of timesteps and Monte Carlo runs

-
-
model.state_update_blocks
-
-

cadCAD model State Update Block structure, composed of Policy and State Update Functions

-
-
model.state_variables
-
-

Definition of State Variables, their types, and default values …

-
-
model.stochastic_processes
-
-

Helper functions to generate stochastic environmental processes

-
-
model.system_parameters
-
-

Definition of System Parameters, their types, and default values …

-
-
model.types
-
-

Various Python types used in the model

-
-
model.utils
-
-

Misc. utility and helper functions

-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parameters.html b/docs/model/parameters.html deleted file mode 100644 index 2b394714..00000000 --- a/docs/model/parameters.html +++ /dev/null @@ -1,1107 +0,0 @@ - - - - - - -model.parameters API documentation - - - - - - - - - - - -
-
-
-

Module model.parameters

-
-
-

Definition of System Parameters, their types, and default values.

-

By using a dataclass to represent the System Parameters: -* We can use types for Python type hints -* Set default values -* Ensure that all System Parameters are initialized

-
- -Expand source code - -
"""
-Definition of System Parameters, their types, and default values.
-
-By using a dataclass to represent the System Parameters:
-* We can use types for Python type hints
-* Set default values
-* Ensure that all System Parameters are initialized
-"""
-
-
-import numpy as np
-from dataclasses import dataclass
-import logging
-from datetime import datetime
-
-import experiments.simulation_configuration as simulation
-import model.constants as constants
-from model.types import (
-    Run,
-    Timestep,
-    Percentage,
-    Gwei,
-    Gas,
-    Gwei_per_Gas,
-    ETH,
-    USD_per_epoch,
-    Percentage_per_epoch,
-    ValidatorEnvironment,
-    List,
-    Callable,
-    Epoch,
-    Stage,
-)
-from model.utils import default
-from model.processes import create_stochastic_process_realizations
-
-
-# Create stochastic (random) process realizations
-stochastic_process_realizations = create_stochastic_process_realizations()
-eth_price_samples = stochastic_process_realizations["eth_price_samples"]
-
-# Configure validator environment distribution
-validator_environments = [
-    ValidatorEnvironment(
-        # Configure a custom environment
-        # Used for dissagregation of single validator performance
-        type="custom",
-        percentage_distribution=0.01,  # Set to 1% by default
-        hardware_costs_per_epoch=0.0014,
-        cloud_costs_per_epoch=0,
-        third_party_costs_per_epoch=0,
-    ),
-    ValidatorEnvironment(
-        type="diy_hardware",
-        percentage_distribution=0.37,
-        hardware_costs_per_epoch=0.0014,
-    ),
-    ValidatorEnvironment(
-        type="diy_cloud",
-        percentage_distribution=0.13,
-        cloud_costs_per_epoch=0.00027,
-    ),
-    ValidatorEnvironment(
-        type="pool_staas",
-        percentage_distribution=0.27,
-        third_party_costs_per_epoch=0.12,
-    ),
-    ValidatorEnvironment(
-        type="pool_hardware",
-        percentage_distribution=0.05,
-        hardware_costs_per_epoch=0.0007,
-    ),
-    ValidatorEnvironment(
-        type="pool_cloud",
-        percentage_distribution=0.02,
-        cloud_costs_per_epoch=0.00136,
-    ),
-    ValidatorEnvironment(
-        type="staas_full",
-        percentage_distribution=0.08,
-        third_party_costs_per_epoch=0.15,
-    ),
-    ValidatorEnvironment(
-        type="staas_self_custodied",
-        percentage_distribution=0.08,
-        third_party_costs_per_epoch=0.12,
-    ),
-]
-"""Validator environment configuration
-
-From the Hoban/Borgers report (Ethereum 2.0 Economic Review):
-> assume validators will consider different validator models according to their preferences, requirements, and the scale of their stake
-> The breakdown of validator environments reflects the results of user surveys and stakeholder interviews
-
-Cost analysis:
-> See "Ethereum 2.0 Ecosystem Staking Report" by ConsenSys Insights: https://cdn2.hubspot.net/hubfs/4795067/Codefi/Ethereum%202.0%20Staking%20Ecosystem%20Report.pdf?__hstc=148571112.51d5567256d6f4167c1422d5c083e93e.1574348924308.1588770700176.1588788083651.18&__hssc=148571112.1.1588788083651
-> See "Ethereum Lighthouse: Chasing Serenity" survey report by Empire Ventures: https://medium.com/empireventures/eth2uxreport-858c73ca1f53
-"""
-
-# Normalise percentage distribution to a total of 100%
-total_percentage_distribution = sum(
-    [validator.percentage_distribution for validator in validator_environments]
-)
-
-if total_percentage_distribution < 1:
-    logging.warn(
-        """
-    Parameter validator.percentage_distribution normalized due to sum not being equal to 100%
-    """
-    )
-    for validator in validator_environments:
-        validator.percentage_distribution /= total_percentage_distribution
-
-# Using list comprehension, map the validator types to each parameter
-validator_percentage_distribution = [
-    np.array(
-        [validator.percentage_distribution for validator in validator_environments],
-        dtype=Percentage,
-    )
-]
-validator_hardware_costs_per_epoch = [
-    np.array(
-        [validator.hardware_costs_per_epoch for validator in validator_environments],
-        dtype=USD_per_epoch,
-    )
-]
-validator_cloud_costs_per_epoch = [
-    np.array(
-        [validator.cloud_costs_per_epoch for validator in validator_environments],
-        dtype=USD_per_epoch,
-    )
-]
-validator_third_party_costs_per_epoch = [
-    np.array(
-        [validator.third_party_costs_per_epoch for validator in validator_environments],
-        dtype=Percentage_per_epoch,
-    )
-]
-
-
-@dataclass
-class Parameters:
-    """System Parameters
-    Each System Parameter is defined as:
-    system parameter key: system parameter type = default system parameter value
-
-    Because lists are mutable, we need to wrap each parameter list in the `default(...)` method.
-    """
-
-    # Time parameters
-    dt: List[Epoch] = default([simulation.DELTA_TIME])
-    """
-    Simulation timescale / timestep unit of time, in epochs.
-
-    Used to scale calculations that depend on the number of epochs that have passed.
-
-    For example, for dt = 100, each timestep equals 100 epochs.
-    
-    By default set to constants.epochs_per_day (225)
-    """
-
-    stage: List[Stage] = default([Stage.PROOF_OF_STAKE])
-    """
-    Which stage or stages of the network upgrade process to simulate.
-
-    By default set to PROOF_OF_STAKE stage, where EIP1559 is enabled and POW issuance is disabled.
-
-    See model.types.Stage Enum for further documentation.
-    """
-
-    date_start: List[datetime] = default([datetime.now()])
-    """Start date for simulation as Python datetime"""
-
-    date_eip1559: List[datetime] = default(
-        [datetime.strptime("2021/07/14", "%Y/%m/%d")]
-    )
-    """
-    EIP1559 activation date as Python datetime
-    Source: https://github.com/ethereum/pm/issues/245#issuecomment-825751460
-    """
-
-    date_merge: List[datetime] = default([datetime.strptime("2021/12/1", "%Y/%m/%d")])
-    """
-    Eth1/Eth2 merge date as Python datetime
-    Source: https://twitter.com/drakefjustin/status/1379052831982956547
-    """
-
-    # Environmental processes
-    eth_price_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda run, timestep: eth_price_samples[run - 1][timestep]]
-    )
-    """
-    A process that returns the ETH spot price at each epoch.
-    """
-
-    eth_staked_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda _run, _timestep: None]
-    )
-    """
-    A process that returns the ETH staked at each epoch.
-    
-    If set to `none`, the model is driven by the validator process,
-    where new validators enter the system and stake accordingly.
-
-    This process is used for simulating a series of ETH staked values directly.
-    """
-
-    validator_process: List[Callable[[Run, Timestep], int]] = default(
-        [
-            # From https://beaconscan.com/statistics as of 20/04/21
-            lambda _run, _timestep: 3,
-        ]
-    )
-    """
-    A process that returns the number of new validators per epoch.
-    
-    Used if model not driven using `eth_staked_process`.
-
-    By default set to a static value from https://beaconscan.com/statistics.
-    """
-
-    # Ethereum system parameters
-    daily_pow_issuance: List[ETH] = default([13_550])
-    """
-    The average daily Proof of Work issuance in ETH.
-
-    See https://etherscan.io/chart/blockreward
-    """
-
-    # Parameters from the Eth2 specification
-    # Uppercase used for all parameters from Eth2 specification
-    BASE_REWARD_FACTOR: List[int] = default([64])
-    """
-    A parameter used to change the issuance rate of the Ethereum PoS system.
-
-    Most validator rewards and penalties are calculated in terms of the base reward.
-    """
-    MAX_EFFECTIVE_BALANCE: List[Gwei] = default([32 * constants.gwei])
-    """
-    A validators effective balance is used to calculate incentives, and for voting,
-    and is a value less than the total stake/balance.
-
-    The max effective balance of a validator is 32 ETH.
-    """
-    EFFECTIVE_BALANCE_INCREMENT: List[Gwei] = default([1 * constants.gwei])
-    """
-    A validators effective balance can only change in steps of EFFECTIVE_BALANCE_INCREMENT,
-    which reduces the computational load for state updates.
-    """
-    PROPOSER_REWARD_QUOTIENT: List[int] = default([8])
-    """
-    Used to calculate the proportion of rewards distributed between attesters and proposers.
-    """
-    WHISTLEBLOWER_REWARD_QUOTIENT: List[int] = default([512])
-    """
-    Used to calculate the proportion of the effective balance of the slashed validator
-    distributed between the whistleblower and the proposer.
-    """
-    MIN_SLASHING_PENALTY_QUOTIENT: List[int] = default([2 ** 6])
-    """
-    Used to calculate the penalty applied for a slashable offence.
-    """
-    PROPORTIONAL_SLASHING_MULTIPLIER: List[int] = default([2])
-    """
-    Scales the slashing penalty proportional to the total slashings for the current epoch
-
-    i.e. the more slashing events there are, the greater the individual penalty
-    """
-    TIMELY_HEAD_WEIGHT: List[int] = default([12])
-    """
-    Used to calculate the reward received for getting a head vote in time and correctly.
-
-    `head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_SOURCE_WEIGHT: List[int] = default([12])
-    """
-    Used to calculate the reward received for getting a source vote in time and correctly.
-
-    `source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_TARGET_WEIGHT: List[int] = default([24])
-    """
-    Used to calculate the reward received for getting a target vote in time and correctly.
-
-    `target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    SYNC_REWARD_WEIGHT: List[int] = default([8])
-    """
-    Used to calculate the reward received for attesting as part of a sync committee.
-    """
-    PROPOSER_WEIGHT: List[int] = default([8])
-    """
-    Used to calculate the reward received for successfully proposing a block.
-    """
-    WEIGHT_DENOMINATOR: List[int] = default([64])
-    """
-    Used as the denominator in incentive calculations to calculate reward and penalty proportions.
-    """
-    MIN_PER_EPOCH_CHURN_LIMIT: List[int] = default([4])
-    """
-    Used to calculate the churn limit for validator entry and exit. The maximum number of validators that can
-    enter or exit the system per epoch.
-    
-    In this system it is used for the validator activation queue process.
-    """
-    CHURN_LIMIT_QUOTIENT: List[int] = default([2 ** 16])
-    """
-    Used in the calculation of the churn limit to set a point at which the limit increases.
-    """
-    BASE_FEE_MAX_CHANGE_DENOMINATOR: List[int] = default([8])
-    """
-    Used to set the maximum rate at which the EIP1559 basefee can change per block, approx. 12.5%.
-    """
-    ELASTICITY_MULTIPLIER: List[int] = default([2])
-    """
-    Used to calculate gas limit from EIP1559 gas target
-    """
-
-    # Validator parameters
-    validator_uptime_process: List[Percentage] = default(
-        [lambda _run, _timestep: max(0.98, 0.666)]
-    )
-    """
-    The combination of validator internet, power, and technical uptime, as a percentage.
-
-    Minimum uptime is inactivity leak threshold = 2/3, as this model doesn't model the inactivity leak process.
-    """
-    validator_percentage_distribution: List[np.ndarray] = default(
-        validator_percentage_distribution
-    )
-    """
-    The percentage of validators in each environment, normalized to a total of 100%.
-
-    A vector with a value for each validator environment.
-    """
-    validator_hardware_costs_per_epoch: List[np.ndarray] = default(
-        validator_hardware_costs_per_epoch
-    )
-    """
-    The validator hardware costs per epoch in dollars.
-    
-    A vector with a value for each validator environment.
-    """
-    validator_cloud_costs_per_epoch: List[np.ndarray] = default(
-        validator_cloud_costs_per_epoch
-    )
-    """
-    The validator cloud costs per epoch in dollars.
-
-    A vector with a value for each validator environment.
-    """
-    validator_third_party_costs_per_epoch: List[np.ndarray] = default(
-        validator_third_party_costs_per_epoch
-    )
-    """
-    The validator third-party costs as a percentage of total online validator rewards.
-
-    Used for expected Staking-as-a-Service fees.
-
-    A vector with a value for each validator environment.
-    """
-
-    # Rewards, penalties, and slashing
-    slashing_events_per_1000_epochs: List[int] = default([1])  # 1 / 1000 epochs
-    """
-    The number of slashing events per 1000 epochs.
-    
-    Asssumption from Hoban/Borgers report.
-    """
-
-    # EIP1559 transaction pricing parameters
-    eip1559_basefee_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 70]  # Gwei per gas
-    )
-    """
-    The basefee burned, in Gwei per gas, for each transaction.
-    
-    An average of 100 Gwei per gas expected to be set as transaction fee cap,
-    split between the basefee and tips - the fee cap less the basefee is sent as a tip to miners/validators.
-
-    Approximated using average gas price from https://etherscan.io/gastracker as of 20/04/21
-
-    An extract from https://notes.ethereum.org/@vbuterin/eip-1559-faq
-    
-    > Each “full block” (ie. a block whose gas is 2x the TARGET) increases the BASEFEE by 1.125x,
-    > so a series of constant full blocks will increase the gas price by a factor of 10 every
-    > ~20 blocks (~4.3 min on average).
-    > Hence, periods of heavy on-chain load will not realistically last longer than ~5 minutes.
-    """
-
-    eip1559_tip_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 30]  # Gwei per gas
-    )
-    """
-    EIP1559 transaction pricing tip, in Gwei per gas.
-    
-    Due to MEV, average tips expected to be higher than usual as bid for inclusion in blockscpace market.
-    
-    The tip is the difference between the fee cap set per transaction, and the basefee.
-
-    For PoW system without MEV influence, the tip level compensates for uncle risk:
-    See https://notes.ethereum.org/@vbuterin/BkSQmQTS8#Why-would-miners-include-transactions-at-all
-    """
-
-    gas_target: List[Gas] = default([15e6])
-    """
-    The long-term average gas target per block.
-
-    The current gas limit is replaced by two values:
-    * a “long-term average target” (equal to the current gas limit) == gas target
-    * a “hard per-block cap” (twice the current gas limit) == gas limit
-
-    EIP1559 gas limit = gas_target * ELASTICITY_MULTIPLIER
-    See https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-    daily_transactions_process: List[int] = default(
-        [lambda _run=None, _timestep=None: 1_400_000]
-    )
-    """
-    A process that returns the number of transactions per day.
-
-    fees_per_day = daily_transactions * transaction_average_gas * (basefee + tip) / 1e9 ~= 10k ETH
-    (see https://etherscan.io/chart/transactionfee)
-    
-    Where:
-    * daily_transactions ~= 1_400_000
-    * transaction_average_gas ~= 73_123
-    * (basefee + tip) ~= 100
-
-    Default static daily transactions from https://etherscan.io/chart/tx as of 20/04/21
-    """
-
-    transaction_average_gas: List[Gas] = default([73_123])
-    """
-    The average gas used per transaction.
-
-    A simple ETH transfer takes 21,000 gas,
-    but executing a trade on a decentralized exchange can cost 100,000 gas or more.
-
-    See https://coinmetrics.io/the-ethereum-gas-report/
-    """
-
-
-# Initialize Parameters instance with default values
-parameters = Parameters().__dict__
-
-
-
-
-
-

Global variables

-
-
var validator_environments
-
-

Validator environment configuration

-

From the Hoban/Borgers report (Ethereum 2.0 Economic Review):

-
-

assume validators will consider different validator models according to their preferences, requirements, and the scale of their stake -The breakdown of validator environments reflects the results of user surveys and stakeholder interviews

-
-

Cost analysis:

-
-

See "Ethereum 2.0 Ecosystem Staking Report" by ConsenSys Insights: https://cdn2.hubspot.net/hubfs/4795067/Codefi/Ethereum%202.0%20Staking%20Ecosystem%20Report.pdf?__hstc=148571112.51d5567256d6f4167c1422d5c083e93e.1574348924308.1588770700176.1588788083651.18&__hssc=148571112.1.1588788083651 -See "Ethereum Lighthouse: Chasing Serenity" survey report by Empire Ventures: https://medium.com/empireventures/eth2uxreport-858c73ca1f53

-
-
-
-
-
-
-
-

Classes

-
-
-class Parameters -(dt: List[int] = <factory>, stage: List[Stage] = <factory>, date_start: List[datetime.datetime] = <factory>, date_eip1559: List[datetime.datetime] = <factory>, date_merge: List[datetime.datetime] = <factory>, eth_price_process: List[Callable[[int, int], float]] = <factory>, eth_staked_process: List[Callable[[int, int], float]] = <factory>, validator_process: List[Callable[[int, int], int]] = <factory>, daily_pow_issuance: List[float] = <factory>, BASE_REWARD_FACTOR: List[int] = <factory>, MAX_EFFECTIVE_BALANCE: List[float] = <factory>, EFFECTIVE_BALANCE_INCREMENT: List[float] = <factory>, PROPOSER_REWARD_QUOTIENT: List[int] = <factory>, WHISTLEBLOWER_REWARD_QUOTIENT: List[int] = <factory>, MIN_SLASHING_PENALTY_QUOTIENT: List[int] = <factory>, PROPORTIONAL_SLASHING_MULTIPLIER: List[int] = <factory>, TIMELY_HEAD_WEIGHT: List[int] = <factory>, TIMELY_SOURCE_WEIGHT: List[int] = <factory>, TIMELY_TARGET_WEIGHT: List[int] = <factory>, SYNC_REWARD_WEIGHT: List[int] = <factory>, PROPOSER_WEIGHT: List[int] = <factory>, WEIGHT_DENOMINATOR: List[int] = <factory>, MIN_PER_EPOCH_CHURN_LIMIT: List[int] = <factory>, CHURN_LIMIT_QUOTIENT: List[int] = <factory>, BASE_FEE_MAX_CHANGE_DENOMINATOR: List[int] = <factory>, ELASTICITY_MULTIPLIER: List[int] = <factory>, validator_uptime_process: List[float] = <factory>, validator_percentage_distribution: List[numpy.ndarray] = <factory>, validator_hardware_costs_per_epoch: List[numpy.ndarray] = <factory>, validator_cloud_costs_per_epoch: List[numpy.ndarray] = <factory>, validator_third_party_costs_per_epoch: List[numpy.ndarray] = <factory>, slashing_events_per_1000_epochs: List[int] = <factory>, eip1559_basefee_process: List[Callable[[int, int], float]] = <factory>, eip1559_tip_process: List[Callable[[int, int], float]] = <factory>, gas_target: List[int] = <factory>, daily_transactions_process: List[int] = <factory>, transaction_average_gas: List[int] = <factory>) -
-
-

System Parameters -Each System Parameter is defined as: -system parameter key: system parameter type = default system parameter value

-

Because lists are mutable, we need to wrap each parameter list in the default(…) method.

-
- -Expand source code - -
class Parameters:
-    """System Parameters
-    Each System Parameter is defined as:
-    system parameter key: system parameter type = default system parameter value
-
-    Because lists are mutable, we need to wrap each parameter list in the `default(...)` method.
-    """
-
-    # Time parameters
-    dt: List[Epoch] = default([simulation.DELTA_TIME])
-    """
-    Simulation timescale / timestep unit of time, in epochs.
-
-    Used to scale calculations that depend on the number of epochs that have passed.
-
-    For example, for dt = 100, each timestep equals 100 epochs.
-    
-    By default set to constants.epochs_per_day (225)
-    """
-
-    stage: List[Stage] = default([Stage.PROOF_OF_STAKE])
-    """
-    Which stage or stages of the network upgrade process to simulate.
-
-    By default set to PROOF_OF_STAKE stage, where EIP1559 is enabled and POW issuance is disabled.
-
-    See model.types.Stage Enum for further documentation.
-    """
-
-    date_start: List[datetime] = default([datetime.now()])
-    """Start date for simulation as Python datetime"""
-
-    date_eip1559: List[datetime] = default(
-        [datetime.strptime("2021/07/14", "%Y/%m/%d")]
-    )
-    """
-    EIP1559 activation date as Python datetime
-    Source: https://github.com/ethereum/pm/issues/245#issuecomment-825751460
-    """
-
-    date_merge: List[datetime] = default([datetime.strptime("2021/12/1", "%Y/%m/%d")])
-    """
-    Eth1/Eth2 merge date as Python datetime
-    Source: https://twitter.com/drakefjustin/status/1379052831982956547
-    """
-
-    # Environmental processes
-    eth_price_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda run, timestep: eth_price_samples[run - 1][timestep]]
-    )
-    """
-    A process that returns the ETH spot price at each epoch.
-    """
-
-    eth_staked_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda _run, _timestep: None]
-    )
-    """
-    A process that returns the ETH staked at each epoch.
-    
-    If set to `none`, the model is driven by the validator process,
-    where new validators enter the system and stake accordingly.
-
-    This process is used for simulating a series of ETH staked values directly.
-    """
-
-    validator_process: List[Callable[[Run, Timestep], int]] = default(
-        [
-            # From https://beaconscan.com/statistics as of 20/04/21
-            lambda _run, _timestep: 3,
-        ]
-    )
-    """
-    A process that returns the number of new validators per epoch.
-    
-    Used if model not driven using `eth_staked_process`.
-
-    By default set to a static value from https://beaconscan.com/statistics.
-    """
-
-    # Ethereum system parameters
-    daily_pow_issuance: List[ETH] = default([13_550])
-    """
-    The average daily Proof of Work issuance in ETH.
-
-    See https://etherscan.io/chart/blockreward
-    """
-
-    # Parameters from the Eth2 specification
-    # Uppercase used for all parameters from Eth2 specification
-    BASE_REWARD_FACTOR: List[int] = default([64])
-    """
-    A parameter used to change the issuance rate of the Ethereum PoS system.
-
-    Most validator rewards and penalties are calculated in terms of the base reward.
-    """
-    MAX_EFFECTIVE_BALANCE: List[Gwei] = default([32 * constants.gwei])
-    """
-    A validators effective balance is used to calculate incentives, and for voting,
-    and is a value less than the total stake/balance.
-
-    The max effective balance of a validator is 32 ETH.
-    """
-    EFFECTIVE_BALANCE_INCREMENT: List[Gwei] = default([1 * constants.gwei])
-    """
-    A validators effective balance can only change in steps of EFFECTIVE_BALANCE_INCREMENT,
-    which reduces the computational load for state updates.
-    """
-    PROPOSER_REWARD_QUOTIENT: List[int] = default([8])
-    """
-    Used to calculate the proportion of rewards distributed between attesters and proposers.
-    """
-    WHISTLEBLOWER_REWARD_QUOTIENT: List[int] = default([512])
-    """
-    Used to calculate the proportion of the effective balance of the slashed validator
-    distributed between the whistleblower and the proposer.
-    """
-    MIN_SLASHING_PENALTY_QUOTIENT: List[int] = default([2 ** 6])
-    """
-    Used to calculate the penalty applied for a slashable offence.
-    """
-    PROPORTIONAL_SLASHING_MULTIPLIER: List[int] = default([2])
-    """
-    Scales the slashing penalty proportional to the total slashings for the current epoch
-
-    i.e. the more slashing events there are, the greater the individual penalty
-    """
-    TIMELY_HEAD_WEIGHT: List[int] = default([12])
-    """
-    Used to calculate the reward received for getting a head vote in time and correctly.
-
-    `head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_SOURCE_WEIGHT: List[int] = default([12])
-    """
-    Used to calculate the reward received for getting a source vote in time and correctly.
-
-    `source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_TARGET_WEIGHT: List[int] = default([24])
-    """
-    Used to calculate the reward received for getting a target vote in time and correctly.
-
-    `target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    SYNC_REWARD_WEIGHT: List[int] = default([8])
-    """
-    Used to calculate the reward received for attesting as part of a sync committee.
-    """
-    PROPOSER_WEIGHT: List[int] = default([8])
-    """
-    Used to calculate the reward received for successfully proposing a block.
-    """
-    WEIGHT_DENOMINATOR: List[int] = default([64])
-    """
-    Used as the denominator in incentive calculations to calculate reward and penalty proportions.
-    """
-    MIN_PER_EPOCH_CHURN_LIMIT: List[int] = default([4])
-    """
-    Used to calculate the churn limit for validator entry and exit. The maximum number of validators that can
-    enter or exit the system per epoch.
-    
-    In this system it is used for the validator activation queue process.
-    """
-    CHURN_LIMIT_QUOTIENT: List[int] = default([2 ** 16])
-    """
-    Used in the calculation of the churn limit to set a point at which the limit increases.
-    """
-    BASE_FEE_MAX_CHANGE_DENOMINATOR: List[int] = default([8])
-    """
-    Used to set the maximum rate at which the EIP1559 basefee can change per block, approx. 12.5%.
-    """
-    ELASTICITY_MULTIPLIER: List[int] = default([2])
-    """
-    Used to calculate gas limit from EIP1559 gas target
-    """
-
-    # Validator parameters
-    validator_uptime_process: List[Percentage] = default(
-        [lambda _run, _timestep: max(0.98, 0.666)]
-    )
-    """
-    The combination of validator internet, power, and technical uptime, as a percentage.
-
-    Minimum uptime is inactivity leak threshold = 2/3, as this model doesn't model the inactivity leak process.
-    """
-    validator_percentage_distribution: List[np.ndarray] = default(
-        validator_percentage_distribution
-    )
-    """
-    The percentage of validators in each environment, normalized to a total of 100%.
-
-    A vector with a value for each validator environment.
-    """
-    validator_hardware_costs_per_epoch: List[np.ndarray] = default(
-        validator_hardware_costs_per_epoch
-    )
-    """
-    The validator hardware costs per epoch in dollars.
-    
-    A vector with a value for each validator environment.
-    """
-    validator_cloud_costs_per_epoch: List[np.ndarray] = default(
-        validator_cloud_costs_per_epoch
-    )
-    """
-    The validator cloud costs per epoch in dollars.
-
-    A vector with a value for each validator environment.
-    """
-    validator_third_party_costs_per_epoch: List[np.ndarray] = default(
-        validator_third_party_costs_per_epoch
-    )
-    """
-    The validator third-party costs as a percentage of total online validator rewards.
-
-    Used for expected Staking-as-a-Service fees.
-
-    A vector with a value for each validator environment.
-    """
-
-    # Rewards, penalties, and slashing
-    slashing_events_per_1000_epochs: List[int] = default([1])  # 1 / 1000 epochs
-    """
-    The number of slashing events per 1000 epochs.
-    
-    Asssumption from Hoban/Borgers report.
-    """
-
-    # EIP1559 transaction pricing parameters
-    eip1559_basefee_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 70]  # Gwei per gas
-    )
-    """
-    The basefee burned, in Gwei per gas, for each transaction.
-    
-    An average of 100 Gwei per gas expected to be set as transaction fee cap,
-    split between the basefee and tips - the fee cap less the basefee is sent as a tip to miners/validators.
-
-    Approximated using average gas price from https://etherscan.io/gastracker as of 20/04/21
-
-    An extract from https://notes.ethereum.org/@vbuterin/eip-1559-faq
-    
-    > Each “full block” (ie. a block whose gas is 2x the TARGET) increases the BASEFEE by 1.125x,
-    > so a series of constant full blocks will increase the gas price by a factor of 10 every
-    > ~20 blocks (~4.3 min on average).
-    > Hence, periods of heavy on-chain load will not realistically last longer than ~5 minutes.
-    """
-
-    eip1559_tip_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 30]  # Gwei per gas
-    )
-    """
-    EIP1559 transaction pricing tip, in Gwei per gas.
-    
-    Due to MEV, average tips expected to be higher than usual as bid for inclusion in blockscpace market.
-    
-    The tip is the difference between the fee cap set per transaction, and the basefee.
-
-    For PoW system without MEV influence, the tip level compensates for uncle risk:
-    See https://notes.ethereum.org/@vbuterin/BkSQmQTS8#Why-would-miners-include-transactions-at-all
-    """
-
-    gas_target: List[Gas] = default([15e6])
-    """
-    The long-term average gas target per block.
-
-    The current gas limit is replaced by two values:
-    * a “long-term average target” (equal to the current gas limit) == gas target
-    * a “hard per-block cap” (twice the current gas limit) == gas limit
-
-    EIP1559 gas limit = gas_target * ELASTICITY_MULTIPLIER
-    See https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-    daily_transactions_process: List[int] = default(
-        [lambda _run=None, _timestep=None: 1_400_000]
-    )
-    """
-    A process that returns the number of transactions per day.
-
-    fees_per_day = daily_transactions * transaction_average_gas * (basefee + tip) / 1e9 ~= 10k ETH
-    (see https://etherscan.io/chart/transactionfee)
-    
-    Where:
-    * daily_transactions ~= 1_400_000
-    * transaction_average_gas ~= 73_123
-    * (basefee + tip) ~= 100
-
-    Default static daily transactions from https://etherscan.io/chart/tx as of 20/04/21
-    """
-
-    transaction_average_gas: List[Gas] = default([73_123])
-    """
-    The average gas used per transaction.
-
-    A simple ETH transfer takes 21,000 gas,
-    but executing a trade on a decentralized exchange can cost 100,000 gas or more.
-
-    See https://coinmetrics.io/the-ethereum-gas-report/
-    """
-
-

Class variables

-
-
var BASE_FEE_MAX_CHANGE_DENOMINATOR : List[int]
-
-

Used to set the maximum rate at which the EIP1559 basefee can change per block, approx. 12.5%.

-
-
var BASE_REWARD_FACTOR : List[int]
-
-

A parameter used to change the issuance rate of the Ethereum PoS system.

-

Most validator rewards and penalties are calculated in terms of the base reward.

-
-
var CHURN_LIMIT_QUOTIENT : List[int]
-
-

Used in the calculation of the churn limit to set a point at which the limit increases.

-
-
var EFFECTIVE_BALANCE_INCREMENT : List[float]
-
-

A validators effective balance can only change in steps of EFFECTIVE_BALANCE_INCREMENT, -which reduces the computational load for state updates.

-
-
var ELASTICITY_MULTIPLIER : List[int]
-
-

Used to calculate gas limit from EIP1559 gas target

-
-
var MAX_EFFECTIVE_BALANCE : List[float]
-
-

A validators effective balance is used to calculate incentives, and for voting, -and is a value less than the total stake/balance.

-

The max effective balance of a validator is 32 ETH.

-
-
var MIN_PER_EPOCH_CHURN_LIMIT : List[int]
-
-

Used to calculate the churn limit for validator entry and exit. The maximum number of validators that can -enter or exit the system per epoch.

-

In this system it is used for the validator activation queue process.

-
-
var MIN_SLASHING_PENALTY_QUOTIENT : List[int]
-
-

Used to calculate the penalty applied for a slashable offence.

-
-
var PROPORTIONAL_SLASHING_MULTIPLIER : List[int]
-
-

Scales the slashing penalty proportional to the total slashings for the current epoch

-

i.e. the more slashing events there are, the greater the individual penalty

-
-
var PROPOSER_REWARD_QUOTIENT : List[int]
-
-

Used to calculate the proportion of rewards distributed between attesters and proposers.

-
-
var PROPOSER_WEIGHT : List[int]
-
-

Used to calculate the reward received for successfully proposing a block.

-
-
var SYNC_REWARD_WEIGHT : List[int]
-
-

Used to calculate the reward received for attesting as part of a sync committee.

-
-
var TIMELY_HEAD_WEIGHT : List[int]
-
-

Used to calculate the reward received for getting a head vote in time and correctly.

-

head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward

-
-
var TIMELY_SOURCE_WEIGHT : List[int]
-
-

Used to calculate the reward received for getting a source vote in time and correctly.

-

source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward

-
-
var TIMELY_TARGET_WEIGHT : List[int]
-
-

Used to calculate the reward received for getting a target vote in time and correctly.

-

target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward

-
-
var WEIGHT_DENOMINATOR : List[int]
-
-

Used as the denominator in incentive calculations to calculate reward and penalty proportions.

-
-
var WHISTLEBLOWER_REWARD_QUOTIENT : List[int]
-
-

Used to calculate the proportion of the effective balance of the slashed validator -distributed between the whistleblower and the proposer.

-
-
var daily_pow_issuance : List[float]
-
-

The average daily Proof of Work issuance in ETH.

-

See https://etherscan.io/chart/blockreward

-
-
var daily_transactions_process : List[int]
-
-

A process that returns the number of transactions per day.

-

fees_per_day = daily_transactions * transaction_average_gas * (basefee + tip) / 1e9 ~= 10k ETH -(see https://etherscan.io/chart/transactionfee)

-

Where: -* daily_transactions ~= 1_400_000 -* transaction_average_gas ~= 73_123 -* (basefee + tip) ~= 100

-

Default static daily transactions from https://etherscan.io/chart/tx as of 20/04/21

-
-
var date_eip1559 : List[datetime.datetime]
-
-

EIP1559 activation date as Python datetime -Source: https://github.com/ethereum/pm/issues/245#issuecomment-825751460

-
-
var date_merge : List[datetime.datetime]
-
-

Eth1/Eth2 merge date as Python datetime -Source: https://twitter.com/drakefjustin/status/1379052831982956547

-
-
var date_start : List[datetime.datetime]
-
-

Start date for simulation as Python datetime

-
-
var dt : List[int]
-
-

Simulation timescale / timestep unit of time, in epochs.

-

Used to scale calculations that depend on the number of epochs that have passed.

-

For example, for dt = 100, each timestep equals 100 epochs.

-

By default set to constants.epochs_per_day (225)

-
-
var eip1559_basefee_process : List[Callable[[int, int], float]]
-
-

The basefee burned, in Gwei per gas, for each transaction.

-

An average of 100 Gwei per gas expected to be set as transaction fee cap, -split between the basefee and tips - the fee cap less the basefee is sent as a tip to miners/validators.

-

Approximated using average gas price from https://etherscan.io/gastracker as of 20/04/21

-

An extract from https://notes.ethereum.org/@vbuterin/eip-1559-faq

-
-

Each “full block” (ie. a block whose gas is 2x the TARGET) increases the BASEFEE by 1.125x, -so a series of constant full blocks will increase the gas price by a factor of 10 every -~20 blocks (~4.3 min on average). -Hence, periods of heavy on-chain load will not realistically last longer than ~5 minutes.

-
-
-
var eip1559_tip_process : List[Callable[[int, int], float]]
-
-

EIP1559 transaction pricing tip, in Gwei per gas.

-

Due to MEV, average tips expected to be higher than usual as bid for inclusion in blockscpace market.

-

The tip is the difference between the fee cap set per transaction, and the basefee.

-

For PoW system without MEV influence, the tip level compensates for uncle risk: -See https://notes.ethereum.org/@vbuterin/BkSQmQTS8#Why-would-miners-include-transactions-at-all

-
-
var eth_price_process : List[Callable[[int, int], float]]
-
-

A process that returns the ETH spot price at each epoch.

-
-
var eth_staked_process : List[Callable[[int, int], float]]
-
-

A process that returns the ETH staked at each epoch.

-

If set to none, the model is driven by the validator process, -where new validators enter the system and stake accordingly.

-

This process is used for simulating a series of ETH staked values directly.

-
-
var gas_target : List[int]
-
-

The long-term average gas target per block.

-

The current gas limit is replaced by two values: -* a “long-term average target” (equal to the current gas limit) == gas target -* a “hard per-block cap” (twice the current gas limit) == gas limit

-

EIP1559 gas limit = gas_target * ELASTICITY_MULTIPLIER -See https://eips.ethereum.org/EIPS/eip-1559

-
-
var slashing_events_per_1000_epochs : List[int]
-
-

The number of slashing events per 1000 epochs.

-

Asssumption from Hoban/Borgers report.

-
-
var stage : List[Stage]
-
-

Which stage or stages of the network upgrade process to simulate.

-

By default set to PROOF_OF_STAKE stage, where EIP1559 is enabled and POW issuance is disabled.

-

See model.types.Stage Enum for further documentation.

-
-
var transaction_average_gas : List[int]
-
-

The average gas used per transaction.

-

A simple ETH transfer takes 21,000 gas, -but executing a trade on a decentralized exchange can cost 100,000 gas or more.

-

See https://coinmetrics.io/the-ethereum-gas-report/

-
-
var validator_cloud_costs_per_epoch : List[numpy.ndarray]
-
-

The validator cloud costs per epoch in dollars.

-

A vector with a value for each validator environment.

-
-
var validator_hardware_costs_per_epoch : List[numpy.ndarray]
-
-

The validator hardware costs per epoch in dollars.

-

A vector with a value for each validator environment.

-
-
var validator_percentage_distribution : List[numpy.ndarray]
-
-

The percentage of validators in each environment, normalized to a total of 100%.

-

A vector with a value for each validator environment.

-
-
var validator_process : List[Callable[[int, int], int]]
-
-

A process that returns the number of new validators per epoch.

-

Used if model not driven using eth_staked_process.

-

By default set to a static value from https://beaconscan.com/statistics.

-
-
var validator_third_party_costs_per_epoch : List[numpy.ndarray]
-
-

The validator third-party costs as a percentage of total online validator rewards.

-

Used for expected Staking-as-a-Service fees.

-

A vector with a value for each validator environment.

-
-
var validator_uptime_process : List[float]
-
-

The combination of validator internet, power, and technical uptime, as a percentage.

-

Minimum uptime is inactivity leak threshold = 2/3, as this model doesn't model the inactivity leak process.

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/ethereum.html b/docs/model/parts/ethereum.html deleted file mode 100644 index 20640347..00000000 --- a/docs/model/parts/ethereum.html +++ /dev/null @@ -1,414 +0,0 @@ - - - - - - -model.parts.ethereum API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.ethereum

-
-
-

Ethereum System

-

Policy Functions and State Update Functions shared between the Eth1 and Eth2 systems.

-
- -Expand source code - -
"""
-# Ethereum System
-
-Policy Functions and State Update Functions shared between the Eth1 and Eth2 systems.
-"""
-
-import typing
-
-import model.constants as constants
-from model.types import ETH, USD_per_ETH, Gwei, Stage
-
-
-def policy_network_issuance(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, ETH]:
-    # Parameters
-    dt = params["dt"]
-    daily_pow_issuance = params["daily_pow_issuance"]
-
-    # State Variables
-    stage = previous_state["stage"]
-    amount_slashed = previous_state["amount_slashed"]
-    total_basefee = previous_state["total_basefee"]
-    total_priority_fee_to_validators = previous_state["total_priority_fee_to_validators"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate network issuance in ETH
-    network_issuance = (
-        # Remove tips to validators which is not issuance (ETH transferred rather than minted)
-        (total_online_validator_rewards - total_priority_fee_to_validators)
-        - amount_slashed
-        - total_basefee
-    ) / constants.gwei
-
-    # Calculate Proof of Work issuance
-    pow_issuance = (
-        daily_pow_issuance / constants.epochs_per_day
-        if Stage(stage) in [Stage.BEACON_CHAIN, Stage.EIP1559]
-        else 0
-    )
-    network_issuance += pow_issuance * dt
-
-    return {
-        "network_issuance": network_issuance,
-        "pow_issuance": pow_issuance,
-    }
-
-
-def policy_eip1559_transaction_pricing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """EIP1559 Transaction Pricing Mechanism
-    A transaction pricing mechanism that includes fixed-per-block network fee
-    that is burned and dynamically expands/contracts block sizes to deal with transient congestion.
-
-    See:
-    * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
-    * https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-    stage = Stage(previous_state["stage"])
-    if not stage in [Stage.EIP1559, Stage.PROOF_OF_STAKE]:
-        return {
-            "basefee": 0,
-            "total_basefee": 0,
-            "total_tips_to_miners": 0,
-            "total_priority_fee_to_validators": 0,
-        }
-
-    # Parameters
-    dt = params["dt"]
-    gas_target = params["gas_target"]  # Gas
-    ELASTICITY_MULTIPLIER = params["ELASTICITY_MULTIPLIER"]
-    BASE_FEE_MAX_CHANGE_DENOMINATOR = params["BASE_FEE_MAX_CHANGE_DENOMINATOR"]
-    eip1559_basefee_process = params["eip1559_basefee_process"]
-    eip1559_tip_process = params["eip1559_tip_process"]
-    daily_transactions_process = params["daily_transactions_process"]
-    transaction_average_gas = params["transaction_average_gas"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-    previous_basefee = previous_state["basefee"]
-
-    # Get samples for current run and timestep from basefee, tip, and transaction processes
-    basefee = eip1559_basefee_process(run, timestep * dt)  # Gwei per Gas
-
-    # Ensure basefee changes by no more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %
-    # assert (
-    #     abs(basefee - previous_basefee) / previous_basefee
-    #     <= constants.slots_per_epoch / BASE_FEE_MAX_CHANGE_DENOMINATOR
-    #     if timestep > 1
-    #     else True
-    # ), "basefee changed by more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %"
-
-    avg_tip_amount = eip1559_tip_process(run, timestep * dt)  # Gwei per Gas
-    transactions_per_day = daily_transactions_process(
-        run, timestep * dt
-    )  # Transactions per day
-    transactions_per_epoch = (
-        transactions_per_day / constants.epochs_per_day
-    )  # Transactions per epoch
-
-    # Calculate total basefee and tips to validators
-    gas_used = transactions_per_epoch * transaction_average_gas  # Gas
-    total_basefee = gas_used * basefee  # Gwei
-    total_tips = gas_used * avg_tip_amount  # Gwei
-
-    if stage in [Stage.PROOF_OF_STAKE]:
-        total_tips_to_miners = 0
-        total_priority_fee_to_validators = total_tips
-    else:
-        total_tips_to_miners = total_tips
-        total_priority_fee_to_validators = 0
-
-    # Check if the block used too much gas
-    assert (
-        gas_used <= gas_target * ELASTICITY_MULTIPLIER * constants.slots_per_epoch
-    ), "invalid block: too much gas used"
-
-    return {
-        "basefee": basefee,
-        "total_basefee": total_basefee * dt,
-        "total_tips_to_miners": total_tips_to_miners * dt,
-        "total_priority_fee_to_validators": total_priority_fee_to_validators * dt,
-    }
-
-
-def update_eth_price(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, USD_per_ETH]:
-    # Parameters
-    dt = params["dt"]
-    eth_price_process = params["eth_price_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-
-    # Get the ETH price sample for the current run and timestep
-    eth_price_sample = eth_price_process(run, timestep * dt)
-
-    return "eth_price", eth_price_sample
-
-
-def update_eth_supply(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, ETH]:
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # State variables
-    eth_supply = previous_state["eth_supply"]
-
-    return "eth_supply", eth_supply + network_issuance
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_eip1559_transaction_pricing(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

EIP1559 Transaction Pricing Mechanism -A transaction pricing mechanism that includes fixed-per-block network fee -that is burned and dynamically expands/contracts block sizes to deal with transient congestion.

-

See: -* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md -* https://eips.ethereum.org/EIPS/eip-1559

-
- -Expand source code - -
def policy_eip1559_transaction_pricing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """EIP1559 Transaction Pricing Mechanism
-    A transaction pricing mechanism that includes fixed-per-block network fee
-    that is burned and dynamically expands/contracts block sizes to deal with transient congestion.
-
-    See:
-    * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
-    * https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-    stage = Stage(previous_state["stage"])
-    if not stage in [Stage.EIP1559, Stage.PROOF_OF_STAKE]:
-        return {
-            "basefee": 0,
-            "total_basefee": 0,
-            "total_tips_to_miners": 0,
-            "total_priority_fee_to_validators": 0,
-        }
-
-    # Parameters
-    dt = params["dt"]
-    gas_target = params["gas_target"]  # Gas
-    ELASTICITY_MULTIPLIER = params["ELASTICITY_MULTIPLIER"]
-    BASE_FEE_MAX_CHANGE_DENOMINATOR = params["BASE_FEE_MAX_CHANGE_DENOMINATOR"]
-    eip1559_basefee_process = params["eip1559_basefee_process"]
-    eip1559_tip_process = params["eip1559_tip_process"]
-    daily_transactions_process = params["daily_transactions_process"]
-    transaction_average_gas = params["transaction_average_gas"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-    previous_basefee = previous_state["basefee"]
-
-    # Get samples for current run and timestep from basefee, tip, and transaction processes
-    basefee = eip1559_basefee_process(run, timestep * dt)  # Gwei per Gas
-
-    # Ensure basefee changes by no more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %
-    # assert (
-    #     abs(basefee - previous_basefee) / previous_basefee
-    #     <= constants.slots_per_epoch / BASE_FEE_MAX_CHANGE_DENOMINATOR
-    #     if timestep > 1
-    #     else True
-    # ), "basefee changed by more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %"
-
-    avg_tip_amount = eip1559_tip_process(run, timestep * dt)  # Gwei per Gas
-    transactions_per_day = daily_transactions_process(
-        run, timestep * dt
-    )  # Transactions per day
-    transactions_per_epoch = (
-        transactions_per_day / constants.epochs_per_day
-    )  # Transactions per epoch
-
-    # Calculate total basefee and tips to validators
-    gas_used = transactions_per_epoch * transaction_average_gas  # Gas
-    total_basefee = gas_used * basefee  # Gwei
-    total_tips = gas_used * avg_tip_amount  # Gwei
-
-    if stage in [Stage.PROOF_OF_STAKE]:
-        total_tips_to_miners = 0
-        total_priority_fee_to_validators = total_tips
-    else:
-        total_tips_to_miners = total_tips
-        total_priority_fee_to_validators = 0
-
-    # Check if the block used too much gas
-    assert (
-        gas_used <= gas_target * ELASTICITY_MULTIPLIER * constants.slots_per_epoch
-    ), "invalid block: too much gas used"
-
-    return {
-        "basefee": basefee,
-        "total_basefee": total_basefee * dt,
-        "total_tips_to_miners": total_tips_to_miners * dt,
-        "total_priority_fee_to_validators": total_priority_fee_to_validators * dt,
-    }
-
-
-
-def policy_network_issuance(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-
-
- -Expand source code - -
def policy_network_issuance(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, ETH]:
-    # Parameters
-    dt = params["dt"]
-    daily_pow_issuance = params["daily_pow_issuance"]
-
-    # State Variables
-    stage = previous_state["stage"]
-    amount_slashed = previous_state["amount_slashed"]
-    total_basefee = previous_state["total_basefee"]
-    total_priority_fee_to_validators = previous_state["total_priority_fee_to_validators"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate network issuance in ETH
-    network_issuance = (
-        # Remove tips to validators which is not issuance (ETH transferred rather than minted)
-        (total_online_validator_rewards - total_priority_fee_to_validators)
-        - amount_slashed
-        - total_basefee
-    ) / constants.gwei
-
-    # Calculate Proof of Work issuance
-    pow_issuance = (
-        daily_pow_issuance / constants.epochs_per_day
-        if Stage(stage) in [Stage.BEACON_CHAIN, Stage.EIP1559]
-        else 0
-    )
-    network_issuance += pow_issuance * dt
-
-    return {
-        "network_issuance": network_issuance,
-        "pow_issuance": pow_issuance,
-    }
-
-
-
-def update_eth_price(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-
-
- -Expand source code - -
def update_eth_price(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, USD_per_ETH]:
-    # Parameters
-    dt = params["dt"]
-    eth_price_process = params["eth_price_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-
-    # Get the ETH price sample for the current run and timestep
-    eth_price_sample = eth_price_process(run, timestep * dt)
-
-    return "eth_price", eth_price_sample
-
-
-
-def update_eth_supply(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-
-
- -Expand source code - -
def update_eth_supply(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, ETH]:
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # State variables
-    eth_supply = previous_state["eth_supply"]
-
-    return "eth_supply", eth_supply + network_issuance
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/ethereum_system.html b/docs/model/parts/ethereum_system.html deleted file mode 100644 index 4811f1e7..00000000 --- a/docs/model/parts/ethereum_system.html +++ /dev/null @@ -1,607 +0,0 @@ - - - - - - -model.parts.ethereum_system API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.ethereum_system

-
-
-

Ethereum System

-

General Ethereum mechanisms, such as managing the system upgrade process, -the EIP-1559 transaction pricing mechanism, and updating the ETH price and ETH supply.

-
- -Expand source code - -
"""
-# Ethereum System
-
-General Ethereum mechanisms, such as managing the system upgrade process,
-the EIP-1559 transaction pricing mechanism, and updating the ETH price and ETH supply.
-"""
-
-import typing
-import datetime
-
-from model import constants as constants
-from model.types import ETH, USD_per_ETH, Gwei, Stage
-
-
-def policy_upgrade_stages(params, substep, state_history, previous_state):
-    """
-    ## Upgrade Stages Policy
-
-    Transitions the model from one stage in the Ethereum network
-    upgrade process to the next at different milestones.
-
-    This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine
-    """
-
-    # Parameters
-    dt = params["dt"]
-    stage: Stage = params["stage"]
-    date_start = params["date_start"]
-    date_eip1559 = params["date_eip1559"]
-    date_pos = params["date_pos"]
-
-    # State Variables
-    current_stage = previous_state["stage"]
-    timestep = previous_state["timestep"]
-
-    # Calculate current timestamp from timestep
-    timestamp = date_start + datetime.timedelta(
-        days=(timestep * dt / constants.epochs_per_day)
-    )
-
-    # Initialize stage State Variable at start of simulation
-    if current_stage is None:
-        current_stage = stage
-    else:
-        # Convert Stage enum value (int) to Stage enum
-        current_stage = Stage(current_stage)
-
-    # Stage finite-state machine
-    if stage == Stage.ALL:
-        # If Stage ALL selected, transition through all stages
-        # at different timestamps
-        if (
-            current_stage in [Stage.ALL, Stage.BEACON_CHAIN]
-            and timestamp < date_eip1559
-        ):
-            current_stage = Stage.BEACON_CHAIN
-        elif (
-            current_stage in [Stage.BEACON_CHAIN, Stage.EIP1559]
-            and timestamp < date_pos
-        ):
-            current_stage = Stage.EIP1559
-        else:
-            current_stage = Stage.PROOF_OF_STAKE
-    elif stage == Stage.BEACON_CHAIN:
-        # If Stage BEACON_CHAIN selected, only execute single stage
-        current_stage = Stage.BEACON_CHAIN
-    elif stage == Stage.EIP1559:
-        # If Stage EIP1559 selected, only execute single stage
-        current_stage = Stage.EIP1559
-    elif stage == Stage.PROOF_OF_STAKE:
-        # If Stage PROOF_OF_STAKE selected, only execute single stage
-        current_stage = Stage.PROOF_OF_STAKE
-    else:
-        # Else, raise exception if invalid Stage
-        raise Exception("Invalid Stage selected")
-
-    return {
-        "stage": current_stage.value,
-        "timestamp": timestamp,
-    }
-
-
-def policy_network_issuance(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, ETH]:
-    """
-    ## Network Issuance Policy Function
-
-    Calculate the total network issuance and issuance from Proof of Work block rewards.
-    """
-
-    # Parameters
-    dt = params["dt"]
-    daily_pow_issuance = params["daily_pow_issuance"]
-
-    # State Variables
-    stage = previous_state["stage"]
-    amount_slashed = previous_state["amount_slashed"]
-    total_base_fee = previous_state["total_base_fee"]
-    total_priority_fee_to_validators = previous_state[
-        "total_priority_fee_to_validators"
-    ]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate network issuance in ETH
-    network_issuance = (
-        # Remove priority fee to validators which is not issuance (ETH transferred rather than minted)
-        (total_online_validator_rewards - total_priority_fee_to_validators)
-        - amount_slashed
-        - total_base_fee
-    ) / constants.gwei
-
-    # Calculate Proof of Work issuance
-    pow_issuance = (
-        daily_pow_issuance / constants.epochs_per_day
-        if Stage(stage) in [Stage.BEACON_CHAIN, Stage.EIP1559]
-        else 0
-    )
-    network_issuance += pow_issuance * dt
-
-    return {
-        "network_issuance": network_issuance,
-        "pow_issuance": pow_issuance,
-    }
-
-
-def policy_eip1559_transaction_pricing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## EIP1559 Transaction Pricing Mechanism
-
-    A transaction pricing mechanism that includes fixed-per-block network fee
-    that is burned and dynamically expands/contracts block sizes to deal with transient congestion.
-
-    See:
-    * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
-    * https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-    stage = Stage(previous_state["stage"])
-    if stage not in [Stage.EIP1559, Stage.PROOF_OF_STAKE]:
-        return {
-            "base_fee_per_gas": 0,
-            "total_base_fee": 0,
-            "total_priority_fee_to_miners": 0,
-            "total_priority_fee_to_validators": 0,
-        }
-
-    # Parameters
-    dt = params["dt"]
-    gas_target_process = params["gas_target_process"]  # Gas
-    ELASTICITY_MULTIPLIER = params["ELASTICITY_MULTIPLIER"]
-    base_fee_process = params["base_fee_process"]
-    priority_fee_process = params["priority_fee_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-
-    # Get samples for current run and timestep from base fee, priority fee, and transaction processes
-    base_fee_per_gas = base_fee_process(run, timestep * dt)  # Gwei per Gas
-
-    gas_target = gas_target_process(run, timestep * dt)  # Gas
-
-    # Ensure basefee changes by no more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %
-    _BASE_FEE_MAX_CHANGE_DENOMINATOR = params["BASE_FEE_MAX_CHANGE_DENOMINATOR"]
-    # assert (
-    #     abs(basefee - previous_basefee) / previous_basefee
-    #     <= constants.slots_per_epoch / BASE_FEE_MAX_CHANGE_DENOMINATOR
-    #     if timestep > 1
-    #     else True
-    # ), "basefee changed by more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %"
-
-    avg_priority_fee_per_gas = priority_fee_process(run, timestep * dt)  # Gwei per Gas
-
-    if stage in [Stage.EIP1559]:
-        gas_used = constants.pow_blocks_per_epoch * gas_target  # Gas
-    else:  # stage is Stage.PROOF_OF_STAKE
-        gas_used = constants.slots_per_epoch * gas_target  # Gas
-
-    # Calculate total base fee, and priority fee to validators
-    total_base_fee = gas_used * base_fee_per_gas  # Gwei
-    total_priority_fee = gas_used * avg_priority_fee_per_gas  # Gwei
-
-    if stage in [Stage.PROOF_OF_STAKE]:
-        total_priority_fee_to_miners = 0
-        total_priority_fee_to_validators = total_priority_fee
-    else:
-        total_priority_fee_to_miners = total_priority_fee
-        total_priority_fee_to_validators = 0
-
-    # Check if the block used too much gas
-    assert (
-        gas_used <= gas_target * ELASTICITY_MULTIPLIER * constants.slots_per_epoch
-    ), "invalid block: too much gas used"
-
-    return {
-        "base_fee_per_gas": base_fee_per_gas,
-        "total_base_fee": total_base_fee * dt,
-        "total_priority_fee_to_miners": total_priority_fee_to_miners * dt,
-        "total_priority_fee_to_validators": total_priority_fee_to_validators * dt,
-    }
-
-
-def update_eth_price(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, USD_per_ETH]:
-    """
-    ## ETH Price State Update Function
-
-    Update the ETH price from the `eth_price_process`.
-    """
-
-    # Parameters
-    dt = params["dt"]
-    eth_price_process = params["eth_price_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-
-    # Get the ETH price sample for the current run and timestep
-    eth_price_sample = eth_price_process(run, timestep * dt)
-
-    return "eth_price", eth_price_sample
-
-
-def update_eth_supply(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, ETH]:
-    """
-    ## ETH Supply State Update Function
-
-    Update the ETH supply from the Network Issuance Policy Function.
-    """
-
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # State variables
-    eth_supply = previous_state["eth_supply"]
-
-    return "eth_supply", eth_supply + network_issuance
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_eip1559_transaction_pricing(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

EIP1559 Transaction Pricing Mechanism

-

A transaction pricing mechanism that includes fixed-per-block network fee -that is burned and dynamically expands/contracts block sizes to deal with transient congestion.

-

See: -* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md -* https://eips.ethereum.org/EIPS/eip-1559

-
- -Expand source code - -
def policy_eip1559_transaction_pricing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## EIP1559 Transaction Pricing Mechanism
-
-    A transaction pricing mechanism that includes fixed-per-block network fee
-    that is burned and dynamically expands/contracts block sizes to deal with transient congestion.
-
-    See:
-    * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
-    * https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-    stage = Stage(previous_state["stage"])
-    if stage not in [Stage.EIP1559, Stage.PROOF_OF_STAKE]:
-        return {
-            "base_fee_per_gas": 0,
-            "total_base_fee": 0,
-            "total_priority_fee_to_miners": 0,
-            "total_priority_fee_to_validators": 0,
-        }
-
-    # Parameters
-    dt = params["dt"]
-    gas_target_process = params["gas_target_process"]  # Gas
-    ELASTICITY_MULTIPLIER = params["ELASTICITY_MULTIPLIER"]
-    base_fee_process = params["base_fee_process"]
-    priority_fee_process = params["priority_fee_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-
-    # Get samples for current run and timestep from base fee, priority fee, and transaction processes
-    base_fee_per_gas = base_fee_process(run, timestep * dt)  # Gwei per Gas
-
-    gas_target = gas_target_process(run, timestep * dt)  # Gas
-
-    # Ensure basefee changes by no more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %
-    _BASE_FEE_MAX_CHANGE_DENOMINATOR = params["BASE_FEE_MAX_CHANGE_DENOMINATOR"]
-    # assert (
-    #     abs(basefee - previous_basefee) / previous_basefee
-    #     <= constants.slots_per_epoch / BASE_FEE_MAX_CHANGE_DENOMINATOR
-    #     if timestep > 1
-    #     else True
-    # ), "basefee changed by more than 1 / BASE_FEE_MAX_CHANGE_DENOMINATOR %"
-
-    avg_priority_fee_per_gas = priority_fee_process(run, timestep * dt)  # Gwei per Gas
-
-    if stage in [Stage.EIP1559]:
-        gas_used = constants.pow_blocks_per_epoch * gas_target  # Gas
-    else:  # stage is Stage.PROOF_OF_STAKE
-        gas_used = constants.slots_per_epoch * gas_target  # Gas
-
-    # Calculate total base fee, and priority fee to validators
-    total_base_fee = gas_used * base_fee_per_gas  # Gwei
-    total_priority_fee = gas_used * avg_priority_fee_per_gas  # Gwei
-
-    if stage in [Stage.PROOF_OF_STAKE]:
-        total_priority_fee_to_miners = 0
-        total_priority_fee_to_validators = total_priority_fee
-    else:
-        total_priority_fee_to_miners = total_priority_fee
-        total_priority_fee_to_validators = 0
-
-    # Check if the block used too much gas
-    assert (
-        gas_used <= gas_target * ELASTICITY_MULTIPLIER * constants.slots_per_epoch
-    ), "invalid block: too much gas used"
-
-    return {
-        "base_fee_per_gas": base_fee_per_gas,
-        "total_base_fee": total_base_fee * dt,
-        "total_priority_fee_to_miners": total_priority_fee_to_miners * dt,
-        "total_priority_fee_to_validators": total_priority_fee_to_validators * dt,
-    }
-
-
-
-def policy_network_issuance(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Network Issuance Policy Function

-

Calculate the total network issuance and issuance from Proof of Work block rewards.

-
- -Expand source code - -
def policy_network_issuance(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, ETH]:
-    """
-    ## Network Issuance Policy Function
-
-    Calculate the total network issuance and issuance from Proof of Work block rewards.
-    """
-
-    # Parameters
-    dt = params["dt"]
-    daily_pow_issuance = params["daily_pow_issuance"]
-
-    # State Variables
-    stage = previous_state["stage"]
-    amount_slashed = previous_state["amount_slashed"]
-    total_base_fee = previous_state["total_base_fee"]
-    total_priority_fee_to_validators = previous_state[
-        "total_priority_fee_to_validators"
-    ]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate network issuance in ETH
-    network_issuance = (
-        # Remove priority fee to validators which is not issuance (ETH transferred rather than minted)
-        (total_online_validator_rewards - total_priority_fee_to_validators)
-        - amount_slashed
-        - total_base_fee
-    ) / constants.gwei
-
-    # Calculate Proof of Work issuance
-    pow_issuance = (
-        daily_pow_issuance / constants.epochs_per_day
-        if Stage(stage) in [Stage.BEACON_CHAIN, Stage.EIP1559]
-        else 0
-    )
-    network_issuance += pow_issuance * dt
-
-    return {
-        "network_issuance": network_issuance,
-        "pow_issuance": pow_issuance,
-    }
-
-
-
-def policy_upgrade_stages(params, substep, state_history, previous_state) -
-
-

Upgrade Stages Policy

-

Transitions the model from one stage in the Ethereum network -upgrade process to the next at different milestones.

-

This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine

-
- -Expand source code - -
def policy_upgrade_stages(params, substep, state_history, previous_state):
-    """
-    ## Upgrade Stages Policy
-
-    Transitions the model from one stage in the Ethereum network
-    upgrade process to the next at different milestones.
-
-    This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine
-    """
-
-    # Parameters
-    dt = params["dt"]
-    stage: Stage = params["stage"]
-    date_start = params["date_start"]
-    date_eip1559 = params["date_eip1559"]
-    date_pos = params["date_pos"]
-
-    # State Variables
-    current_stage = previous_state["stage"]
-    timestep = previous_state["timestep"]
-
-    # Calculate current timestamp from timestep
-    timestamp = date_start + datetime.timedelta(
-        days=(timestep * dt / constants.epochs_per_day)
-    )
-
-    # Initialize stage State Variable at start of simulation
-    if current_stage is None:
-        current_stage = stage
-    else:
-        # Convert Stage enum value (int) to Stage enum
-        current_stage = Stage(current_stage)
-
-    # Stage finite-state machine
-    if stage == Stage.ALL:
-        # If Stage ALL selected, transition through all stages
-        # at different timestamps
-        if (
-            current_stage in [Stage.ALL, Stage.BEACON_CHAIN]
-            and timestamp < date_eip1559
-        ):
-            current_stage = Stage.BEACON_CHAIN
-        elif (
-            current_stage in [Stage.BEACON_CHAIN, Stage.EIP1559]
-            and timestamp < date_pos
-        ):
-            current_stage = Stage.EIP1559
-        else:
-            current_stage = Stage.PROOF_OF_STAKE
-    elif stage == Stage.BEACON_CHAIN:
-        # If Stage BEACON_CHAIN selected, only execute single stage
-        current_stage = Stage.BEACON_CHAIN
-    elif stage == Stage.EIP1559:
-        # If Stage EIP1559 selected, only execute single stage
-        current_stage = Stage.EIP1559
-    elif stage == Stage.PROOF_OF_STAKE:
-        # If Stage PROOF_OF_STAKE selected, only execute single stage
-        current_stage = Stage.PROOF_OF_STAKE
-    else:
-        # Else, raise exception if invalid Stage
-        raise Exception("Invalid Stage selected")
-
-    return {
-        "stage": current_stage.value,
-        "timestamp": timestamp,
-    }
-
-
-
-def update_eth_price(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

ETH Price State Update Function

-

Update the ETH price from the eth_price_process.

-
- -Expand source code - -
def update_eth_price(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, USD_per_ETH]:
-    """
-    ## ETH Price State Update Function
-
-    Update the ETH price from the `eth_price_process`.
-    """
-
-    # Parameters
-    dt = params["dt"]
-    eth_price_process = params["eth_price_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-
-    # Get the ETH price sample for the current run and timestep
-    eth_price_sample = eth_price_process(run, timestep * dt)
-
-    return "eth_price", eth_price_sample
-
-
-
-def update_eth_supply(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

ETH Supply State Update Function

-

Update the ETH supply from the Network Issuance Policy Function.

-
- -Expand source code - -
def update_eth_supply(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, ETH]:
-    """
-    ## ETH Supply State Update Function
-
-    Update the ETH supply from the Network Issuance Policy Function.
-    """
-
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # State variables
-    eth_supply = previous_state["eth_supply"]
-
-    return "eth_supply", eth_supply + network_issuance
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/incentives.html b/docs/model/parts/incentives.html deleted file mode 100644 index cb60cc2c..00000000 --- a/docs/model/parts/incentives.html +++ /dev/null @@ -1,888 +0,0 @@ - - - - - - -model.parts.incentives API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.incentives

-
-
-

Proof of Stake Incentives

-
    -
  • Calculation of PoS attestation and block proposal rewards and penalties
  • -
  • Calculation of PoS slashing penalties
  • -
-
- -Expand source code - -
"""
-# Proof of Stake Incentives
-
-* Calculation of PoS attestation and block proposal rewards and penalties
-* Calculation of PoS slashing penalties
-"""
-
-import typing
-
-from model.types import Gwei
-import model.parts.spec as spec
-
-
-def policy_attestation_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Attestation Rewards Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    reward_numerator = base_reward * weight * unslashed_participating_increments
-    rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total source reward
-    # All submitted attestations have to match source vote
-    source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    source_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    source_reward *= number_of_validators_online
-
-    # Calculate total target reward
-    target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    target_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    target_reward *= number_of_validators_online
-
-    # Calculate total head reward
-    head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    head_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    head_reward *= number_of_validators_online
-
-    return {
-        "source_reward": source_reward,
-        "target_reward": target_reward,
-        "head_reward": head_reward,
-    }
-
-
-def policy_attestation_penalties(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Attestation Penalties Policy Function
-    Validators are penalized for not attesting to the source, target, and head.
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators_offline = previous_state["number_of_validators_offline"]
-
-    # Calculate validating penalties
-    validating_penalties = (
-        (TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT)
-        / WEIGHT_DENOMINATOR
-        * base_reward
-    )
-    # Aggregation over all offline validators
-    validating_penalties *= number_of_validators_offline
-
-    return {"validating_penalties": validating_penalties}
-
-
-def policy_sync_committee_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Sync Committee Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing
-
-    Extract from spec:
-    ```python
-    # Compute participant and proposer rewards
-    total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-    total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-    max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-    participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-    proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-    ```
-    """
-
-    # Parameters
-    SYNC_REWARD_WEIGHT = params["SYNC_REWARD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total base rewards
-    total_base_rewards = base_reward * number_of_validators_online
-    # Set sync reward to proportion of total base rewards
-    sync_reward = total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR
-    # Scale reward by the percentage of online validators
-    sync_reward *= number_of_validators_online / number_of_validators
-
-    return {"sync_reward": sync_reward}
-
-
-def policy_block_proposal_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Block Proposal Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-process_attestation
-
-    Extract from spec:
-    ```python
-    # Participation flag indices
-    participation_flag_indices = []
-    if is_matching_head and is_matching_target and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
-        participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
-    if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
-        participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
-    if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
-        participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
-
-    # Update epoch participation flags
-    proposer_reward_numerator = 0
-    for index in get_attesting_indices(state, data, attestation.aggregation_bits):
-        for flag_index, weight in get_flag_indices_and_weights():
-            if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
-                epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
-                proposer_reward_numerator += get_base_reward(state, index) * weight
-
-    # Reward proposer
-    proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
-    increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
-    ```
-    """
-
-    # Parameters
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    sync_reward = previous_state["sync_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate block proposer reward
-    proposer_reward_numerator = base_reward * (
-        TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT
-    )
-    # Aggregate over all attestations in the epoch
-    # Assumes every online validator gets one correct source, target, and head vote per epoch
-    proposer_reward_numerator *= number_of_validators_online
-    # Normalize by the sum of weights so that proposer rewards are 1/8th of base reward
-    proposer_reward_denominator = (
-        (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    )
-    block_proposer_reward = Gwei(
-        proposer_reward_numerator // proposer_reward_denominator
-    )
-
-    # Add block proposer reward for including sync committee attestations
-    # See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing
-    block_proposer_reward += (
-        sync_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)
-    )
-
-    return {"block_proposer_reward": block_proposer_reward}
-
-
-def policy_slashing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Slashing Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-slash_validator
-
-    Extract from spec:
-    ```python
-    state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
-    decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
-
-    # Apply proposer and whistleblower rewards
-    proposer_index = get_beacon_proposer_index(state)
-    if whistleblower_index is None:
-        whistleblower_index = proposer_index
-    whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    increase_balance(state, proposer_index, proposer_reward)
-    increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
-    ```
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#slashings
-
-    Extract from spec:
-    ```python
-    def process_slashings(state: BeaconState) -> None:
-        epoch = get_current_epoch(state)
-        total_balance = get_total_active_balance(state)
-        adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
-        for index, validator in enumerate(state.validators):
-            if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
-                increment = EFFECTIVE_BALANCE_INCREMENT  # Factored out from penalty numerator to avoid uint64 overflow
-                penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
-                penalty = penalty_numerator // total_balance * increment
-                decrease_balance(state, ValidatorIndex(index), penalty)
-    ```
-    """
-    # Parameters
-    dt = params["dt"]
-    slashing_events_per_1000_epochs = params["slashing_events_per_1000_epochs"]
-    MIN_SLASHING_PENALTY_QUOTIENT = params["MIN_SLASHING_PENALTY_QUOTIENT"]
-    PROPORTIONAL_SLASHING_MULTIPLIER = params["PROPORTIONAL_SLASHING_MULTIPLIER"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    WHISTLEBLOWER_REWARD_QUOTIENT = params["WHISTLEBLOWER_REWARD_QUOTIENT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate slashing, whistleblower reward, and proposer reward for a single slashing event
-    slashing = Gwei(average_effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
-    whistleblower_reward = Gwei(
-        average_effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT
-    )
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    whistleblower_reward = Gwei(whistleblower_reward - proposer_reward)
-
-    # Calculate number of slashing events for current epoch
-    number_of_slashing_events = slashing_events_per_1000_epochs / 1000
-
-    # Calculate the individual penalty proportional to total slashings
-    # in current time period using `PROPORTIONAL_SLASHING_MULTIPLIER`
-    total_balance = spec.get_total_active_balance(params, previous_state)
-    adjusted_total_slashing_balance = min(
-        slashing * number_of_slashing_events * PROPORTIONAL_SLASHING_MULTIPLIER,
-        total_balance
-    )
-    increment = EFFECTIVE_BALANCE_INCREMENT
-    penalty_numerator = average_effective_balance // increment * adjusted_total_slashing_balance
-    proportional_penalty = penalty_numerator // total_balance * increment
-    
-    # Scale penalty by the number of slashing events per epoch
-    amount_slashed = (slashing + proportional_penalty) * number_of_slashing_events
-    # Scale rewards by the number of slashing events per epoch
-    whistleblower_reward *= number_of_slashing_events
-    proposer_reward *= number_of_slashing_events
-
-    # The whistleblower and the block proposer who includes the slashing receive a reward
-    whistleblower_rewards = whistleblower_reward + proposer_reward
-
-    return {
-        "amount_slashed": amount_slashed * dt,
-        "whistleblower_rewards": whistleblower_rewards * dt,
-    }
-
-
-def update_base_reward(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """Base Reward State Update Function
-    Calculate and update base reward per validator
-    """
-    # Parameters
-    dt = params["dt"]
-
-    # Get base reward per validator
-    base_reward_per_validator: Gwei = spec.get_base_reward(params, previous_state)
-
-    # By scaling the base reward by our unit of time dt (in epochs),
-    # we can scale all rewards and penalties by the same unit of time
-    return "base_reward", Gwei(base_reward_per_validator) * dt
-
-
-def update_validating_rewards(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """Validating Rewards State Update Function
-    Calculate and update total validating rewards
-    i.e. rewards received for block proposal, attesting, and being a member of sync committee
-    """
-    # State Variables
-    block_proposer_reward = previous_state["block_proposer_reward"]
-    sync_reward = previous_state["sync_reward"]
-
-    source_reward = previous_state["source_reward"]
-    target_reward = previous_state["target_reward"]
-    head_reward = previous_state["head_reward"]
-
-    base_reward = previous_state["base_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total validating rewards
-    validating_rewards = (
-        block_proposer_reward
-        + source_reward
-        + target_reward
-        + head_reward
-        + sync_reward
-    )
-
-    # Assert validating rewards should be less than equal to the maximum validating rewards
-    max_validating_rewards = number_of_validators_online * base_reward
-    assert validating_rewards <= max_validating_rewards
-
-    return "validating_rewards", validating_rewards
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_attestation_penalties(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Attestation Penalties Policy Function -Validators are penalized for not attesting to the source, target, and head.

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas

-

Extract from spec:

-
penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
-
-
- -Expand source code - -
def policy_attestation_penalties(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Attestation Penalties Policy Function
-    Validators are penalized for not attesting to the source, target, and head.
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators_offline = previous_state["number_of_validators_offline"]
-
-    # Calculate validating penalties
-    validating_penalties = (
-        (TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT)
-        / WEIGHT_DENOMINATOR
-        * base_reward
-    )
-    # Aggregation over all offline validators
-    validating_penalties *= number_of_validators_offline
-
-    return {"validating_penalties": validating_penalties}
-
-
-
-def policy_attestation_rewards(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Attestation Rewards Policy Function -Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas

-

Extract from spec:

-
reward_numerator = base_reward * weight * unslashed_participating_increments
-rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
-
-
- -Expand source code - -
def policy_attestation_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Attestation Rewards Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    reward_numerator = base_reward * weight * unslashed_participating_increments
-    rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total source reward
-    # All submitted attestations have to match source vote
-    source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    source_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    source_reward *= number_of_validators_online
-
-    # Calculate total target reward
-    target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    target_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    target_reward *= number_of_validators_online
-
-    # Calculate total head reward
-    head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    head_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    head_reward *= number_of_validators_online
-
-    return {
-        "source_reward": source_reward,
-        "target_reward": target_reward,
-        "head_reward": head_reward,
-    }
-
-
-
-def policy_block_proposal_reward(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Block Proposal Reward Policy Function -Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-process_attestation

-

Extract from spec:

-
# Participation flag indices
-participation_flag_indices = []
-if is_matching_head and is_matching_target and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
-    participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
-if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
-    participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
-if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
-    participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
-
-# Update epoch participation flags
-proposer_reward_numerator = 0
-for index in get_attesting_indices(state, data, attestation.aggregation_bits):
-    for flag_index, weight in get_flag_indices_and_weights():
-        if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
-            epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
-            proposer_reward_numerator += get_base_reward(state, index) * weight
-
-# Reward proposer
-proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
-increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
-
-
- -Expand source code - -
def policy_block_proposal_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Block Proposal Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-process_attestation
-
-    Extract from spec:
-    ```python
-    # Participation flag indices
-    participation_flag_indices = []
-    if is_matching_head and is_matching_target and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
-        participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
-    if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
-        participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
-    if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
-        participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
-
-    # Update epoch participation flags
-    proposer_reward_numerator = 0
-    for index in get_attesting_indices(state, data, attestation.aggregation_bits):
-        for flag_index, weight in get_flag_indices_and_weights():
-            if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
-                epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
-                proposer_reward_numerator += get_base_reward(state, index) * weight
-
-    # Reward proposer
-    proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
-    increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
-    ```
-    """
-
-    # Parameters
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    sync_reward = previous_state["sync_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate block proposer reward
-    proposer_reward_numerator = base_reward * (
-        TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT
-    )
-    # Aggregate over all attestations in the epoch
-    # Assumes every online validator gets one correct source, target, and head vote per epoch
-    proposer_reward_numerator *= number_of_validators_online
-    # Normalize by the sum of weights so that proposer rewards are 1/8th of base reward
-    proposer_reward_denominator = (
-        (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    )
-    block_proposer_reward = Gwei(
-        proposer_reward_numerator // proposer_reward_denominator
-    )
-
-    # Add block proposer reward for including sync committee attestations
-    # See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing
-    block_proposer_reward += (
-        sync_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)
-    )
-
-    return {"block_proposer_reward": block_proposer_reward}
-
-
-
-def policy_slashing(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Slashing Policy Function -Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-slash_validator

-

Extract from spec:

-
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
-decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
-
-# Apply proposer and whistleblower rewards
-proposer_index = get_beacon_proposer_index(state)
-if whistleblower_index is None:
-    whistleblower_index = proposer_index
-whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
-proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-increase_balance(state, proposer_index, proposer_reward)
-increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
-
-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#slashings

-

Extract from spec:

-
def process_slashings(state: BeaconState) -> None:
-    epoch = get_current_epoch(state)
-    total_balance = get_total_active_balance(state)
-    adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
-    for index, validator in enumerate(state.validators):
-        if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
-            increment = EFFECTIVE_BALANCE_INCREMENT  # Factored out from penalty numerator to avoid uint64 overflow
-            penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
-            penalty = penalty_numerator // total_balance * increment
-            decrease_balance(state, ValidatorIndex(index), penalty)
-
-
- -Expand source code - -
def policy_slashing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Slashing Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-slash_validator
-
-    Extract from spec:
-    ```python
-    state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
-    decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
-
-    # Apply proposer and whistleblower rewards
-    proposer_index = get_beacon_proposer_index(state)
-    if whistleblower_index is None:
-        whistleblower_index = proposer_index
-    whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    increase_balance(state, proposer_index, proposer_reward)
-    increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
-    ```
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#slashings
-
-    Extract from spec:
-    ```python
-    def process_slashings(state: BeaconState) -> None:
-        epoch = get_current_epoch(state)
-        total_balance = get_total_active_balance(state)
-        adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
-        for index, validator in enumerate(state.validators):
-            if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
-                increment = EFFECTIVE_BALANCE_INCREMENT  # Factored out from penalty numerator to avoid uint64 overflow
-                penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
-                penalty = penalty_numerator // total_balance * increment
-                decrease_balance(state, ValidatorIndex(index), penalty)
-    ```
-    """
-    # Parameters
-    dt = params["dt"]
-    slashing_events_per_1000_epochs = params["slashing_events_per_1000_epochs"]
-    MIN_SLASHING_PENALTY_QUOTIENT = params["MIN_SLASHING_PENALTY_QUOTIENT"]
-    PROPORTIONAL_SLASHING_MULTIPLIER = params["PROPORTIONAL_SLASHING_MULTIPLIER"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    WHISTLEBLOWER_REWARD_QUOTIENT = params["WHISTLEBLOWER_REWARD_QUOTIENT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate slashing, whistleblower reward, and proposer reward for a single slashing event
-    slashing = Gwei(average_effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
-    whistleblower_reward = Gwei(
-        average_effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT
-    )
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    whistleblower_reward = Gwei(whistleblower_reward - proposer_reward)
-
-    # Calculate number of slashing events for current epoch
-    number_of_slashing_events = slashing_events_per_1000_epochs / 1000
-
-    # Calculate the individual penalty proportional to total slashings
-    # in current time period using `PROPORTIONAL_SLASHING_MULTIPLIER`
-    total_balance = spec.get_total_active_balance(params, previous_state)
-    adjusted_total_slashing_balance = min(
-        slashing * number_of_slashing_events * PROPORTIONAL_SLASHING_MULTIPLIER,
-        total_balance
-    )
-    increment = EFFECTIVE_BALANCE_INCREMENT
-    penalty_numerator = average_effective_balance // increment * adjusted_total_slashing_balance
-    proportional_penalty = penalty_numerator // total_balance * increment
-    
-    # Scale penalty by the number of slashing events per epoch
-    amount_slashed = (slashing + proportional_penalty) * number_of_slashing_events
-    # Scale rewards by the number of slashing events per epoch
-    whistleblower_reward *= number_of_slashing_events
-    proposer_reward *= number_of_slashing_events
-
-    # The whistleblower and the block proposer who includes the slashing receive a reward
-    whistleblower_rewards = whistleblower_reward + proposer_reward
-
-    return {
-        "amount_slashed": amount_slashed * dt,
-        "whistleblower_rewards": whistleblower_rewards * dt,
-    }
-
-
-
-def policy_sync_committee_reward(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Sync Committee Reward Policy Function -Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing

-

Extract from spec:

-
# Compute participant and proposer rewards
-total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-
-
- -Expand source code - -
def policy_sync_committee_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """Sync Committee Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing
-
-    Extract from spec:
-    ```python
-    # Compute participant and proposer rewards
-    total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-    total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-    max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-    participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-    proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-    ```
-    """
-
-    # Parameters
-    SYNC_REWARD_WEIGHT = params["SYNC_REWARD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total base rewards
-    total_base_rewards = base_reward * number_of_validators_online
-    # Set sync reward to proportion of total base rewards
-    sync_reward = total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR
-    # Scale reward by the percentage of online validators
-    sync_reward *= number_of_validators_online / number_of_validators
-
-    return {"sync_reward": sync_reward}
-
-
-
-def update_base_reward(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

Base Reward State Update Function -Calculate and update base reward per validator

-
- -Expand source code - -
def update_base_reward(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """Base Reward State Update Function
-    Calculate and update base reward per validator
-    """
-    # Parameters
-    dt = params["dt"]
-
-    # Get base reward per validator
-    base_reward_per_validator: Gwei = spec.get_base_reward(params, previous_state)
-
-    # By scaling the base reward by our unit of time dt (in epochs),
-    # we can scale all rewards and penalties by the same unit of time
-    return "base_reward", Gwei(base_reward_per_validator) * dt
-
-
-
-def update_validating_rewards(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

Validating Rewards State Update Function -Calculate and update total validating rewards -i.e. rewards received for block proposal, attesting, and being a member of sync committee

-
- -Expand source code - -
def update_validating_rewards(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """Validating Rewards State Update Function
-    Calculate and update total validating rewards
-    i.e. rewards received for block proposal, attesting, and being a member of sync committee
-    """
-    # State Variables
-    block_proposer_reward = previous_state["block_proposer_reward"]
-    sync_reward = previous_state["sync_reward"]
-
-    source_reward = previous_state["source_reward"]
-    target_reward = previous_state["target_reward"]
-    head_reward = previous_state["head_reward"]
-
-    base_reward = previous_state["base_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total validating rewards
-    validating_rewards = (
-        block_proposer_reward
-        + source_reward
-        + target_reward
-        + head_reward
-        + sync_reward
-    )
-
-    # Assert validating rewards should be less than equal to the maximum validating rewards
-    max_validating_rewards = number_of_validators_online * base_reward
-    assert validating_rewards <= max_validating_rewards
-
-    return "validating_rewards", validating_rewards
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/index.html b/docs/model/parts/index.html deleted file mode 100644 index 9269ab5a..00000000 --- a/docs/model/parts/index.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - -model.parts API documentation - - - - - - - - - - - -
-
-
-

Namespace model.parts

-
-
-
-
-

Sub-modules

-
-
model.parts.ethereum_system
-
-

Ethereum System …

-
-
model.parts.pos_incentives
-
-

Proof of Stake Incentives …

-
-
model.parts.system_metrics
-
-

System Metrics …

-
-
model.parts.utils
-
-
-
-
model.parts.validators
-
-

Validator Mechanisms …

-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/metrics.html b/docs/model/parts/metrics.html deleted file mode 100644 index 601c303c..00000000 --- a/docs/model/parts/metrics.html +++ /dev/null @@ -1,443 +0,0 @@ - - - - - - -model.parts.metrics API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.metrics

-
-
-

System Metrics

-
    -
  • Calculation of validator operational costs
  • -
  • Calculation of validator revenue, profit, and yield metrics
  • -
-
- -Expand source code - -
"""
-# System Metrics
-
-* Calculation of validator operational costs
-* Calculation of validator revenue, profit, and yield metrics
-"""
-
-import typing
-
-import model.constants as constants
-from model.types import Percentage, Gwei
-
-
-def policy_validator_costs(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-    validator_hardware_costs_per_epoch = params["validator_hardware_costs_per_epoch"]
-    validator_cloud_costs_per_epoch = params["validator_cloud_costs_per_epoch"]
-    validator_third_party_costs_per_epoch = params[
-        "validator_third_party_costs_per_epoch"
-    ]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    number_of_validators = previous_state["number_of_validators"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate hardware, cloud, and third-party costs per validator type
-    validator_count_distribution = (
-        number_of_validators * validator_percentage_distribution
-    )
-
-    validator_hardware_costs = (
-        validator_count_distribution * validator_hardware_costs_per_epoch * dt
-    )
-
-    validator_cloud_costs = (
-        validator_count_distribution * validator_cloud_costs_per_epoch * dt
-    )
-
-    validator_third_party_costs = (
-        validator_percentage_distribution
-        * validator_third_party_costs_per_epoch  # % of total
-        * total_online_validator_rewards
-    )
-    validator_third_party_costs /= constants.gwei  # Convert from Gwei to ETH
-    validator_third_party_costs *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate total validator costs per validator type and total network costs
-    validator_costs = (
-        validator_hardware_costs + validator_cloud_costs + validator_third_party_costs
-    )
-    total_network_costs = validator_costs.sum(axis=0)
-
-    return {
-        "validator_count_distribution": validator_count_distribution,
-        "validator_hardware_costs": validator_hardware_costs,
-        "validator_cloud_costs": validator_cloud_costs,
-        "validator_third_party_costs": validator_third_party_costs,
-        "validator_costs": validator_costs,
-        "total_network_costs": total_network_costs,
-    }
-
-
-def policy_validator_yields(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    eth_staked = previous_state["eth_staked"]
-    validator_costs = previous_state["validator_costs"]
-    total_network_costs = previous_state["total_network_costs"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-    validator_count_distribution = previous_state["validator_count_distribution"]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate ETH staked per validator type
-    validator_eth_staked = validator_count_distribution * average_effective_balance
-    validator_eth_staked /= constants.gwei  # Convert from Gwei to ETH
-
-    # Calculate the revenue per validator type
-    validator_revenue = (
-        validator_percentage_distribution * total_online_validator_rewards
-    )
-    validator_revenue /= constants.gwei  # Convert from Gwei to ETH
-    validator_revenue *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate the profit per validator type
-    validator_profit = validator_revenue - validator_costs
-
-    # Calculate the revenue yields per validator type
-    validator_revenue_yields = validator_revenue / (validator_eth_staked * eth_price)
-    validator_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the profit yields per validator type
-    validator_profit_yields = validator_profit / (validator_eth_staked * eth_price)
-    validator_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network revenue
-    total_revenue = validator_revenue.sum(axis=0)
-
-    # Calculate the total network profit
-    total_profit = total_revenue - total_network_costs
-
-    # Calculate the total network revenue yields
-    total_revenue_yields = total_revenue / (eth_staked * eth_price)
-    total_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network profit yields
-    total_profit_yields = total_profit / (eth_staked * eth_price)
-    total_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    return {
-        # Per validator type
-        "validator_eth_staked": validator_eth_staked,
-        "validator_revenue": validator_revenue,
-        "validator_profit": validator_profit,
-        "validator_revenue_yields": validator_revenue_yields,
-        "validator_profit_yields": validator_profit_yields,
-        # Aggregate
-        "total_revenue": total_revenue,
-        "total_profit": total_profit,
-        "total_revenue_yields": total_revenue_yields,
-        "total_profit_yields": total_profit_yields,
-    }
-
-
-def policy_total_online_validator_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    # State Variables
-    validating_rewards = previous_state["validating_rewards"]
-    whistleblower_rewards = previous_state["whistleblower_rewards"]
-    validating_penalties = previous_state["validating_penalties"]
-    total_priority_fee_to_validators = previous_state["total_priority_fee_to_validators"]
-
-    # Calculate total rewards for online validators
-    total_online_validator_rewards = (
-        validating_rewards
-        + whistleblower_rewards
-        - validating_penalties
-        + total_priority_fee_to_validators
-    )
-
-    return {"total_online_validator_rewards": total_online_validator_rewards}
-
-
-def update_supply_inflation(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Percentage]:
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # Parameters
-    dt = params["dt"]
-
-    # State Variables
-    eth_supply = previous_state["eth_supply"]
-
-    # Calculate the ETH supply inflation
-    supply_inflation = network_issuance / eth_supply
-    supply_inflation *= constants.epochs_per_year / dt  # Annualize value
-
-    return "supply_inflation", supply_inflation
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_total_online_validator_rewards(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-
-
- -Expand source code - -
def policy_total_online_validator_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    # State Variables
-    validating_rewards = previous_state["validating_rewards"]
-    whistleblower_rewards = previous_state["whistleblower_rewards"]
-    validating_penalties = previous_state["validating_penalties"]
-    total_priority_fee_to_validators = previous_state["total_priority_fee_to_validators"]
-
-    # Calculate total rewards for online validators
-    total_online_validator_rewards = (
-        validating_rewards
-        + whistleblower_rewards
-        - validating_penalties
-        + total_priority_fee_to_validators
-    )
-
-    return {"total_online_validator_rewards": total_online_validator_rewards}
-
-
-
-def policy_validator_costs(params, substep, state_history, previous_state) ‑> Dict[str, ] -
-
-
-
- -Expand source code - -
def policy_validator_costs(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-    validator_hardware_costs_per_epoch = params["validator_hardware_costs_per_epoch"]
-    validator_cloud_costs_per_epoch = params["validator_cloud_costs_per_epoch"]
-    validator_third_party_costs_per_epoch = params[
-        "validator_third_party_costs_per_epoch"
-    ]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    number_of_validators = previous_state["number_of_validators"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate hardware, cloud, and third-party costs per validator type
-    validator_count_distribution = (
-        number_of_validators * validator_percentage_distribution
-    )
-
-    validator_hardware_costs = (
-        validator_count_distribution * validator_hardware_costs_per_epoch * dt
-    )
-
-    validator_cloud_costs = (
-        validator_count_distribution * validator_cloud_costs_per_epoch * dt
-    )
-
-    validator_third_party_costs = (
-        validator_percentage_distribution
-        * validator_third_party_costs_per_epoch  # % of total
-        * total_online_validator_rewards
-    )
-    validator_third_party_costs /= constants.gwei  # Convert from Gwei to ETH
-    validator_third_party_costs *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate total validator costs per validator type and total network costs
-    validator_costs = (
-        validator_hardware_costs + validator_cloud_costs + validator_third_party_costs
-    )
-    total_network_costs = validator_costs.sum(axis=0)
-
-    return {
-        "validator_count_distribution": validator_count_distribution,
-        "validator_hardware_costs": validator_hardware_costs,
-        "validator_cloud_costs": validator_cloud_costs,
-        "validator_third_party_costs": validator_third_party_costs,
-        "validator_costs": validator_costs,
-        "total_network_costs": total_network_costs,
-    }
-
-
-
-def policy_validator_yields(params, substep, state_history, previous_state) ‑> Dict[str, ] -
-
-
-
- -Expand source code - -
def policy_validator_yields(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    eth_staked = previous_state["eth_staked"]
-    validator_costs = previous_state["validator_costs"]
-    total_network_costs = previous_state["total_network_costs"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-    validator_count_distribution = previous_state["validator_count_distribution"]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate ETH staked per validator type
-    validator_eth_staked = validator_count_distribution * average_effective_balance
-    validator_eth_staked /= constants.gwei  # Convert from Gwei to ETH
-
-    # Calculate the revenue per validator type
-    validator_revenue = (
-        validator_percentage_distribution * total_online_validator_rewards
-    )
-    validator_revenue /= constants.gwei  # Convert from Gwei to ETH
-    validator_revenue *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate the profit per validator type
-    validator_profit = validator_revenue - validator_costs
-
-    # Calculate the revenue yields per validator type
-    validator_revenue_yields = validator_revenue / (validator_eth_staked * eth_price)
-    validator_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the profit yields per validator type
-    validator_profit_yields = validator_profit / (validator_eth_staked * eth_price)
-    validator_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network revenue
-    total_revenue = validator_revenue.sum(axis=0)
-
-    # Calculate the total network profit
-    total_profit = total_revenue - total_network_costs
-
-    # Calculate the total network revenue yields
-    total_revenue_yields = total_revenue / (eth_staked * eth_price)
-    total_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network profit yields
-    total_profit_yields = total_profit / (eth_staked * eth_price)
-    total_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    return {
-        # Per validator type
-        "validator_eth_staked": validator_eth_staked,
-        "validator_revenue": validator_revenue,
-        "validator_profit": validator_profit,
-        "validator_revenue_yields": validator_revenue_yields,
-        "validator_profit_yields": validator_profit_yields,
-        # Aggregate
-        "total_revenue": total_revenue,
-        "total_profit": total_profit,
-        "total_revenue_yields": total_revenue_yields,
-        "total_profit_yields": total_profit_yields,
-    }
-
-
-
-def update_supply_inflation(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-
-
- -Expand source code - -
def update_supply_inflation(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Percentage]:
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # Parameters
-    dt = params["dt"]
-
-    # State Variables
-    eth_supply = previous_state["eth_supply"]
-
-    # Calculate the ETH supply inflation
-    supply_inflation = network_issuance / eth_supply
-    supply_inflation *= constants.epochs_per_year / dt  # Annualize value
-
-    return "supply_inflation", supply_inflation
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/phases.html b/docs/model/parts/phases.html deleted file mode 100644 index 99edd2c6..00000000 --- a/docs/model/parts/phases.html +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - -model.parts.phases API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.phases

-
-
-

Ethereum Upgrade Phases -The Phases module contains the logic for transitioning the model -from one phase in the Ethereum network upgrade process to the next at different milestones.

-
- -Expand source code - -
"""Ethereum Upgrade Phases
-The Phases module contains the logic for transitioning the model
-from one phase in the Ethereum network upgrade process to the next at different milestones.
-"""
-
-import datetime
-
-import experiments.simulation_configuration as simulation
-import model.constants as constants
-from model.types import Phase
-
-
-def policy_phases(params, substep, state_history, previous_state):
-    """Phases Policy
-    Transitions the model from one phase in the Ethereum network
-    upgrade process to the next at different milestones.
-
-    This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine
-    """
-    # Parameters
-    dt = params["dt"]
-    phase: Phase = params["phase"]
-    date_start = params["date_start"]
-    date_eip1559 = params["date_eip1559"]
-    date_merge = params["date_merge"]
-
-    # State Variables
-    current_phase = previous_state["phase"]
-    timestep = previous_state["timestep"]
-
-    # Calculate current timestamp from timestep
-    timestamp = date_start + datetime.timedelta(
-        days=(timestep * dt / constants.epochs_per_day)
-    )
-
-    # Initialize phase State Variable at start of simulation
-    if current_phase == None:
-        current_phase = phase
-    else:
-        # Convert Phase enum value (int) to Phase enum
-        current_phase = Phase(current_phase)
-
-    # Phase finite-state machine
-    if phase == Phase.ALL:
-        # If Phase ALL selected, transition through all phases
-        # at different timestamps
-        if current_phase in [Phase.ALL, Phase.PHASE_0] and timestamp < date_eip1559:
-            current_phase = Phase.PHASE_0
-        elif (
-            current_phase in [Phase.PHASE_0, Phase.POST_EIP1559]
-            and timestamp < date_merge
-        ):
-            current_phase = Phase.POST_EIP1559
-        else:
-            current_phase = Phase.POST_MERGE
-    elif phase == Phase.PHASE_0:
-        # If Phase PHASE_0 selected, only execute single phase
-        current_phase = Phase.PHASE_0
-    elif phase == Phase.POST_EIP1559:
-        # If Phase POST_EIP1559 selected, only execute single phase
-        current_phase = Phase.POST_EIP1559
-    elif phase == Phase.POST_MERGE:
-        # If Phase POST_MERGE selected, only execute single phase
-        current_phase = Phase.POST_MERGE
-    else:
-        # Else, raise exception if invalid Phase
-        raise Exception("Invalid Phase selected")
-
-    return {
-        "phase": current_phase.value,
-        "timestamp": timestamp,
-    }
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_phases(params, substep, state_history, previous_state) -
-
-

Phases Policy -Transitions the model from one phase in the Ethereum network -upgrade process to the next at different milestones.

-

This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine

-
- -Expand source code - -
def policy_phases(params, substep, state_history, previous_state):
-    """Phases Policy
-    Transitions the model from one phase in the Ethereum network
-    upgrade process to the next at different milestones.
-
-    This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine
-    """
-    # Parameters
-    dt = params["dt"]
-    phase: Phase = params["phase"]
-    date_start = params["date_start"]
-    date_eip1559 = params["date_eip1559"]
-    date_merge = params["date_merge"]
-
-    # State Variables
-    current_phase = previous_state["phase"]
-    timestep = previous_state["timestep"]
-
-    # Calculate current timestamp from timestep
-    timestamp = date_start + datetime.timedelta(
-        days=(timestep * dt / constants.epochs_per_day)
-    )
-
-    # Initialize phase State Variable at start of simulation
-    if current_phase == None:
-        current_phase = phase
-    else:
-        # Convert Phase enum value (int) to Phase enum
-        current_phase = Phase(current_phase)
-
-    # Phase finite-state machine
-    if phase == Phase.ALL:
-        # If Phase ALL selected, transition through all phases
-        # at different timestamps
-        if current_phase in [Phase.ALL, Phase.PHASE_0] and timestamp < date_eip1559:
-            current_phase = Phase.PHASE_0
-        elif (
-            current_phase in [Phase.PHASE_0, Phase.POST_EIP1559]
-            and timestamp < date_merge
-        ):
-            current_phase = Phase.POST_EIP1559
-        else:
-            current_phase = Phase.POST_MERGE
-    elif phase == Phase.PHASE_0:
-        # If Phase PHASE_0 selected, only execute single phase
-        current_phase = Phase.PHASE_0
-    elif phase == Phase.POST_EIP1559:
-        # If Phase POST_EIP1559 selected, only execute single phase
-        current_phase = Phase.POST_EIP1559
-    elif phase == Phase.POST_MERGE:
-        # If Phase POST_MERGE selected, only execute single phase
-        current_phase = Phase.POST_MERGE
-    else:
-        # Else, raise exception if invalid Phase
-        raise Exception("Invalid Phase selected")
-
-    return {
-        "phase": current_phase.value,
-        "timestamp": timestamp,
-    }
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/pos_incentives.html b/docs/model/parts/pos_incentives.html deleted file mode 100644 index e9f6757e..00000000 --- a/docs/model/parts/pos_incentives.html +++ /dev/null @@ -1,1047 +0,0 @@ - - - - - - -model.parts.pos_incentives API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.pos_incentives

-
-
-

Proof of Stake Incentives

-

Calculation of PoS incentives such as attestation and block proposal rewards and penalties.

-
- -Expand source code - -
"""
-# Proof of Stake Incentives
-
-Calculation of PoS incentives such as attestation and block proposal rewards and penalties.
-"""
-
-import typing
-
-import model.parts.utils.ethereum_spec as spec
-from model.types import Gwei
-
-
-def policy_attestation_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Attestation Rewards Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    reward_numerator = base_reward * weight * unslashed_participating_increments
-    rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total source reward
-    # All submitted attestations have to match source vote
-    source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    source_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    source_reward *= number_of_validators_online
-
-    # Calculate total target reward
-    target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    target_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    target_reward *= number_of_validators_online
-
-    # Calculate total head reward
-    head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    head_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    head_reward *= number_of_validators_online
-
-    return {
-        "source_reward": source_reward,
-        "target_reward": target_reward,
-        "head_reward": head_reward,
-    }
-
-
-def policy_attestation_penalties(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Attestation Penalties Policy Function
-    Validators are penalized for not attesting to the source, target, and head.
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators_offline = previous_state["number_of_validators_offline"]
-
-    # Calculate attestation penalties
-    attestation_penalties = (
-        (TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT)
-        / WEIGHT_DENOMINATOR
-        * base_reward
-    )
-    # Aggregation over all offline validators
-    attestation_penalties *= number_of_validators_offline
-
-    return {"attestation_penalties": attestation_penalties}
-
-
-def policy_sync_committee_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Sync Committee Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-aggregate-processing
-
-    Extract from spec:
-    ```python
-    # Compute participant and proposer rewards
-    total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-    total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-    max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-    participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-    proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-    ```
-    """
-
-    # Parameters
-    SYNC_REWARD_WEIGHT = params["SYNC_REWARD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total base rewards
-    total_base_rewards = base_reward * number_of_validators
-    # Set sync reward to proportion of total base rewards
-    sync_reward = total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR
-    # Scale reward by the percentage of online validators
-    sync_reward *= number_of_validators_online / number_of_validators
-
-    return {"sync_reward": sync_reward}
-
-
-def policy_sync_committee_penalties(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Sync Committee Penalty Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-aggregate-processing
-
-    Extract from spec:
-    ```python
-    # Compute participant and proposer rewards
-    total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-    total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-    max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-    participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-    proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-    ```
-    """
-
-    # Parameters
-    SYNC_REWARD_WEIGHT = params["SYNC_REWARD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_offline = previous_state["number_of_validators_offline"]
-
-    # Calculate total base rewards
-    total_base_rewards = base_reward * number_of_validators
-    # Set sync penalty to proportion of total base rewards
-    sync_penalty = total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR
-    # Scale penalty by the percentage of offline validators
-    sync_penalty *= number_of_validators_offline / number_of_validators
-
-    return {"sync_committee_penalties": sync_penalty}
-
-
-def policy_block_proposal_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Block Proposal Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-process_attestation
-
-    Extract from spec:
-    ```python
-    # Participation flag indices
-    participation_flag_indices = []
-    if is_matching_head and is_matching_target and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
-        participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
-    if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
-        participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
-    if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
-        participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
-
-    # Update epoch participation flags
-    proposer_reward_numerator = 0
-    for index in get_attesting_indices(state, data, attestation.aggregation_bits):
-        for flag_index, weight in get_flag_indices_and_weights():
-            if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
-                epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
-                proposer_reward_numerator += get_base_reward(state, index) * weight
-
-    # Reward proposer
-    proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
-    increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
-    ```
-    """
-
-    # Parameters
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    sync_reward = previous_state["sync_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate block proposer reward
-    proposer_reward_numerator = base_reward * (
-        TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT
-    )
-    # Aggregate over all attestations in the epoch
-    # Assumes every online validator gets one correct source, target, and head vote per epoch
-    proposer_reward_numerator *= number_of_validators_online
-    # Normalize by the sum of weights so that proposer rewards are 1/8th of base reward
-    proposer_reward_denominator = (
-        (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    )
-    block_proposer_reward = Gwei(
-        proposer_reward_numerator // proposer_reward_denominator
-    )
-
-    # Add block proposer reward for including sync committee attestations
-    # See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing
-    block_proposer_reward += (
-        sync_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)
-    )
-
-    return {"block_proposer_reward": block_proposer_reward}
-
-
-def policy_slashing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Slashing Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-slash_validator
-
-    Extract from spec:
-    ```python
-    state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
-    decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
-
-    # Apply proposer and whistleblower rewards
-    proposer_index = get_beacon_proposer_index(state)
-    if whistleblower_index is None:
-        whistleblower_index = proposer_index
-    whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    increase_balance(state, proposer_index, proposer_reward)
-    increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
-    ```
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#slashings
-
-    Extract from spec:
-    ```python
-    def process_slashings(state: BeaconState) -> None:
-        epoch = get_current_epoch(state)
-        total_balance = get_total_active_balance(state)
-        adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
-        for index, validator in enumerate(state.validators):
-            if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
-                increment = EFFECTIVE_BALANCE_INCREMENT  # Factored out from penalty numerator to avoid uint64 overflow
-                penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
-                penalty = penalty_numerator // total_balance * increment
-                decrease_balance(state, ValidatorIndex(index), penalty)
-    ```
-    """
-    # Parameters
-    dt = params["dt"]
-    slashing_events_per_1000_epochs = params["slashing_events_per_1000_epochs"]
-    MIN_SLASHING_PENALTY_QUOTIENT = params["MIN_SLASHING_PENALTY_QUOTIENT"]
-    PROPORTIONAL_SLASHING_MULTIPLIER = params["PROPORTIONAL_SLASHING_MULTIPLIER"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    WHISTLEBLOWER_REWARD_QUOTIENT = params["WHISTLEBLOWER_REWARD_QUOTIENT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate slashing, whistleblower reward, and proposer reward for a single slashing event
-    slashing = Gwei(average_effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
-    whistleblower_reward = Gwei(
-        average_effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT
-    )
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    whistleblower_reward = Gwei(whistleblower_reward - proposer_reward)
-
-    # Calculate number of slashing events for current epoch
-    number_of_slashing_events = slashing_events_per_1000_epochs / 1000
-
-    # Calculate the individual penalty proportional to total slashings
-    # in current time period using `PROPORTIONAL_SLASHING_MULTIPLIER`
-    total_balance = spec.get_total_active_balance(params, previous_state)
-    adjusted_total_slashing_balance = min(
-        slashing * number_of_slashing_events * PROPORTIONAL_SLASHING_MULTIPLIER,
-        total_balance,
-    )
-    increment = EFFECTIVE_BALANCE_INCREMENT
-    penalty_numerator = (
-        average_effective_balance // increment * adjusted_total_slashing_balance
-    )
-    proportional_penalty = penalty_numerator // total_balance * increment
-
-    # Scale penalty by the number of slashing events per epoch
-    amount_slashed = (slashing + proportional_penalty) * number_of_slashing_events
-    # Scale rewards by the number of slashing events per epoch
-    whistleblower_reward *= number_of_slashing_events
-    proposer_reward *= number_of_slashing_events
-
-    # The whistleblower and the block proposer who includes the slashing receive a reward
-    whistleblower_rewards = whistleblower_reward + proposer_reward
-
-    return {
-        "amount_slashed": amount_slashed * dt,
-        "whistleblower_rewards": whistleblower_rewards * dt,
-    }
-
-
-def update_base_reward(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """
-    ## Base Reward State Update Function
-    Calculate and update base reward per validator
-    """
-    # Parameters
-    dt = params["dt"]
-
-    # Get base reward per validator
-    base_reward_per_validator: Gwei = spec.get_base_reward(params, previous_state)
-
-    # By scaling the base reward by our unit of time dt (in epochs),
-    # we can scale all rewards and penalties by the same unit of time
-    return "base_reward", Gwei(base_reward_per_validator) * dt
-
-
-def update_validating_rewards(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """
-    ## Validating Rewards State Update Function
-    Calculate and update total validating rewards
-
-    i.e. rewards received for block proposal, attesting, and being a member of sync committee
-    """
-    # State Variables
-    block_proposer_reward = previous_state["block_proposer_reward"]
-    sync_reward = previous_state["sync_reward"]
-
-    source_reward = previous_state["source_reward"]
-    target_reward = previous_state["target_reward"]
-    head_reward = previous_state["head_reward"]
-
-    base_reward = previous_state["base_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total validating rewards
-    validating_rewards = (
-        block_proposer_reward
-        + source_reward
-        + target_reward
-        + head_reward
-        + sync_reward
-    )
-
-    # Assert validating rewards should be less than equal to the maximum validating rewards
-    max_validating_rewards = number_of_validators_online * base_reward
-    assert validating_rewards <= max_validating_rewards
-
-    return "validating_rewards", validating_rewards
-
-
-def update_validating_penalties(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """
-    ## Validating Penalties State Update Function
-    Calculate and update total validating penalties
-
-    i.e. penalties received for failing to attest, or failing to perform sync committee duties
-    """
-    # State Variables
-    attestation_penalties = previous_state["attestation_penalties"]
-    sync_committee_penalties = previous_state["sync_committee_penalties"]
-
-    # Calculate total validating penalties
-    validating_penalties = attestation_penalties + sync_committee_penalties
-
-    return "validating_penalties", validating_penalties
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_attestation_penalties(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Attestation Penalties Policy Function

-

Validators are penalized for not attesting to the source, target, and head.

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas

-

Extract from spec:

-
penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
-
-
- -Expand source code - -
def policy_attestation_penalties(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Attestation Penalties Policy Function
-    Validators are penalized for not attesting to the source, target, and head.
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    penalties[index] += Gwei(base_reward * weight // WEIGHT_DENOMINATOR)
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators_offline = previous_state["number_of_validators_offline"]
-
-    # Calculate attestation penalties
-    attestation_penalties = (
-        (TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT)
-        / WEIGHT_DENOMINATOR
-        * base_reward
-    )
-    # Aggregation over all offline validators
-    attestation_penalties *= number_of_validators_offline
-
-    return {"attestation_penalties": attestation_penalties}
-
-
-
-def policy_attestation_rewards(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Attestation Rewards Policy Function

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas

-

Extract from spec:

-
reward_numerator = base_reward * weight * unslashed_participating_increments
-rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
-
-
- -Expand source code - -
def policy_attestation_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Attestation Rewards Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#get_flag_index_deltas
-
-    Extract from spec:
-    ```python
-    reward_numerator = base_reward * weight * unslashed_participating_increments
-    rewards[index] += Gwei(reward_numerator // (active_increments * WEIGHT_DENOMINATOR))
-    ```
-    """
-
-    # Parameters
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total source reward
-    # All submitted attestations have to match source vote
-    source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    source_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    source_reward *= number_of_validators_online
-
-    # Calculate total target reward
-    target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    target_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    target_reward *= number_of_validators_online
-
-    # Calculate total head reward
-    head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward
-    # Scale reward by the proportion of validators who also got the attestation in time and correctly
-    head_reward *= number_of_validators_online / number_of_validators
-    # Aggregation over all online validators; assumes one correct vote per online validator per epoch
-    head_reward *= number_of_validators_online
-
-    return {
-        "source_reward": source_reward,
-        "target_reward": target_reward,
-        "head_reward": head_reward,
-    }
-
-
-
-def policy_block_proposal_reward(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Block Proposal Reward Policy Function

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-process_attestation

-

Extract from spec:

-
# Participation flag indices
-participation_flag_indices = []
-if is_matching_head and is_matching_target and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
-    participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
-if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
-    participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
-if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
-    participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
-
-# Update epoch participation flags
-proposer_reward_numerator = 0
-for index in get_attesting_indices(state, data, attestation.aggregation_bits):
-    for flag_index, weight in get_flag_indices_and_weights():
-        if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
-            epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
-            proposer_reward_numerator += get_base_reward(state, index) * weight
-
-# Reward proposer
-proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
-increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
-
-
- -Expand source code - -
def policy_block_proposal_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Block Proposal Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-process_attestation
-
-    Extract from spec:
-    ```python
-    # Participation flag indices
-    participation_flag_indices = []
-    if is_matching_head and is_matching_target and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY:
-        participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX)
-    if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH):
-        participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX)
-    if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH:
-        participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX)
-
-    # Update epoch participation flags
-    proposer_reward_numerator = 0
-    for index in get_attesting_indices(state, data, attestation.aggregation_bits):
-        for flag_index, weight in get_flag_indices_and_weights():
-            if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
-                epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
-                proposer_reward_numerator += get_base_reward(state, index) * weight
-
-    # Reward proposer
-    proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
-    increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
-    ```
-    """
-
-    # Parameters
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-    TIMELY_SOURCE_WEIGHT = params["TIMELY_SOURCE_WEIGHT"]
-    TIMELY_TARGET_WEIGHT = params["TIMELY_TARGET_WEIGHT"]
-    TIMELY_HEAD_WEIGHT = params["TIMELY_HEAD_WEIGHT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    sync_reward = previous_state["sync_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate block proposer reward
-    proposer_reward_numerator = base_reward * (
-        TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + TIMELY_HEAD_WEIGHT
-    )
-    # Aggregate over all attestations in the epoch
-    # Assumes every online validator gets one correct source, target, and head vote per epoch
-    proposer_reward_numerator *= number_of_validators_online
-    # Normalize by the sum of weights so that proposer rewards are 1/8th of base reward
-    proposer_reward_denominator = (
-        (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
-    )
-    block_proposer_reward = Gwei(
-        proposer_reward_numerator // proposer_reward_denominator
-    )
-
-    # Add block proposer reward for including sync committee attestations
-    # See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee-processing
-    block_proposer_reward += (
-        sync_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)
-    )
-
-    return {"block_proposer_reward": block_proposer_reward}
-
-
-
-def policy_slashing(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Slashing Policy Function

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-slash_validator

-

Extract from spec:

-
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
-decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
-
-# Apply proposer and whistleblower rewards
-proposer_index = get_beacon_proposer_index(state)
-if whistleblower_index is None:
-    whistleblower_index = proposer_index
-whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
-proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-increase_balance(state, proposer_index, proposer_reward)
-increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
-
-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#slashings

-

Extract from spec:

-
def process_slashings(state: BeaconState) -> None:
-    epoch = get_current_epoch(state)
-    total_balance = get_total_active_balance(state)
-    adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
-    for index, validator in enumerate(state.validators):
-        if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
-            increment = EFFECTIVE_BALANCE_INCREMENT  # Factored out from penalty numerator to avoid uint64 overflow
-            penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
-            penalty = penalty_numerator // total_balance * increment
-            decrease_balance(state, ValidatorIndex(index), penalty)
-
-
- -Expand source code - -
def policy_slashing(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Slashing Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#modified-slash_validator
-
-    Extract from spec:
-    ```python
-    state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
-    decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
-
-    # Apply proposer and whistleblower rewards
-    proposer_index = get_beacon_proposer_index(state)
-    if whistleblower_index is None:
-        whistleblower_index = proposer_index
-    whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    increase_balance(state, proposer_index, proposer_reward)
-    increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
-    ```
-
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#slashings
-
-    Extract from spec:
-    ```python
-    def process_slashings(state: BeaconState) -> None:
-        epoch = get_current_epoch(state)
-        total_balance = get_total_active_balance(state)
-        adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
-        for index, validator in enumerate(state.validators):
-            if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
-                increment = EFFECTIVE_BALANCE_INCREMENT  # Factored out from penalty numerator to avoid uint64 overflow
-                penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
-                penalty = penalty_numerator // total_balance * increment
-                decrease_balance(state, ValidatorIndex(index), penalty)
-    ```
-    """
-    # Parameters
-    dt = params["dt"]
-    slashing_events_per_1000_epochs = params["slashing_events_per_1000_epochs"]
-    MIN_SLASHING_PENALTY_QUOTIENT = params["MIN_SLASHING_PENALTY_QUOTIENT"]
-    PROPORTIONAL_SLASHING_MULTIPLIER = params["PROPORTIONAL_SLASHING_MULTIPLIER"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    WHISTLEBLOWER_REWARD_QUOTIENT = params["WHISTLEBLOWER_REWARD_QUOTIENT"]
-    PROPOSER_WEIGHT = params["PROPOSER_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate slashing, whistleblower reward, and proposer reward for a single slashing event
-    slashing = Gwei(average_effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
-    whistleblower_reward = Gwei(
-        average_effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT
-    )
-    proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
-    whistleblower_reward = Gwei(whistleblower_reward - proposer_reward)
-
-    # Calculate number of slashing events for current epoch
-    number_of_slashing_events = slashing_events_per_1000_epochs / 1000
-
-    # Calculate the individual penalty proportional to total slashings
-    # in current time period using `PROPORTIONAL_SLASHING_MULTIPLIER`
-    total_balance = spec.get_total_active_balance(params, previous_state)
-    adjusted_total_slashing_balance = min(
-        slashing * number_of_slashing_events * PROPORTIONAL_SLASHING_MULTIPLIER,
-        total_balance,
-    )
-    increment = EFFECTIVE_BALANCE_INCREMENT
-    penalty_numerator = (
-        average_effective_balance // increment * adjusted_total_slashing_balance
-    )
-    proportional_penalty = penalty_numerator // total_balance * increment
-
-    # Scale penalty by the number of slashing events per epoch
-    amount_slashed = (slashing + proportional_penalty) * number_of_slashing_events
-    # Scale rewards by the number of slashing events per epoch
-    whistleblower_reward *= number_of_slashing_events
-    proposer_reward *= number_of_slashing_events
-
-    # The whistleblower and the block proposer who includes the slashing receive a reward
-    whistleblower_rewards = whistleblower_reward + proposer_reward
-
-    return {
-        "amount_slashed": amount_slashed * dt,
-        "whistleblower_rewards": whistleblower_rewards * dt,
-    }
-
-
-
-def policy_sync_committee_penalties(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Sync Committee Penalty Policy Function

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-aggregate-processing

-

Extract from spec:

-
# Compute participant and proposer rewards
-total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-
-
- -Expand source code - -
def policy_sync_committee_penalties(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Sync Committee Penalty Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-aggregate-processing
-
-    Extract from spec:
-    ```python
-    # Compute participant and proposer rewards
-    total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-    total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-    max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-    participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-    proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-    ```
-    """
-
-    # Parameters
-    SYNC_REWARD_WEIGHT = params["SYNC_REWARD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_offline = previous_state["number_of_validators_offline"]
-
-    # Calculate total base rewards
-    total_base_rewards = base_reward * number_of_validators
-    # Set sync penalty to proportion of total base rewards
-    sync_penalty = total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR
-    # Scale penalty by the percentage of offline validators
-    sync_penalty *= number_of_validators_offline / number_of_validators
-
-    return {"sync_committee_penalties": sync_penalty}
-
-
-
-def policy_sync_committee_reward(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Sync Committee Reward Policy Function

-

Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-aggregate-processing

-

Extract from spec:

-
# Compute participant and proposer rewards
-total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-
-
- -Expand source code - -
def policy_sync_committee_reward(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Sync Committee Reward Policy Function
-    Derived from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md#sync-aggregate-processing
-
-    Extract from spec:
-    ```python
-    # Compute participant and proposer rewards
-    total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
-    total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
-    max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
-    participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
-    proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
-    ```
-    """
-
-    # Parameters
-    SYNC_REWARD_WEIGHT = params["SYNC_REWARD_WEIGHT"]
-    WEIGHT_DENOMINATOR = params["WEIGHT_DENOMINATOR"]
-
-    # State Variables
-    base_reward = previous_state["base_reward"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total base rewards
-    total_base_rewards = base_reward * number_of_validators
-    # Set sync reward to proportion of total base rewards
-    sync_reward = total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR
-    # Scale reward by the percentage of online validators
-    sync_reward *= number_of_validators_online / number_of_validators
-
-    return {"sync_reward": sync_reward}
-
-
-
-def update_base_reward(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

Base Reward State Update Function

-

Calculate and update base reward per validator

-
- -Expand source code - -
def update_base_reward(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """
-    ## Base Reward State Update Function
-    Calculate and update base reward per validator
-    """
-    # Parameters
-    dt = params["dt"]
-
-    # Get base reward per validator
-    base_reward_per_validator: Gwei = spec.get_base_reward(params, previous_state)
-
-    # By scaling the base reward by our unit of time dt (in epochs),
-    # we can scale all rewards and penalties by the same unit of time
-    return "base_reward", Gwei(base_reward_per_validator) * dt
-
-
-
-def update_validating_penalties(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

Validating Penalties State Update Function

-

Calculate and update total validating penalties

-

i.e. penalties received for failing to attest, or failing to perform sync committee duties

-
- -Expand source code - -
def update_validating_penalties(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """
-    ## Validating Penalties State Update Function
-    Calculate and update total validating penalties
-
-    i.e. penalties received for failing to attest, or failing to perform sync committee duties
-    """
-    # State Variables
-    attestation_penalties = previous_state["attestation_penalties"]
-    sync_committee_penalties = previous_state["sync_committee_penalties"]
-
-    # Calculate total validating penalties
-    validating_penalties = attestation_penalties + sync_committee_penalties
-
-    return "validating_penalties", validating_penalties
-
-
-
-def update_validating_rewards(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

Validating Rewards State Update Function

-

Calculate and update total validating rewards

-

i.e. rewards received for block proposal, attesting, and being a member of sync committee

-
- -Expand source code - -
def update_validating_rewards(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Gwei]:
-    """
-    ## Validating Rewards State Update Function
-    Calculate and update total validating rewards
-
-    i.e. rewards received for block proposal, attesting, and being a member of sync committee
-    """
-    # State Variables
-    block_proposer_reward = previous_state["block_proposer_reward"]
-    sync_reward = previous_state["sync_reward"]
-
-    source_reward = previous_state["source_reward"]
-    target_reward = previous_state["target_reward"]
-    head_reward = previous_state["head_reward"]
-
-    base_reward = previous_state["base_reward"]
-    number_of_validators_online = previous_state["number_of_validators_online"]
-
-    # Calculate total validating rewards
-    validating_rewards = (
-        block_proposer_reward
-        + source_reward
-        + target_reward
-        + head_reward
-        + sync_reward
-    )
-
-    # Assert validating rewards should be less than equal to the maximum validating rewards
-    max_validating_rewards = number_of_validators_online * base_reward
-    assert validating_rewards <= max_validating_rewards
-
-    return "validating_rewards", validating_rewards
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/spec.html b/docs/model/parts/spec.html deleted file mode 100644 index 4addfbc5..00000000 --- a/docs/model/parts/spec.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - -model.parts.spec API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.spec

-
-
-

Relevant Eth2 Spec Methods

- -
- -Expand source code - -
"""
-# Relevant Eth2 Spec Methods
-
-* Phase 0: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md
-* Altair updates: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md
-"""
-
-import math
-
-import model.constants as constants
-from model.types import Gwei
-from model.system_parameters import Parameters
-from model.state_variables import StateVariables
-
-
-# Beacon state accessors
-
-
-def get_total_active_balance(params: Parameters, state: StateVariables) -> Gwei:
-    '''
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_total_active_balance
-
-    ```python
-    def get_total_active_balance(state: BeaconState) -> Gwei:
-        """
-        Return the combined effective balance of the active validators.
-        Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
-        """
-        return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
-    ```
-    '''
-
-    # Parameters
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-
-    # State Variables
-    eth_staked = state["eth_staked"]
-    number_of_validators = state["number_of_validators"]
-
-    # Calculate total active balance
-    total_active_balance = (
-        eth_staked * constants.gwei
-        - (eth_staked * constants.gwei) % EFFECTIVE_BALANCE_INCREMENT
-    )
-    max_total_active_balance = MAX_EFFECTIVE_BALANCE * number_of_validators
-
-    total_active_balance = min(total_active_balance, max_total_active_balance)
-
-    return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, total_active_balance))
-
-
-def get_base_reward_per_increment(params: Parameters, state: StateVariables) -> Gwei:
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    BASE_REWARD_FACTOR = params["BASE_REWARD_FACTOR"]
-
-    return Gwei(
-        EFFECTIVE_BALANCE_INCREMENT
-        * BASE_REWARD_FACTOR
-        // math.sqrt(get_total_active_balance(params, state))
-    )
-
-
-def get_base_reward(params: Parameters, state: StateVariables) -> Gwei:
-    # Parameters
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-
-    # State Variables
-    average_effective_balance = state["average_effective_balance"]
-
-    increments = (
-        min(average_effective_balance, MAX_EFFECTIVE_BALANCE)
-        // EFFECTIVE_BALANCE_INCREMENT
-    )
-
-    return Gwei(increments * get_base_reward_per_increment(params, state))
-
-
-def get_proposer_reward(params: Parameters, state: StateVariables) -> Gwei:
-    PROPOSER_REWARD_QUOTIENT = params["PROPOSER_REWARD_QUOTIENT"]
-    return Gwei(get_base_reward(params, state) // PROPOSER_REWARD_QUOTIENT)
-
-
-def get_validator_churn_limit(params: Parameters, state: StateVariables) -> int:
-    """Return the validator churn limit for the current epoch
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit
-
-    ```python
-    active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
-    ```
-    """
-    # Parameters
-    MIN_PER_EPOCH_CHURN_LIMIT = params["MIN_PER_EPOCH_CHURN_LIMIT"]
-    CHURN_LIMIT_QUOTIENT = params["CHURN_LIMIT_QUOTIENT"]
-
-    # State Variables
-    number_of_validators = state["number_of_validators"]
-
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, number_of_validators // CHURN_LIMIT_QUOTIENT)
-
-
-
-
-
-
-
-

Functions

-
-
-def get_base_reward(params: Parameters, state: StateVariables) ‑> float -
-
-
-
- -Expand source code - -
def get_base_reward(params: Parameters, state: StateVariables) -> Gwei:
-    # Parameters
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-
-    # State Variables
-    average_effective_balance = state["average_effective_balance"]
-
-    increments = (
-        min(average_effective_balance, MAX_EFFECTIVE_BALANCE)
-        // EFFECTIVE_BALANCE_INCREMENT
-    )
-
-    return Gwei(increments * get_base_reward_per_increment(params, state))
-
-
-
-def get_base_reward_per_increment(params: Parameters, state: StateVariables) ‑> float -
-
-
-
- -Expand source code - -
def get_base_reward_per_increment(params: Parameters, state: StateVariables) -> Gwei:
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    BASE_REWARD_FACTOR = params["BASE_REWARD_FACTOR"]
-
-    return Gwei(
-        EFFECTIVE_BALANCE_INCREMENT
-        * BASE_REWARD_FACTOR
-        // math.sqrt(get_total_active_balance(params, state))
-    )
-
-
-
-def get_proposer_reward(params: Parameters, state: StateVariables) ‑> float -
-
-
-
- -Expand source code - -
def get_proposer_reward(params: Parameters, state: StateVariables) -> Gwei:
-    PROPOSER_REWARD_QUOTIENT = params["PROPOSER_REWARD_QUOTIENT"]
-    return Gwei(get_base_reward(params, state) // PROPOSER_REWARD_QUOTIENT)
-
-
-
-def get_total_active_balance(params: Parameters, state: StateVariables) ‑> float -
-
-

See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_total_active_balance

-
def get_total_active_balance(state: BeaconState) -> Gwei:
-    """
-    Return the combined effective balance of the active validators.
-    Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
-    """
-    return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
-
-
- -Expand source code - -
def get_total_active_balance(params: Parameters, state: StateVariables) -> Gwei:
-    '''
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_total_active_balance
-
-    ```python
-    def get_total_active_balance(state: BeaconState) -> Gwei:
-        """
-        Return the combined effective balance of the active validators.
-        Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
-        """
-        return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
-    ```
-    '''
-
-    # Parameters
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-
-    # State Variables
-    eth_staked = state["eth_staked"]
-    number_of_validators = state["number_of_validators"]
-
-    # Calculate total active balance
-    total_active_balance = (
-        eth_staked * constants.gwei
-        - (eth_staked * constants.gwei) % EFFECTIVE_BALANCE_INCREMENT
-    )
-    max_total_active_balance = MAX_EFFECTIVE_BALANCE * number_of_validators
-
-    total_active_balance = min(total_active_balance, max_total_active_balance)
-
-    return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, total_active_balance))
-
-
-
-def get_validator_churn_limit(params: Parameters, state: StateVariables) ‑> int -
-
-

Return the validator churn limit for the current epoch -See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit

-
active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
-return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
-
-
- -Expand source code - -
def get_validator_churn_limit(params: Parameters, state: StateVariables) -> int:
-    """Return the validator churn limit for the current epoch
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit
-
-    ```python
-    active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
-    ```
-    """
-    # Parameters
-    MIN_PER_EPOCH_CHURN_LIMIT = params["MIN_PER_EPOCH_CHURN_LIMIT"]
-    CHURN_LIMIT_QUOTIENT = params["CHURN_LIMIT_QUOTIENT"]
-
-    # State Variables
-    number_of_validators = state["number_of_validators"]
-
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, number_of_validators // CHURN_LIMIT_QUOTIENT)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/stages.html b/docs/model/parts/stages.html deleted file mode 100644 index 3d797234..00000000 --- a/docs/model/parts/stages.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - -model.parts.stages API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.stages

-
-
-

Ethereum Upgrade Stages -The Stages module contains the logic for transitioning the model -from one stage in the Ethereum network upgrade process to the next at different milestones.

-
- -Expand source code - -
"""Ethereum Upgrade Stages
-The Stages module contains the logic for transitioning the model
-from one stage in the Ethereum network upgrade process to the next at different milestones.
-"""
-
-import datetime
-
-import model.constants as constants
-from model.types import Stage
-
-
-def policy_upgrade_stages(params, substep, state_history, previous_state):
-    """Stages Policy
-    Transitions the model from one stage in the Ethereum network
-    upgrade process to the next at different milestones.
-
-    This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine
-    """
-    # Parameters
-    dt = params["dt"]
-    stage: Stage = params["stage"]
-    date_start = params["date_start"]
-    date_eip1559 = params["date_eip1559"]
-    date_merge = params["date_merge"]
-
-    # State Variables
-    current_stage = previous_state["stage"]
-    timestep = previous_state["timestep"]
-
-    # Calculate current timestamp from timestep
-    timestamp = date_start + datetime.timedelta(
-        days=(timestep * dt / constants.epochs_per_day)
-    )
-
-    # Initialize stage State Variable at start of simulation
-    if current_stage == None:
-        current_stage = stage
-    else:
-        # Convert Stage enum value (int) to Stage enum
-        current_stage = Stage(current_stage)
-
-    # Stage finite-state machine
-    if stage == Stage.ALL:
-        # If Stage ALL selected, transition through all stages
-        # at different timestamps
-        if current_stage in [Stage.ALL, Stage.BEACON_CHAIN] and timestamp < date_eip1559:
-            current_stage = Stage.BEACON_CHAIN
-        elif (
-            current_stage in [Stage.BEACON_CHAIN, Stage.EIP1559]
-            and timestamp < date_merge
-        ):
-            current_stage = Stage.EIP1559
-        else:
-            current_stage = Stage.PROOF_OF_STAKE
-    elif stage == Stage.BEACON_CHAIN:
-        # If Stage BEACON_CHAIN selected, only execute single stage
-        current_stage = Stage.BEACON_CHAIN
-    elif stage == Stage.EIP1559:
-        # If Stage EIP1559 selected, only execute single stage
-        current_stage = Stage.EIP1559
-    elif stage == Stage.PROOF_OF_STAKE:
-        # If Stage PROOF_OF_STAKE selected, only execute single stage
-        current_stage = Stage.PROOF_OF_STAKE
-    else:
-        # Else, raise exception if invalid Stage
-        raise Exception("Invalid Stage selected")
-
-    return {
-        "stage": current_stage.value,
-        "timestamp": timestamp,
-    }
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_upgrade_stages(params, substep, state_history, previous_state) -
-
-

Stages Policy -Transitions the model from one stage in the Ethereum network -upgrade process to the next at different milestones.

-

This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine

-
- -Expand source code - -
def policy_upgrade_stages(params, substep, state_history, previous_state):
-    """Stages Policy
-    Transitions the model from one stage in the Ethereum network
-    upgrade process to the next at different milestones.
-
-    This is essentially a finite-state machine: https://en.wikipedia.org/wiki/Finite-state_machine
-    """
-    # Parameters
-    dt = params["dt"]
-    stage: Stage = params["stage"]
-    date_start = params["date_start"]
-    date_eip1559 = params["date_eip1559"]
-    date_merge = params["date_merge"]
-
-    # State Variables
-    current_stage = previous_state["stage"]
-    timestep = previous_state["timestep"]
-
-    # Calculate current timestamp from timestep
-    timestamp = date_start + datetime.timedelta(
-        days=(timestep * dt / constants.epochs_per_day)
-    )
-
-    # Initialize stage State Variable at start of simulation
-    if current_stage == None:
-        current_stage = stage
-    else:
-        # Convert Stage enum value (int) to Stage enum
-        current_stage = Stage(current_stage)
-
-    # Stage finite-state machine
-    if stage == Stage.ALL:
-        # If Stage ALL selected, transition through all stages
-        # at different timestamps
-        if current_stage in [Stage.ALL, Stage.BEACON_CHAIN] and timestamp < date_eip1559:
-            current_stage = Stage.BEACON_CHAIN
-        elif (
-            current_stage in [Stage.BEACON_CHAIN, Stage.EIP1559]
-            and timestamp < date_merge
-        ):
-            current_stage = Stage.EIP1559
-        else:
-            current_stage = Stage.PROOF_OF_STAKE
-    elif stage == Stage.BEACON_CHAIN:
-        # If Stage BEACON_CHAIN selected, only execute single stage
-        current_stage = Stage.BEACON_CHAIN
-    elif stage == Stage.EIP1559:
-        # If Stage EIP1559 selected, only execute single stage
-        current_stage = Stage.EIP1559
-    elif stage == Stage.PROOF_OF_STAKE:
-        # If Stage PROOF_OF_STAKE selected, only execute single stage
-        current_stage = Stage.PROOF_OF_STAKE
-    else:
-        # Else, raise exception if invalid Stage
-        raise Exception("Invalid Stage selected")
-
-    return {
-        "stage": current_stage.value,
-        "timestamp": timestamp,
-    }
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/system_metrics.html b/docs/model/parts/system_metrics.html deleted file mode 100644 index 46495da6..00000000 --- a/docs/model/parts/system_metrics.html +++ /dev/null @@ -1,479 +0,0 @@ - - - - - - -model.parts.system_metrics API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.system_metrics

-
-
-

System Metrics

-

Calculation of metrics such as validator operational costs and yields.

-
- -Expand source code - -
"""
-# System Metrics
-
-Calculation of metrics such as validator operational costs and yields.
-"""
-
-import typing
-
-import model.constants as constants
-from model.types import Percentage, Gwei
-
-
-def policy_validator_costs(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    """
-    ## Validator Costs Policy Function
-    Calculate the aggregate validator costs.
-    """
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-    validator_hardware_costs_per_epoch = params["validator_hardware_costs_per_epoch"]
-    validator_cloud_costs_per_epoch = params["validator_cloud_costs_per_epoch"]
-    validator_third_party_costs_per_epoch = params[
-        "validator_third_party_costs_per_epoch"
-    ]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    number_of_validators = previous_state["number_of_validators"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate hardware, cloud, and third-party costs per validator type
-    validator_count_distribution = (
-        number_of_validators * validator_percentage_distribution
-    )
-
-    validator_hardware_costs = (
-        validator_count_distribution * validator_hardware_costs_per_epoch * dt
-    )
-
-    validator_cloud_costs = (
-        validator_count_distribution * validator_cloud_costs_per_epoch * dt
-    )
-
-    validator_third_party_costs = (
-        validator_percentage_distribution
-        * validator_third_party_costs_per_epoch  # % of total
-        * total_online_validator_rewards
-    )
-    validator_third_party_costs /= constants.gwei  # Convert from Gwei to ETH
-    validator_third_party_costs *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate total validator costs per validator type and total network costs
-    validator_costs = (
-        validator_hardware_costs + validator_cloud_costs + validator_third_party_costs
-    )
-    total_network_costs = validator_costs.sum(axis=0)
-
-    return {
-        "validator_count_distribution": validator_count_distribution,
-        "validator_hardware_costs": validator_hardware_costs,
-        "validator_cloud_costs": validator_cloud_costs,
-        "validator_third_party_costs": validator_third_party_costs,
-        "validator_costs": validator_costs,
-        "total_network_costs": total_network_costs,
-    }
-
-
-def policy_validator_yields(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    """
-    ## Validator Yields Policy Function
-    Calculate the aggregate validator revenue and profit yields.
-    """
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    eth_staked = previous_state["eth_staked"]
-    validator_costs = previous_state["validator_costs"]
-    total_network_costs = previous_state["total_network_costs"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-    validator_count_distribution = previous_state["validator_count_distribution"]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate ETH staked per validator type
-    validator_eth_staked = validator_count_distribution * average_effective_balance
-    validator_eth_staked /= constants.gwei  # Convert from Gwei to ETH
-
-    # Calculate the revenue per validator type
-    validator_revenue = (
-        validator_percentage_distribution * total_online_validator_rewards
-    )
-    validator_revenue /= constants.gwei  # Convert from Gwei to ETH
-    validator_revenue *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate the profit per validator type
-    validator_profit = validator_revenue - validator_costs
-
-    # Calculate the revenue yields per validator type
-    validator_revenue_yields = validator_revenue / (validator_eth_staked * eth_price)
-    validator_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the profit yields per validator type
-    validator_profit_yields = validator_profit / (validator_eth_staked * eth_price)
-    validator_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network revenue
-    total_revenue = validator_revenue.sum(axis=0)
-
-    # Calculate the total network profit
-    total_profit = total_revenue - total_network_costs
-
-    # Calculate the total network revenue yields
-    total_revenue_yields = total_revenue / (eth_staked * eth_price)
-    total_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network profit yields
-    total_profit_yields = total_profit / (eth_staked * eth_price)
-    total_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    return {
-        # Per validator type
-        "validator_eth_staked": validator_eth_staked,
-        "validator_revenue": validator_revenue,
-        "validator_profit": validator_profit,
-        "validator_revenue_yields": validator_revenue_yields,
-        "validator_profit_yields": validator_profit_yields,
-        # Aggregate
-        "total_revenue": total_revenue,
-        "total_profit": total_profit,
-        "total_revenue_yields": total_revenue_yields,
-        "total_profit_yields": total_profit_yields,
-    }
-
-
-def policy_total_online_validator_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Total Online Validator Rewards Policy Function
-    Calculate the aggregate total online validator rewards.
-    """
-    # State Variables
-    validating_rewards = previous_state["validating_rewards"]
-    validating_penalties = previous_state["validating_penalties"]
-    whistleblower_rewards = previous_state["whistleblower_rewards"]
-    total_priority_fee_to_validators = previous_state[
-        "total_priority_fee_to_validators"
-    ]
-
-    # Calculate total rewards for online validators
-    total_online_validator_rewards = (
-        validating_rewards
-        - validating_penalties
-        + whistleblower_rewards
-        + total_priority_fee_to_validators
-    )
-
-    return {"total_online_validator_rewards": total_online_validator_rewards}
-
-
-def update_supply_inflation(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Percentage]:
-    """
-    ## Supply Inflation State Update Function
-    Update the annualized ETH supply inflation.
-    """
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # Parameters
-    dt = params["dt"]
-
-    # State Variables
-    eth_supply = previous_state["eth_supply"]
-
-    # Calculate the ETH supply inflation
-    supply_inflation = network_issuance / eth_supply
-    supply_inflation *= constants.epochs_per_year / dt  # Annualize value
-
-    return "supply_inflation", supply_inflation
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_total_online_validator_rewards(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Total Online Validator Rewards Policy Function

-

Calculate the aggregate total online validator rewards.

-
- -Expand source code - -
def policy_total_online_validator_rewards(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Total Online Validator Rewards Policy Function
-    Calculate the aggregate total online validator rewards.
-    """
-    # State Variables
-    validating_rewards = previous_state["validating_rewards"]
-    validating_penalties = previous_state["validating_penalties"]
-    whistleblower_rewards = previous_state["whistleblower_rewards"]
-    total_priority_fee_to_validators = previous_state[
-        "total_priority_fee_to_validators"
-    ]
-
-    # Calculate total rewards for online validators
-    total_online_validator_rewards = (
-        validating_rewards
-        - validating_penalties
-        + whistleblower_rewards
-        + total_priority_fee_to_validators
-    )
-
-    return {"total_online_validator_rewards": total_online_validator_rewards}
-
-
-
-def policy_validator_costs(params, substep, state_history, previous_state) ‑> Dict[str, ] -
-
-

Validator Costs Policy Function

-

Calculate the aggregate validator costs.

-
- -Expand source code - -
def policy_validator_costs(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    """
-    ## Validator Costs Policy Function
-    Calculate the aggregate validator costs.
-    """
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-    validator_hardware_costs_per_epoch = params["validator_hardware_costs_per_epoch"]
-    validator_cloud_costs_per_epoch = params["validator_cloud_costs_per_epoch"]
-    validator_third_party_costs_per_epoch = params[
-        "validator_third_party_costs_per_epoch"
-    ]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    number_of_validators = previous_state["number_of_validators"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-
-    # Calculate hardware, cloud, and third-party costs per validator type
-    validator_count_distribution = (
-        number_of_validators * validator_percentage_distribution
-    )
-
-    validator_hardware_costs = (
-        validator_count_distribution * validator_hardware_costs_per_epoch * dt
-    )
-
-    validator_cloud_costs = (
-        validator_count_distribution * validator_cloud_costs_per_epoch * dt
-    )
-
-    validator_third_party_costs = (
-        validator_percentage_distribution
-        * validator_third_party_costs_per_epoch  # % of total
-        * total_online_validator_rewards
-    )
-    validator_third_party_costs /= constants.gwei  # Convert from Gwei to ETH
-    validator_third_party_costs *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate total validator costs per validator type and total network costs
-    validator_costs = (
-        validator_hardware_costs + validator_cloud_costs + validator_third_party_costs
-    )
-    total_network_costs = validator_costs.sum(axis=0)
-
-    return {
-        "validator_count_distribution": validator_count_distribution,
-        "validator_hardware_costs": validator_hardware_costs,
-        "validator_cloud_costs": validator_cloud_costs,
-        "validator_third_party_costs": validator_third_party_costs,
-        "validator_costs": validator_costs,
-        "total_network_costs": total_network_costs,
-    }
-
-
-
-def policy_validator_yields(params, substep, state_history, previous_state) ‑> Dict[str, ] -
-
-

Validator Yields Policy Function

-

Calculate the aggregate validator revenue and profit yields.

-
- -Expand source code - -
def policy_validator_yields(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, any]:
-    """
-    ## Validator Yields Policy Function
-    Calculate the aggregate validator revenue and profit yields.
-    """
-    # Parameters
-    dt = params["dt"]
-    validator_percentage_distribution = params["validator_percentage_distribution"]
-
-    # State Variables
-    eth_price = previous_state["eth_price"]
-    eth_staked = previous_state["eth_staked"]
-    validator_costs = previous_state["validator_costs"]
-    total_network_costs = previous_state["total_network_costs"]
-    total_online_validator_rewards = previous_state["total_online_validator_rewards"]
-    validator_count_distribution = previous_state["validator_count_distribution"]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate ETH staked per validator type
-    validator_eth_staked = validator_count_distribution * average_effective_balance
-    validator_eth_staked /= constants.gwei  # Convert from Gwei to ETH
-
-    # Calculate the revenue per validator type
-    validator_revenue = (
-        validator_percentage_distribution * total_online_validator_rewards
-    )
-    validator_revenue /= constants.gwei  # Convert from Gwei to ETH
-    validator_revenue *= eth_price  # Convert from ETH to Dollars
-
-    # Calculate the profit per validator type
-    validator_profit = validator_revenue - validator_costs
-
-    # Calculate the revenue yields per validator type
-    validator_revenue_yields = validator_revenue / (validator_eth_staked * eth_price)
-    validator_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the profit yields per validator type
-    validator_profit_yields = validator_profit / (validator_eth_staked * eth_price)
-    validator_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network revenue
-    total_revenue = validator_revenue.sum(axis=0)
-
-    # Calculate the total network profit
-    total_profit = total_revenue - total_network_costs
-
-    # Calculate the total network revenue yields
-    total_revenue_yields = total_revenue / (eth_staked * eth_price)
-    total_revenue_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    # Calculate the total network profit yields
-    total_profit_yields = total_profit / (eth_staked * eth_price)
-    total_profit_yields *= constants.epochs_per_year / dt  # Annualize value
-
-    return {
-        # Per validator type
-        "validator_eth_staked": validator_eth_staked,
-        "validator_revenue": validator_revenue,
-        "validator_profit": validator_profit,
-        "validator_revenue_yields": validator_revenue_yields,
-        "validator_profit_yields": validator_profit_yields,
-        # Aggregate
-        "total_revenue": total_revenue,
-        "total_profit": total_profit,
-        "total_revenue_yields": total_revenue_yields,
-        "total_profit_yields": total_profit_yields,
-    }
-
-
-
-def update_supply_inflation(params, substep, state_history, previous_state, policy_input) ‑> Tuple[str, float] -
-
-

Supply Inflation State Update Function

-

Update the annualized ETH supply inflation.

-
- -Expand source code - -
def update_supply_inflation(
-    params, substep, state_history, previous_state, policy_input
-) -> typing.Tuple[str, Percentage]:
-    """
-    ## Supply Inflation State Update Function
-    Update the annualized ETH supply inflation.
-    """
-    # Policy Inputs
-    network_issuance = policy_input["network_issuance"]
-
-    # Parameters
-    dt = params["dt"]
-
-    # State Variables
-    eth_supply = previous_state["eth_supply"]
-
-    # Calculate the ETH supply inflation
-    supply_inflation = network_issuance / eth_supply
-    supply_inflation *= constants.epochs_per_year / dt  # Annualize value
-
-    return "supply_inflation", supply_inflation
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/utils/ethereum_spec.html b/docs/model/parts/utils/ethereum_spec.html deleted file mode 100644 index 3e1d7bc8..00000000 --- a/docs/model/parts/utils/ethereum_spec.html +++ /dev/null @@ -1,370 +0,0 @@ - - - - - - -model.parts.utils.ethereum_spec API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.utils.ethereum_spec

-
-
-

Relevant methods from the official eth2.0-specs specification repo.

- -
- -Expand source code - -
"""
-Relevant methods from the official [eth2.0-specs](https://github.com/ethereum/eth2.0-specs) specification repo.
-
-* Phase 0: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md
-* Altair updates: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/altair/beacon-chain.md
-"""
-
-import model.constants as constants
-from model.state_variables import StateVariables
-from model.system_parameters import Parameters
-from model.types import Gwei
-
-
-# Beacon state accessors
-
-
-def get_total_active_balance(params: Parameters, state: StateVariables) -> Gwei:
-    """
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_total_active_balance
-
-    ```python
-    def get_total_active_balance(state: BeaconState) -> Gwei:
-        '''
-        Return the combined effective balance of the active validators.
-        Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
-        '''
-        return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
-    ```
-    """
-
-    # Parameters
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-
-    # State Variables
-    eth_staked = state["eth_staked"]
-    number_of_validators = state["number_of_validators"]
-
-    # Calculate total active balance
-    total_active_balance = (
-        eth_staked * constants.gwei
-        - (eth_staked * constants.gwei) % EFFECTIVE_BALANCE_INCREMENT
-    )
-    max_total_active_balance = MAX_EFFECTIVE_BALANCE * number_of_validators
-
-    total_active_balance = min(total_active_balance, max_total_active_balance)
-
-    return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, total_active_balance))
-
-
-def integer_squareroot(n):
-    """
-    Return the largest integer ``x`` such that ``x**2 <= n``.
-
-    See https://benjaminion.xyz/eth2-annotated-spec/phase0/beacon-chain/
-    """
-    x = n
-    y = (x + 1) // 2
-    while y < x:
-        x = y
-        y = (x + n // x) // 2
-    return x
-
-
-def get_base_reward_per_increment(params: Parameters, state: StateVariables) -> Gwei:
-    """Get the base reward per increment (single validator)"""
-
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    BASE_REWARD_FACTOR = params["BASE_REWARD_FACTOR"]
-
-    return Gwei(
-        EFFECTIVE_BALANCE_INCREMENT
-        * BASE_REWARD_FACTOR
-        // integer_squareroot(int(get_total_active_balance(params, state)))
-    )
-
-
-def get_base_reward(params: Parameters, state: StateVariables) -> Gwei:
-    """Get the base reward for the current epoch"""
-
-    # Parameters
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-
-    # State Variables
-    average_effective_balance = state["average_effective_balance"]
-
-    increments = (
-        min(average_effective_balance, MAX_EFFECTIVE_BALANCE)
-        // EFFECTIVE_BALANCE_INCREMENT
-    )
-
-    return Gwei(increments * get_base_reward_per_increment(params, state))
-
-
-def get_proposer_reward(params: Parameters, state: StateVariables) -> Gwei:
-    """Get the proposer reward as a proportion of the base reward"""
-
-    PROPOSER_REWARD_QUOTIENT = params["PROPOSER_REWARD_QUOTIENT"]
-    return Gwei(get_base_reward(params, state) // PROPOSER_REWARD_QUOTIENT)
-
-
-def get_validator_churn_limit(params: Parameters, state: StateVariables) -> int:
-    """
-    Return the validator churn limit for the current epoch.
-
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit
-
-    ```python
-    active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
-    ```
-    """
-    # Parameters
-    MIN_PER_EPOCH_CHURN_LIMIT = params["MIN_PER_EPOCH_CHURN_LIMIT"]
-    CHURN_LIMIT_QUOTIENT = params["CHURN_LIMIT_QUOTIENT"]
-
-    # State Variables
-    number_of_validators = state["number_of_validators"]
-
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, number_of_validators // CHURN_LIMIT_QUOTIENT)
-
-
-
-
-
-
-
-

Functions

-
-
-def get_base_reward(params: Parameters, state: StateVariables) ‑> float -
-
-

Get the base reward for the current epoch

-
- -Expand source code - -
def get_base_reward(params: Parameters, state: StateVariables) -> Gwei:
-    """Get the base reward for the current epoch"""
-
-    # Parameters
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-
-    # State Variables
-    average_effective_balance = state["average_effective_balance"]
-
-    increments = (
-        min(average_effective_balance, MAX_EFFECTIVE_BALANCE)
-        // EFFECTIVE_BALANCE_INCREMENT
-    )
-
-    return Gwei(increments * get_base_reward_per_increment(params, state))
-
-
-
-def get_base_reward_per_increment(params: Parameters, state: StateVariables) ‑> float -
-
-

Get the base reward per increment (single validator)

-
- -Expand source code - -
def get_base_reward_per_increment(params: Parameters, state: StateVariables) -> Gwei:
-    """Get the base reward per increment (single validator)"""
-
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    BASE_REWARD_FACTOR = params["BASE_REWARD_FACTOR"]
-
-    return Gwei(
-        EFFECTIVE_BALANCE_INCREMENT
-        * BASE_REWARD_FACTOR
-        // integer_squareroot(int(get_total_active_balance(params, state)))
-    )
-
-
-
-def get_proposer_reward(params: Parameters, state: StateVariables) ‑> float -
-
-

Get the proposer reward as a proportion of the base reward

-
- -Expand source code - -
def get_proposer_reward(params: Parameters, state: StateVariables) -> Gwei:
-    """Get the proposer reward as a proportion of the base reward"""
-
-    PROPOSER_REWARD_QUOTIENT = params["PROPOSER_REWARD_QUOTIENT"]
-    return Gwei(get_base_reward(params, state) // PROPOSER_REWARD_QUOTIENT)
-
-
-
-def get_total_active_balance(params: Parameters, state: StateVariables) ‑> float -
-
-

See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_total_active_balance

-
def get_total_active_balance(state: BeaconState) -> Gwei:
-    '''
-    Return the combined effective balance of the active validators.
-    Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
-    '''
-    return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
-
-
- -Expand source code - -
def get_total_active_balance(params: Parameters, state: StateVariables) -> Gwei:
-    """
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_total_active_balance
-
-    ```python
-    def get_total_active_balance(state: BeaconState) -> Gwei:
-        '''
-        Return the combined effective balance of the active validators.
-        Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
-        '''
-        return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
-    ```
-    """
-
-    # Parameters
-    EFFECTIVE_BALANCE_INCREMENT = params["EFFECTIVE_BALANCE_INCREMENT"]
-    MAX_EFFECTIVE_BALANCE = params["MAX_EFFECTIVE_BALANCE"]
-
-    # State Variables
-    eth_staked = state["eth_staked"]
-    number_of_validators = state["number_of_validators"]
-
-    # Calculate total active balance
-    total_active_balance = (
-        eth_staked * constants.gwei
-        - (eth_staked * constants.gwei) % EFFECTIVE_BALANCE_INCREMENT
-    )
-    max_total_active_balance = MAX_EFFECTIVE_BALANCE * number_of_validators
-
-    total_active_balance = min(total_active_balance, max_total_active_balance)
-
-    return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, total_active_balance))
-
-
-
-def get_validator_churn_limit(params: Parameters, state: StateVariables) ‑> int -
-
-

Return the validator churn limit for the current epoch.

-

See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit

-
active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
-return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
-
-
- -Expand source code - -
def get_validator_churn_limit(params: Parameters, state: StateVariables) -> int:
-    """
-    Return the validator churn limit for the current epoch.
-
-    See https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit
-
-    ```python
-    active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
-    ```
-    """
-    # Parameters
-    MIN_PER_EPOCH_CHURN_LIMIT = params["MIN_PER_EPOCH_CHURN_LIMIT"]
-    CHURN_LIMIT_QUOTIENT = params["CHURN_LIMIT_QUOTIENT"]
-
-    # State Variables
-    number_of_validators = state["number_of_validators"]
-
-    return max(MIN_PER_EPOCH_CHURN_LIMIT, number_of_validators // CHURN_LIMIT_QUOTIENT)
-
-
-
-def integer_squareroot(n) -
-
-

Return the largest integer x such that x**2 <= n.

-

See https://benjaminion.xyz/eth2-annotated-spec/phase0/beacon-chain/

-
- -Expand source code - -
def integer_squareroot(n):
-    """
-    Return the largest integer ``x`` such that ``x**2 <= n``.
-
-    See https://benjaminion.xyz/eth2-annotated-spec/phase0/beacon-chain/
-    """
-    x = n
-    y = (x + 1) // 2
-    while y < x:
-        x = y
-        y = (x + n // x) // 2
-    return x
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/utils/index.html b/docs/model/parts/utils/index.html deleted file mode 100644 index 7e793a46..00000000 --- a/docs/model/parts/utils/index.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - -model.parts.utils API documentation - - - - - - - - - - - -
-
-
-

Namespace model.parts.utils

-
-
-
-
-

Sub-modules

-
-
model.parts.utils.ethereum_spec
-
-

Relevant methods from the official eth2.0-specs specification repo …

-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/parts/validators.html b/docs/model/parts/validators.html deleted file mode 100644 index 86c0f664..00000000 --- a/docs/model/parts/validators.html +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - -model.parts.validators API documentation - - - - - - - - - - - -
-
-
-

Module model.parts.validators

-
-
-

Validator Mechanisms

-

Validator processes such as validator activation, staking, and uptime.

-
- -Expand source code - -
"""
-# Validator Mechanisms
-
-Validator processes such as validator activation, staking, and uptime.
-"""
-
-import typing
-
-import model.constants as constants
-import model.parts.utils.ethereum_spec as spec
-from model.types import ETH, Gwei
-
-
-def policy_staking(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, ETH]:
-    """
-    ## Staking Policy
-    A policy used when driving the model with the `eth_staked_process`,
-    for generating phase-space analyses, e.g. simulating a set of discrete `eth_staked` values.
-
-    When the `eth_staked_process` is disabled, the model is driven using the `validator_process`,
-    for generating state-space analyses.
-    """
-    # Parameters
-    dt = params["dt"]
-    eth_staked_process = params["eth_staked_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-    eth_supply = previous_state["eth_supply"]
-    number_of_validators = previous_state["number_of_validators"]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # If the eth_staked_process is defined
-    if eth_staked_process(0, 0) is not None:
-        # Get the ETH staked sample for the current run and timestep
-        eth_staked = eth_staked_process(run, timestep * dt)
-    # Else, calculate from the number of validators
-    else:
-        eth_staked = number_of_validators * average_effective_balance / constants.gwei
-
-    # Assert expected conditions
-    assert eth_staked <= eth_supply, f"ETH staked can't be more than ETH supply"
-
-    return {"eth_staked": eth_staked}
-
-
-def policy_validators(params, substep, state_history, previous_state):
-    """
-    ## Validator Policy Function
-    Calculate the number of validators driven by the ETH staked or validator processes.
-    """
-    # Parameters
-    dt = params["dt"]
-    eth_staked_process = params["eth_staked_process"]
-    validator_process = params["validator_process"]
-    validator_uptime_process = params["validator_uptime_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_in_activation_queue = previous_state[
-        "number_of_validators_in_activation_queue"
-    ]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate the number of validators using ETH staked
-    if eth_staked_process(0, 0) is not None:
-        eth_staked = eth_staked_process(run, timestep * dt)
-        number_of_validators = int(
-            round(eth_staked / (average_effective_balance / constants.gwei))
-        )
-    else:
-        new_validators_per_epoch = validator_process(run, timestep * dt)
-        number_of_validators_in_activation_queue += new_validators_per_epoch * dt
-
-        validator_churn_limit = (
-            spec.get_validator_churn_limit(params, previous_state) * dt
-        )
-        activated_validators = min(
-            number_of_validators_in_activation_queue, validator_churn_limit
-        )
-
-        number_of_validators += activated_validators
-        number_of_validators_in_activation_queue -= activated_validators
-
-    # Calculate the number of validators online and offline using validator uptime
-    validator_uptime = validator_uptime_process(run, timestep * dt)
-    number_of_validators_online = int(round(number_of_validators * validator_uptime))
-    number_of_validators_offline = number_of_validators - number_of_validators_online
-
-    # Assert expected conditions
-    # Assume a participation of more than 2/3 due to lack of inactivity leak mechanism
-    assert validator_uptime >= 2 / 3, "Validator uptime must be greater than 2/3"
-    assert (
-        number_of_validators
-        == number_of_validators_online + number_of_validators_offline
-    )
-
-    return {
-        "number_of_validators_in_activation_queue": number_of_validators_in_activation_queue,
-        "number_of_validators": number_of_validators,
-        "number_of_validators_online": number_of_validators_online,
-        "number_of_validators_offline": number_of_validators_offline,
-    }
-
-
-def policy_average_effective_balance(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Average Effective Balance Policy Function
-    Calculate the validator average effective balance.
-    """
-    # State Variables
-    number_of_validators = previous_state["number_of_validators"]
-
-    # Get total active balance
-    total_active_balance = spec.get_total_active_balance(params, previous_state)
-    # Aggregate by averaging over all validators
-    average_effective_balance = total_active_balance / number_of_validators
-
-    return {"average_effective_balance": average_effective_balance}
-
-
-
-
-
-
-
-

Functions

-
-
-def policy_average_effective_balance(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Average Effective Balance Policy Function

-

Calculate the validator average effective balance.

-
- -Expand source code - -
def policy_average_effective_balance(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, Gwei]:
-    """
-    ## Average Effective Balance Policy Function
-    Calculate the validator average effective balance.
-    """
-    # State Variables
-    number_of_validators = previous_state["number_of_validators"]
-
-    # Get total active balance
-    total_active_balance = spec.get_total_active_balance(params, previous_state)
-    # Aggregate by averaging over all validators
-    average_effective_balance = total_active_balance / number_of_validators
-
-    return {"average_effective_balance": average_effective_balance}
-
-
-
-def policy_staking(params, substep, state_history, previous_state) ‑> Dict[str, float] -
-
-

Staking Policy

-

A policy used when driving the model with the eth_staked_process, -for generating phase-space analyses, e.g. simulating a set of discrete eth_staked values.

-

When the eth_staked_process is disabled, the model is driven using the validator_process, -for generating state-space analyses.

-
- -Expand source code - -
def policy_staking(
-    params, substep, state_history, previous_state
-) -> typing.Dict[str, ETH]:
-    """
-    ## Staking Policy
-    A policy used when driving the model with the `eth_staked_process`,
-    for generating phase-space analyses, e.g. simulating a set of discrete `eth_staked` values.
-
-    When the `eth_staked_process` is disabled, the model is driven using the `validator_process`,
-    for generating state-space analyses.
-    """
-    # Parameters
-    dt = params["dt"]
-    eth_staked_process = params["eth_staked_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-    eth_supply = previous_state["eth_supply"]
-    number_of_validators = previous_state["number_of_validators"]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # If the eth_staked_process is defined
-    if eth_staked_process(0, 0) is not None:
-        # Get the ETH staked sample for the current run and timestep
-        eth_staked = eth_staked_process(run, timestep * dt)
-    # Else, calculate from the number of validators
-    else:
-        eth_staked = number_of_validators * average_effective_balance / constants.gwei
-
-    # Assert expected conditions
-    assert eth_staked <= eth_supply, f"ETH staked can't be more than ETH supply"
-
-    return {"eth_staked": eth_staked}
-
-
-
-def policy_validators(params, substep, state_history, previous_state) -
-
-

Validator Policy Function

-

Calculate the number of validators driven by the ETH staked or validator processes.

-
- -Expand source code - -
def policy_validators(params, substep, state_history, previous_state):
-    """
-    ## Validator Policy Function
-    Calculate the number of validators driven by the ETH staked or validator processes.
-    """
-    # Parameters
-    dt = params["dt"]
-    eth_staked_process = params["eth_staked_process"]
-    validator_process = params["validator_process"]
-    validator_uptime_process = params["validator_uptime_process"]
-
-    # State Variables
-    run = previous_state["run"]
-    timestep = previous_state["timestep"]
-    number_of_validators = previous_state["number_of_validators"]
-    number_of_validators_in_activation_queue = previous_state[
-        "number_of_validators_in_activation_queue"
-    ]
-    average_effective_balance = previous_state["average_effective_balance"]
-
-    # Calculate the number of validators using ETH staked
-    if eth_staked_process(0, 0) is not None:
-        eth_staked = eth_staked_process(run, timestep * dt)
-        number_of_validators = int(
-            round(eth_staked / (average_effective_balance / constants.gwei))
-        )
-    else:
-        new_validators_per_epoch = validator_process(run, timestep * dt)
-        number_of_validators_in_activation_queue += new_validators_per_epoch * dt
-
-        validator_churn_limit = (
-            spec.get_validator_churn_limit(params, previous_state) * dt
-        )
-        activated_validators = min(
-            number_of_validators_in_activation_queue, validator_churn_limit
-        )
-
-        number_of_validators += activated_validators
-        number_of_validators_in_activation_queue -= activated_validators
-
-    # Calculate the number of validators online and offline using validator uptime
-    validator_uptime = validator_uptime_process(run, timestep * dt)
-    number_of_validators_online = int(round(number_of_validators * validator_uptime))
-    number_of_validators_offline = number_of_validators - number_of_validators_online
-
-    # Assert expected conditions
-    # Assume a participation of more than 2/3 due to lack of inactivity leak mechanism
-    assert validator_uptime >= 2 / 3, "Validator uptime must be greater than 2/3"
-    assert (
-        number_of_validators
-        == number_of_validators_online + number_of_validators_offline
-    )
-
-    return {
-        "number_of_validators_in_activation_queue": number_of_validators_in_activation_queue,
-        "number_of_validators": number_of_validators,
-        "number_of_validators_online": number_of_validators_online,
-        "number_of_validators_offline": number_of_validators_offline,
-    }
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/processes.html b/docs/model/processes.html deleted file mode 100644 index 0699f8aa..00000000 --- a/docs/model/processes.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - -model.processes API documentation - - - - - - - - - - - -
-
-
-

Module model.processes

-
-
-
- -Expand source code - -
import numpy as np
-from stochastic import processes
-
-import experiments.simulation_configuration as simulation
-
-
-def create_eth_price_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    minimum_eth_price=1500,
-):
-    """Configure environmental ETH price process
-    > A Brownian excursion is a Brownian bridge from (0, 0) to (t, 0) which is conditioned to be nonnegative on the interval [0, t].
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.BrownianExcursion(t=(timesteps * dt), rng=rng)
-    samples = process.sample(timesteps * dt + 1)
-    maximum_eth_price = max(samples)
-    samples = [
-        minimum_eth_price + eth_price_sample / maximum_eth_price * minimum_eth_price
-        for eth_price_sample in samples
-    ]
-    return samples
-
-
-def create_validator_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    validator_adoption_rate=4,
-):
-    """Configure environmental validator staking process
-    > A Poisson process with rate :math:\lambda is a count of occurrences of i.i.d. exponential random variables with mean :math:1/\lambda. This class generates samples of times for which cumulative exponential random variables occur.
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.PoissonProcess(rate=1 / 3, rng=rng)
-    samples = process.sample(timesteps * dt + 1)
-    samples = np.diff(samples)
-    samples = [int(sample) for sample in samples]
-    return samples
-
-
-def create_stochastic_process_realizations(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    runs=simulation.MONTE_CARLO_RUNS,
-):
-    """Create stochastic process realizations
-    Using the stochastic processes defined in `processes` module, create random number generator (RNG) seeds,
-    and use RNG to pre-generate samples for number of simulation timesteps.
-    """
-    # Create Random Number Generator (RNG) with a seed for range of runs
-    rngs = [np.random.default_rng(seed) for seed in range(runs)]
-
-    eth_price_samples = [
-        create_eth_price_process(timesteps=timesteps, dt=dt, rng=rng) for rng in rngs
-    ]
-    validator_samples = [
-        create_validator_process(timesteps=timesteps, dt=dt, rng=rng) for rng in rngs
-    ]
-    validator_uptime_samples = [
-        rng.uniform(0.96, 0.99, timesteps * dt + 1) for rng in rngs
-    ]
-
-    return {
-        "eth_price_samples": eth_price_samples,
-        "validator_samples": validator_samples,
-        "validator_uptime_samples": validator_uptime_samples,
-    }
-
-
-
-
-
-
-
-

Functions

-
-
-def create_eth_price_process(timesteps=360, dt=225, rng=Generator(PCG64), minimum_eth_price=1500) -
-
-

Configure environmental ETH price process

-
-

A Brownian excursion is a Brownian bridge from (0, 0) to (t, 0) which is conditioned to be nonnegative on the interval [0, t].

-
-

See https://stochastic.readthedocs.io/en/latest/continuous.html

-
- -Expand source code - -
def create_eth_price_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    minimum_eth_price=1500,
-):
-    """Configure environmental ETH price process
-    > A Brownian excursion is a Brownian bridge from (0, 0) to (t, 0) which is conditioned to be nonnegative on the interval [0, t].
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.BrownianExcursion(t=(timesteps * dt), rng=rng)
-    samples = process.sample(timesteps * dt + 1)
-    maximum_eth_price = max(samples)
-    samples = [
-        minimum_eth_price + eth_price_sample / maximum_eth_price * minimum_eth_price
-        for eth_price_sample in samples
-    ]
-    return samples
-
-
-
-def create_stochastic_process_realizations(timesteps=360, dt=225, runs=1) -
-
-

Create stochastic process realizations -Using the stochastic processes defined in processes module, create random number generator (RNG) seeds, -and use RNG to pre-generate samples for number of simulation timesteps.

-
- -Expand source code - -
def create_stochastic_process_realizations(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    runs=simulation.MONTE_CARLO_RUNS,
-):
-    """Create stochastic process realizations
-    Using the stochastic processes defined in `processes` module, create random number generator (RNG) seeds,
-    and use RNG to pre-generate samples for number of simulation timesteps.
-    """
-    # Create Random Number Generator (RNG) with a seed for range of runs
-    rngs = [np.random.default_rng(seed) for seed in range(runs)]
-
-    eth_price_samples = [
-        create_eth_price_process(timesteps=timesteps, dt=dt, rng=rng) for rng in rngs
-    ]
-    validator_samples = [
-        create_validator_process(timesteps=timesteps, dt=dt, rng=rng) for rng in rngs
-    ]
-    validator_uptime_samples = [
-        rng.uniform(0.96, 0.99, timesteps * dt + 1) for rng in rngs
-    ]
-
-    return {
-        "eth_price_samples": eth_price_samples,
-        "validator_samples": validator_samples,
-        "validator_uptime_samples": validator_uptime_samples,
-    }
-
-
-
-def create_validator_process(timesteps=360, dt=225, rng=Generator(PCG64), validator_adoption_rate=4) -
-
-

Configure environmental validator staking process

-
-

A Poisson process with rate :math:\lambda is a count of occurrences of i.i.d. exponential random variables with mean :math:1/\lambda. This class generates samples of times for which cumulative exponential random variables occur.

-
-

See https://stochastic.readthedocs.io/en/latest/continuous.html

-
- -Expand source code - -
def create_validator_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    validator_adoption_rate=4,
-):
-    """Configure environmental validator staking process
-    > A Poisson process with rate :math:\lambda is a count of occurrences of i.i.d. exponential random variables with mean :math:1/\lambda. This class generates samples of times for which cumulative exponential random variables occur.
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.PoissonProcess(rate=1 / 3, rng=rng)
-    samples = process.sample(timesteps * dt + 1)
-    samples = np.diff(samples)
-    samples = [int(sample) for sample in samples]
-    return samples
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/simulation_configuration.html b/docs/model/simulation_configuration.html deleted file mode 100644 index 8084795a..00000000 --- a/docs/model/simulation_configuration.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - -experiments.simulation_configuration API documentation - - - - - - - - - - - -
-
-
-

Module experiments.simulation_configuration

-
-
-

Simulation configuration such as the number of timesteps and Monte Carlo runs

-
- -Expand source code - -
"""
-Simulation configuration such as the number of timesteps and Monte Carlo runs
-"""
-
-from model.constants import epochs_per_month, epochs_per_day
-
-
-DELTA_TIME = epochs_per_day  # epochs per timestep
-SIMULATION_TIME_MONTHS = 12  # number of months
-TIMESTEPS = (
-    epochs_per_month * SIMULATION_TIME_MONTHS // DELTA_TIME
-)  # number of simulation timesteps
-MONTE_CARLO_RUNS = 1  # number of runs
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/state_update_blocks.html b/docs/model/state_update_blocks.html deleted file mode 100644 index cb4ba7fb..00000000 --- a/docs/model/state_update_blocks.html +++ /dev/null @@ -1,309 +0,0 @@ - - - - - - -model.state_update_blocks API documentation - - - - - - - - - - - -
-
-
-

Module model.state_update_blocks

-
-
-

cadCAD model State Update Block structure, composed of Policy and State Update Functions

-
- -Expand source code - -
"""
-cadCAD model State Update Block structure, composed of Policy and State Update Functions
-"""
-
-import model.parts.ethereum_system as ethereum
-import model.parts.pos_incentives as incentives
-import model.parts.system_metrics as metrics
-import model.parts.validators as validators
-from model.system_parameters import parameters
-from model.utils import update_from_signal
-
-state_update_block_stages = {
-    "description": """
-        Transition between stages of network upgrade process
-    """,
-    "policies": {"upgrade_stages": ethereum.policy_upgrade_stages},
-    "variables": {
-        "stage": update_from_signal("stage"),
-        "timestamp": update_from_signal("timestamp"),
-    },
-}
-
-state_update_block_staking = {
-    "description": """
-        Environmental Ethereum processes:
-        * ETH price update
-        * Staking of ETH for new validators
-    """,
-    "policies": {
-        "staking": validators.policy_staking,
-    },
-    "variables": {
-        "eth_price": ethereum.update_eth_price,
-        "eth_staked": update_from_signal("eth_staked"),
-    },
-}
-
-state_update_block_validators = {
-    "description": """
-        Environmental validator processes:
-        * New validators
-        * Online and offline validators
-    """,
-    "policies": {
-        "policy_validators": validators.policy_validators,
-    },
-    "variables": {
-        "number_of_validators_in_activation_queue": update_from_signal(
-            "number_of_validators_in_activation_queue"
-        ),
-        "number_of_validators": update_from_signal("number_of_validators"),
-        "number_of_validators_online": update_from_signal(
-            "number_of_validators_online"
-        ),
-        "number_of_validators_offline": update_from_signal(
-            "number_of_validators_offline"
-        ),
-    },
-}
-
-_state_update_blocks = [
-    {
-        "description": """
-            Calculation and update of validator average effective balance & base reward
-        """,
-        "policies": {
-            "average_effective_balance": validators.policy_average_effective_balance,
-        },
-        "variables": {
-            "average_effective_balance": update_from_signal(
-                "average_effective_balance"
-            ),
-            "base_reward": incentives.update_base_reward,
-        },
-    },
-    {
-        "description": """
-            Sync committee and attestation rewards
-        """,
-        "policies": {
-            "attestation": incentives.policy_attestation_rewards,
-            "sync_committee": incentives.policy_sync_committee_reward,
-        },
-        "variables": {
-            "source_reward": update_from_signal("source_reward"),
-            "target_reward": update_from_signal("target_reward"),
-            "head_reward": update_from_signal("head_reward"),
-            "sync_reward": update_from_signal("sync_reward"),
-        },
-    },
-    {
-        "description": """
-            Sync committee and attestation penalties
-        """,
-        "policies": {
-            "attestation": incentives.policy_attestation_penalties,
-            "sync_committee": incentives.policy_sync_committee_penalties,
-        },
-        "variables": {
-            "attestation_penalties": update_from_signal("attestation_penalties"),
-            "sync_committee_penalties": update_from_signal("sync_committee_penalties"),
-        },
-    },
-    {
-        "description": """
-            Block proposal rewards
-        """,
-        "policies": {
-            "block_proposal": incentives.policy_block_proposal_reward,
-        },
-        "variables": {
-            "block_proposer_reward": update_from_signal("block_proposer_reward"),
-        },
-    },
-    {
-        "description": """
-            Total validating rewards and penalties
-        """,
-        "policies": {},
-        "variables": {
-            "validating_rewards": incentives.update_validating_rewards,
-            "validating_penalties": incentives.update_validating_penalties,
-        },
-    },
-    {
-        "description": """
-            Validator slashing process, rewards, and penalties
-        """,
-        "policies": {
-            "slashing": incentives.policy_slashing,
-        },
-        "variables": {
-            "amount_slashed": update_from_signal("amount_slashed"),
-            "whistleblower_rewards": update_from_signal("whistleblower_rewards"),
-        },
-    },
-    {
-        "description": """
-            Ethereum EIP1559 process
-        """,
-        "policies": {
-            "eip1559": ethereum.policy_eip1559_transaction_pricing,
-        },
-        "variables": {
-            "base_fee_per_gas": update_from_signal("base_fee_per_gas"),
-            "total_base_fee": update_from_signal("total_base_fee"),
-            "total_priority_fee_to_miners": update_from_signal(
-                "total_priority_fee_to_miners"
-            ),
-            "total_priority_fee_to_validators": update_from_signal(
-                "total_priority_fee_to_validators"
-            ),
-        },
-    },
-    {
-        "description": """
-            Online validator reward aggregation
-        """,
-        "policies": {
-            "calculate_total_online_validator_rewards": metrics.policy_total_online_validator_rewards,
-        },
-        "variables": {
-            "total_online_validator_rewards": update_from_signal(
-                "total_online_validator_rewards"
-            ),
-        },
-    },
-    {
-        "description": """
-            Accounting of Ethereum issuance & inflation
-        """,
-        "policies": {
-            "issuance": ethereum.policy_network_issuance,
-        },
-        "variables": {
-            "eth_supply": ethereum.update_eth_supply,
-            "supply_inflation": metrics.update_supply_inflation,
-            "network_issuance": update_from_signal("network_issuance"),
-            "pow_issuance": update_from_signal("pow_issuance"),
-        },
-    },
-    {
-        "description": """
-            Accounting of validator costs and online validator rewards
-        """,
-        "post_processing": False,
-        "policies": {
-            "metric_validator_costs": metrics.policy_validator_costs,
-        },
-        "variables": {
-            "validator_count_distribution": update_from_signal(
-                "validator_count_distribution"
-            ),
-            "validator_hardware_costs": update_from_signal("validator_hardware_costs"),
-            "validator_cloud_costs": update_from_signal("validator_cloud_costs"),
-            "validator_third_party_costs": update_from_signal(
-                "validator_third_party_costs"
-            ),
-            "validator_costs": update_from_signal("validator_costs"),
-            "total_network_costs": update_from_signal("total_network_costs"),
-        },
-    },
-    {
-        "description": """
-            Accounting of validator yield metrics
-        """,
-        "post_processing": False,
-        "policies": {
-            "yields": metrics.policy_validator_yields,
-        },
-        "variables": {
-            "validator_eth_staked": update_from_signal("validator_eth_staked"),
-            "validator_revenue": update_from_signal("validator_revenue"),
-            "validator_profit": update_from_signal("validator_profit"),
-            "validator_revenue_yields": update_from_signal("validator_revenue_yields"),
-            "validator_profit_yields": update_from_signal("validator_profit_yields"),
-            "total_revenue": update_from_signal("total_revenue"),
-            "total_profit": update_from_signal("total_profit"),
-            "total_revenue_yields": update_from_signal("total_revenue_yields"),
-            "total_profit_yields": update_from_signal("total_profit_yields"),
-        },
-    },
-]
-
-# Conditionally update the order of the State Update Blocks using a ternary operator
-_state_update_blocks = (
-    [
-        state_update_block_stages,
-        state_update_block_staking,
-        state_update_block_validators,
-    ]
-    + _state_update_blocks
-    if parameters["eth_staked_process"][0](0, 0) is not None
-    # If driving with validator process, switch staking and validator blocks
-    else [
-        state_update_block_stages,
-        state_update_block_validators,
-        state_update_block_staking,
-    ]
-    + _state_update_blocks
-)
-
-# Split the state update blocks into those used during the simulation (state_update_blocks)
-# and those used in post-processing to calculate the system metrics (post_processing_blocks)
-state_update_blocks = [
-    block for block in _state_update_blocks if not block.get("post_processing", False)
-]
-post_processing_blocks = [
-    block for block in _state_update_blocks if block.get("post_processing", False)
-]
-
-
-
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/state_variables.html b/docs/model/state_variables.html deleted file mode 100644 index 0d28d496..00000000 --- a/docs/model/state_variables.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - -model.state_variables API documentation - - - - - - - - - - - -
-
-
-

Module model.state_variables

-
-
-

Definition of State Variables, their types, and default values.

-

By using a dataclass to represent the State Variables: -* We can use types for Python type hints -* Set default values -* Ensure that all State Variables are initialized

-
- -Expand source code - -
"""
-Definition of State Variables, their types, and default values.
-
-By using a dataclass to represent the State Variables:
-* We can use types for Python type hints
-* Set default values
-* Ensure that all State Variables are initialized
-"""
-
-
-import numpy as np
-from dataclasses import dataclass
-from datetime import datetime
-
-import model.constants as constants
-import data.api.beaconchain as beaconchain
-import data.api.etherscan as etherscan
-from model.system_parameters import validator_environments
-from model.types import (
-    Gwei,
-    Gwei_per_Gas,
-    ETH,
-    USD,
-    USD_per_ETH,
-    Percentage,
-    Stage,
-)
-from data.historical_values import eth_price_mean, eth_price_min, eth_price_max
-
-
-# Get number of validator environments for initializing Numpy array size
-number_of_validator_environments = len(validator_environments)
-
-# Intial state from external live data source, setting a default in case API call fails
-number_of_validators: int = beaconchain.get_validators_count(default=156_250)
-eth_staked: ETH = (
-    beaconchain.get_total_validator_balance(default=5_000_000e9) / constants.gwei
-)
-eth_supply: ETH = etherscan.get_eth_supply(default=116_250_000e18) / constants.wei
-
-
-@dataclass
-class StateVariables:
-    """State Variables
-    Each State Variable is defined as:
-    state variable key: state variable type = default state variable value
-    """
-
-    # Time state variables
-    stage: Stage = None
-    """
-    The stage of the network upgrade process.
-
-    By default set to PROOF_OF_STAKE Stage, where EIP1559 is enabled and POW issuance is disabled.
-
-    Otherwise set to ALL Stage, which transitions through each stage, updating the `stage` State Variable.
-
-    See model.types.Stage Enum for further documentation.
-    """
-    timestamp: datetime = None
-    """
-    The timestamp for each timestep as a Python `datetime` object, starting from `date_start` Parameter.
-    """
-
-    # Ethereum state variables
-    eth_price: USD_per_ETH = eth_price_mean
-    """The ETH spot price"""
-    eth_supply: ETH = eth_supply
-    """The total ETH supply"""
-    eth_staked: ETH = eth_staked
-    """The total ETH staked as part of the Proof of Stake system"""
-    supply_inflation: Percentage = 0
-    """The annualized ETH supply inflation rate"""
-    network_issuance: ETH = 0
-    """The total network issuance in ETH"""
-    pow_issuance: ETH = 0
-    """The total Proof of Work issuance in ETH"""
-
-    # Validator state variables
-    number_of_validators_in_activation_queue: int = 0
-    """The number of validators in activation queue"""
-    average_effective_balance: Gwei = 32 * constants.gwei
-    """The validator average effective balance"""
-    number_of_validators: int = number_of_validators
-    """The total number of validators"""
-    number_of_validators_online: int = 0
-    """The total number of online validators"""
-    number_of_validators_offline: int = 0
-    """The total number of offline validators"""
-
-    # Reward and penalty state variables
-    base_reward: Gwei = 0
-    """
-    Validator rewards and penalties are calculated in terms of the base reward.
-    Under perfect network conditions, each validator should receive 1 base reward per epoch for performing their duties.
-    """
-    validating_rewards: Gwei = 0
-    """The total rewards received for PoS validation (attestation, block proposal, sync vote)"""
-    validating_penalties: Gwei = 0
-    """The total penalties received for failing to perform PoS validation duties (attestation, sync vote)"""
-    source_reward: Gwei = 0
-    """The total rewards received for getting a source vote in time and correctly"""
-    target_reward: Gwei = 0
-    """The total rewards received for getting a target vote in time and correctly"""
-    head_reward: Gwei = 0
-    """The total rewards received for getting a head vote in time and correctly"""
-    block_proposer_reward: Gwei = 0
-    """The total rewards received for successfully proposing a block"""
-    sync_reward: Gwei = 0
-    """The total rewards received for attesting as part of a sync committee"""
-    attestation_penalties: Gwei = 0
-    """The total penalties received for failing to perform attestation duties"""
-    sync_committee_penalties: Gwei = 0
-    """The total penalties received for failing to perform sync committee duties"""
-
-    # Slashing state variables
-    amount_slashed: Gwei = 0
-    """The total penalties applied for slashable offences"""
-    whistleblower_rewards: Gwei = 0
-    """The total rewards received as a proportion of the effective balance of the slashed validators"""
-
-    # EIP1559 state variables
-    base_fee_per_gas: Gwei_per_Gas = 1
-    """The base fee burned, in Gwei per gas, dynamically updated for each block"""
-    total_base_fee: Gwei = 0
-    """The total base fee burned for all transactions included in blockspace"""
-    total_priority_fee_to_miners: Gwei = 0
-    """"The total priority fee to miners pre-merge for all transactions included in blockspace"""
-    total_priority_fee_to_validators: Gwei = 0
-    """"The total priority fee to validators post-merge for all transactions included in blockspace"""
-
-    # System metric state variables
-    validator_eth_staked: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The ETH staked per validator environment"""
-    validator_revenue: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total revenue (income received) for performing PoS duties per validator environment"""
-    validator_profit: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total profit (income received - costs) per validator environment"""
-    validator_revenue_yields: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total annualized revenue (income received) yields (percentage of investment amount)
-    per validator environment"""
-    validator_profit_yields: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total annualized profit (income received - costs) yields (percentage of investment amount)
-    per validator environment"""
-
-    validator_count_distribution: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total number of validators per validator environment"""
-    validator_hardware_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator hardware operation costs per validator environment"""
-    validator_cloud_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator cloud operation costs per validator environment"""
-    validator_third_party_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator third-party fee costs validator environment"""
-    validator_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator costs validator environment"""
-
-    total_online_validator_rewards: Gwei = 0
-    """The total rewards received by online validators"""
-    total_network_costs: USD = 0
-    """The total validator operational costs for securing the network"""
-    total_revenue: USD = 0
-    """The total validator revenue (income received)"""
-    total_profit: USD = 0
-    """The total validator profit (income received - costs)"""
-    total_revenue_yields: Percentage = 0
-    """Annualized revenue (income received) for all validators"""
-    total_profit_yields: Percentage = 0
-    """Annualized profit (income received - costs) for all validators"""
-
-
-# Initialize State Variables instance with default values
-initial_state = StateVariables().__dict__
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class StateVariables -(stage: Stage = None, timestamp: datetime.datetime = None, eth_price: float = 1251.477131147541, eth_supply: float = 116678363.1865, eth_staked: float = 6314465.175230042, supply_inflation: float = 0, network_issuance: float = 0, pow_issuance: float = 0, number_of_validators_in_activation_queue: int = 0, average_effective_balance: float = 32000000000.0, number_of_validators: int = 191596, number_of_validators_online: int = 0, number_of_validators_offline: int = 0, base_reward: float = 0, validating_rewards: float = 0, validating_penalties: float = 0, source_reward: float = 0, target_reward: float = 0, head_reward: float = 0, block_proposer_reward: float = 0, sync_reward: float = 0, attestation_penalties: float = 0, sync_committee_penalties: float = 0, amount_slashed: float = 0, whistleblower_rewards: float = 0, base_fee_per_gas: float = 1, total_base_fee: float = 0, total_priority_fee_to_miners: float = 0, total_priority_fee_to_validators: float = 0, validator_eth_staked: numpy.ndarray = array([[0], -[0], -[0], -[0], -[0], -[0], -[0]]), validator_revenue: numpy.ndarray = array([[0], -[0], -[0], -[0], -[0], -[0], -[0]]), validator_profit: numpy.ndarray = array([[0], -[0], -[0], -[0], -[0], -[0], -[0]]), validator_revenue_yields: numpy.ndarray = array([[0], -[0], -[0], -[0], -[0], -[0], -[0]]), validator_profit_yields: numpy.ndarray = array([[0], -[0], -[0], -[0], -[0], -[0], -[0]]), validator_count_distribution: numpy.ndarray = array([[0], -[0], -[0], -[0], -[0], -[0], -[0]]), validator_hardware_costs: numpy.ndarray = array([[0.], -[0.], -[0.], -[0.], -[0.], -[0.], -[0.]]), validator_cloud_costs: numpy.ndarray = array([[0.], -[0.], -[0.], -[0.], -[0.], -[0.], -[0.]]), validator_third_party_costs: numpy.ndarray = array([[0.], -[0.], -[0.], -[0.], -[0.], -[0.], -[0.]]), validator_costs: numpy.ndarray = array([[0.], -[0.], -[0.], -[0.], -[0.], -[0.], -[0.]]), total_online_validator_rewards: float = 0, total_network_costs: float = 0, total_revenue: float = 0, total_profit: float = 0, total_revenue_yields: float = 0, total_profit_yields: float = 0) -
-
-

State Variables -Each State Variable is defined as: -state variable key: state variable type = default state variable value

-
- -Expand source code - -
class StateVariables:
-    """State Variables
-    Each State Variable is defined as:
-    state variable key: state variable type = default state variable value
-    """
-
-    # Time state variables
-    stage: Stage = None
-    """
-    The stage of the network upgrade process.
-
-    By default set to PROOF_OF_STAKE Stage, where EIP1559 is enabled and POW issuance is disabled.
-
-    Otherwise set to ALL Stage, which transitions through each stage, updating the `stage` State Variable.
-
-    See model.types.Stage Enum for further documentation.
-    """
-    timestamp: datetime = None
-    """
-    The timestamp for each timestep as a Python `datetime` object, starting from `date_start` Parameter.
-    """
-
-    # Ethereum state variables
-    eth_price: USD_per_ETH = eth_price_mean
-    """The ETH spot price"""
-    eth_supply: ETH = eth_supply
-    """The total ETH supply"""
-    eth_staked: ETH = eth_staked
-    """The total ETH staked as part of the Proof of Stake system"""
-    supply_inflation: Percentage = 0
-    """The annualized ETH supply inflation rate"""
-    network_issuance: ETH = 0
-    """The total network issuance in ETH"""
-    pow_issuance: ETH = 0
-    """The total Proof of Work issuance in ETH"""
-
-    # Validator state variables
-    number_of_validators_in_activation_queue: int = 0
-    """The number of validators in activation queue"""
-    average_effective_balance: Gwei = 32 * constants.gwei
-    """The validator average effective balance"""
-    number_of_validators: int = number_of_validators
-    """The total number of validators"""
-    number_of_validators_online: int = 0
-    """The total number of online validators"""
-    number_of_validators_offline: int = 0
-    """The total number of offline validators"""
-
-    # Reward and penalty state variables
-    base_reward: Gwei = 0
-    """
-    Validator rewards and penalties are calculated in terms of the base reward.
-    Under perfect network conditions, each validator should receive 1 base reward per epoch for performing their duties.
-    """
-    validating_rewards: Gwei = 0
-    """The total rewards received for PoS validation (attestation, block proposal, sync vote)"""
-    validating_penalties: Gwei = 0
-    """The total penalties received for failing to perform PoS validation duties (attestation, sync vote)"""
-    source_reward: Gwei = 0
-    """The total rewards received for getting a source vote in time and correctly"""
-    target_reward: Gwei = 0
-    """The total rewards received for getting a target vote in time and correctly"""
-    head_reward: Gwei = 0
-    """The total rewards received for getting a head vote in time and correctly"""
-    block_proposer_reward: Gwei = 0
-    """The total rewards received for successfully proposing a block"""
-    sync_reward: Gwei = 0
-    """The total rewards received for attesting as part of a sync committee"""
-    attestation_penalties: Gwei = 0
-    """The total penalties received for failing to perform attestation duties"""
-    sync_committee_penalties: Gwei = 0
-    """The total penalties received for failing to perform sync committee duties"""
-
-    # Slashing state variables
-    amount_slashed: Gwei = 0
-    """The total penalties applied for slashable offences"""
-    whistleblower_rewards: Gwei = 0
-    """The total rewards received as a proportion of the effective balance of the slashed validators"""
-
-    # EIP1559 state variables
-    base_fee_per_gas: Gwei_per_Gas = 1
-    """The base fee burned, in Gwei per gas, dynamically updated for each block"""
-    total_base_fee: Gwei = 0
-    """The total base fee burned for all transactions included in blockspace"""
-    total_priority_fee_to_miners: Gwei = 0
-    """"The total priority fee to miners pre-merge for all transactions included in blockspace"""
-    total_priority_fee_to_validators: Gwei = 0
-    """"The total priority fee to validators post-merge for all transactions included in blockspace"""
-
-    # System metric state variables
-    validator_eth_staked: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The ETH staked per validator environment"""
-    validator_revenue: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total revenue (income received) for performing PoS duties per validator environment"""
-    validator_profit: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total profit (income received - costs) per validator environment"""
-    validator_revenue_yields: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total annualized revenue (income received) yields (percentage of investment amount)
-    per validator environment"""
-    validator_profit_yields: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total annualized profit (income received - costs) yields (percentage of investment amount)
-    per validator environment"""
-
-    validator_count_distribution: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=int
-    )
-    """The total number of validators per validator environment"""
-    validator_hardware_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator hardware operation costs per validator environment"""
-    validator_cloud_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator cloud operation costs per validator environment"""
-    validator_third_party_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator third-party fee costs validator environment"""
-    validator_costs: np.ndarray = np.zeros(
-        (number_of_validator_environments, 1), dtype=USD
-    )
-    """The total validator costs validator environment"""
-
-    total_online_validator_rewards: Gwei = 0
-    """The total rewards received by online validators"""
-    total_network_costs: USD = 0
-    """The total validator operational costs for securing the network"""
-    total_revenue: USD = 0
-    """The total validator revenue (income received)"""
-    total_profit: USD = 0
-    """The total validator profit (income received - costs)"""
-    total_revenue_yields: Percentage = 0
-    """Annualized revenue (income received) for all validators"""
-    total_profit_yields: Percentage = 0
-    """Annualized profit (income received - costs) for all validators"""
-
-

Class variables

-
-
var amount_slashed : float
-
-

The total penalties applied for slashable offences

-
-
var attestation_penalties : float
-
-

The total penalties received for failing to perform attestation duties

-
-
var average_effective_balance : float
-
-

The validator average effective balance

-
-
var base_fee_per_gas : float
-
-

The base fee burned, in Gwei per gas, dynamically updated for each block

-
-
var base_reward : float
-
-

Validator rewards and penalties are calculated in terms of the base reward. -Under perfect network conditions, each validator should receive 1 base reward per epoch for performing their duties.

-
-
var block_proposer_reward : float
-
-

The total rewards received for successfully proposing a block

-
-
var eth_price : float
-
-

The ETH spot price

-
-
var eth_staked : float
-
-

The total ETH staked as part of the Proof of Stake system

-
-
var eth_supply : float
-
-

The total ETH supply

-
-
var head_reward : float
-
-

The total rewards received for getting a head vote in time and correctly

-
-
var network_issuance : float
-
-

The total network issuance in ETH

-
-
var number_of_validators : int
-
-

The total number of validators

-
-
var number_of_validators_in_activation_queue : int
-
-

The number of validators in activation queue

-
-
var number_of_validators_offline : int
-
-

The total number of offline validators

-
-
var number_of_validators_online : int
-
-

The total number of online validators

-
-
var pow_issuance : float
-
-

The total Proof of Work issuance in ETH

-
-
var source_reward : float
-
-

The total rewards received for getting a source vote in time and correctly

-
-
var stageStage
-
-

The stage of the network upgrade process.

-

By default set to PROOF_OF_STAKE Stage, where EIP1559 is enabled and POW issuance is disabled.

-

Otherwise set to ALL Stage, which transitions through each stage, updating the stage State Variable.

-

See model.types.Stage Enum for further documentation.

-
-
var supply_inflation : float
-
-

The annualized ETH supply inflation rate

-
-
var sync_committee_penalties : float
-
-

The total penalties received for failing to perform sync committee duties

-
-
var sync_reward : float
-
-

The total rewards received for attesting as part of a sync committee

-
-
var target_reward : float
-
-

The total rewards received for getting a target vote in time and correctly

-
-
var timestamp : datetime.datetime
-
-

The timestamp for each timestep as a Python datetime object, starting from date_start Parameter.

-
-
var total_base_fee : float
-
-

The total base fee burned for all transactions included in blockspace

-
-
var total_network_costs : float
-
-

The total validator operational costs for securing the network

-
-
var total_online_validator_rewards : float
-
-

The total rewards received by online validators

-
-
var total_priority_fee_to_miners : float
-
-

"The total priority fee to miners pre-merge for all transactions included in blockspace

-
-
var total_priority_fee_to_validators : float
-
-

"The total priority fee to validators post-merge for all transactions included in blockspace

-
-
var total_profit : float
-
-

The total validator profit (income received - costs)

-
-
var total_profit_yields : float
-
-

Annualized profit (income received - costs) for all validators

-
-
var total_revenue : float
-
-

The total validator revenue (income received)

-
-
var total_revenue_yields : float
-
-

Annualized revenue (income received) for all validators

-
-
var validating_penalties : float
-
-

The total penalties received for failing to perform PoS validation duties (attestation, sync vote)

-
-
var validating_rewards : float
-
-

The total rewards received for PoS validation (attestation, block proposal, sync vote)

-
-
var validator_cloud_costs : numpy.ndarray
-
-

The total validator cloud operation costs per validator environment

-
-
var validator_costs : numpy.ndarray
-
-

The total validator costs validator environment

-
-
var validator_count_distribution : numpy.ndarray
-
-

The total number of validators per validator environment

-
-
var validator_eth_staked : numpy.ndarray
-
-

The ETH staked per validator environment

-
-
var validator_hardware_costs : numpy.ndarray
-
-

The total validator hardware operation costs per validator environment

-
-
var validator_profit : numpy.ndarray
-
-

The total profit (income received - costs) per validator environment

-
-
var validator_profit_yields : numpy.ndarray
-
-

The total annualized profit (income received - costs) yields (percentage of investment amount) -per validator environment

-
-
var validator_revenue : numpy.ndarray
-
-

The total revenue (income received) for performing PoS duties per validator environment

-
-
var validator_revenue_yields : numpy.ndarray
-
-

The total annualized revenue (income received) yields (percentage of investment amount) -per validator environment

-
-
var validator_third_party_costs : numpy.ndarray
-
-

The total validator third-party fee costs validator environment

-
-
var whistleblower_rewards : float
-
-

The total rewards received as a proportion of the effective balance of the slashed validators

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/stochastic_processes.html b/docs/model/stochastic_processes.html deleted file mode 100644 index c495ede9..00000000 --- a/docs/model/stochastic_processes.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - -model.stochastic_processes API documentation - - - - - - - - - - - -
-
-
-

Module model.stochastic_processes

-
-
-

Helper functions to generate stochastic environmental processes

-
- -Expand source code - -
"""
-Helper functions to generate stochastic environmental processes
-"""
-
-import numpy as np
-from stochastic import processes
-
-import experiments.simulation_configuration as simulation
-from experiments.utils import rng_generator
-
-
-def create_eth_price_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    minimum_eth_price=1500,
-):
-    """Configure environmental ETH price process
-
-    > A Brownian excursion is a Brownian bridge from (0, 0) to (t, 0) which is conditioned to be non-negative on the interval [0, t].
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.BrownianExcursion(t=(timesteps * dt), rng=rng)
-    samples = process.sample(timesteps * dt + 1)
-    maximum_eth_price = max(samples)
-    samples = [
-        minimum_eth_price + eth_price_sample / maximum_eth_price * minimum_eth_price
-        for eth_price_sample in samples
-    ]
-    return samples
-
-
-def create_validator_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    validator_adoption_rate=4,
-):
-    """Configure environmental validator staking process
-
-    > A Poisson process with rate lambda is a count of occurrences of i.i.d. exponential random variables with mean 1/lambda. This class generates samples of times for which cumulative exponential random variables occur.
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.PoissonProcess(
-        rate=1 / validator_adoption_rate, rng=rng
-    )
-    samples = process.sample(timesteps * dt + 1)
-    samples = np.diff(samples)
-    samples = [int(sample) for sample in samples]
-    return samples
-
-
-def create_stochastic_process_realizations(
-    process,
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    runs=5,
-):
-    """Create stochastic process realizations
-
-    Using the stochastic processes defined in `processes` module, create random number generator (RNG) seeds,
-    and use RNG to pre-generate samples for number of simulation timesteps.
-    """
-
-    switcher = {
-        "eth_price_samples": [
-            create_eth_price_process(timesteps=timesteps, dt=dt, rng=rng_generator())
-            for _ in range(runs)
-        ],
-        "validator_samples": [
-            create_validator_process(timesteps=timesteps, dt=dt, rng=rng_generator())
-            for _ in range(runs)
-        ],
-        "validator_uptime_samples": [
-            rng_generator().uniform(0.96, 0.99, timesteps * dt + 1) for _ in range(runs)
-        ],
-    }
-
-    return switcher.get(process, "Invalid Process")
-
-
-
-
-
-
-
-

Functions

-
-
-def create_eth_price_process(timesteps=360, dt=225, rng=Generator(PCG64), minimum_eth_price=1500) -
-
-

Configure environmental ETH price process

-
-

A Brownian excursion is a Brownian bridge from (0, 0) to (t, 0) which is conditioned to be non-negative on the interval [0, t].

-
-

See https://stochastic.readthedocs.io/en/latest/continuous.html

-
- -Expand source code - -
def create_eth_price_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    minimum_eth_price=1500,
-):
-    """Configure environmental ETH price process
-
-    > A Brownian excursion is a Brownian bridge from (0, 0) to (t, 0) which is conditioned to be non-negative on the interval [0, t].
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.BrownianExcursion(t=(timesteps * dt), rng=rng)
-    samples = process.sample(timesteps * dt + 1)
-    maximum_eth_price = max(samples)
-    samples = [
-        minimum_eth_price + eth_price_sample / maximum_eth_price * minimum_eth_price
-        for eth_price_sample in samples
-    ]
-    return samples
-
-
-
-def create_stochastic_process_realizations(process, timesteps=360, dt=225, runs=5) -
-
-

Create stochastic process realizations

-

Using the stochastic processes defined in processes module, create random number generator (RNG) seeds, -and use RNG to pre-generate samples for number of simulation timesteps.

-
- -Expand source code - -
def create_stochastic_process_realizations(
-    process,
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    runs=5,
-):
-    """Create stochastic process realizations
-
-    Using the stochastic processes defined in `processes` module, create random number generator (RNG) seeds,
-    and use RNG to pre-generate samples for number of simulation timesteps.
-    """
-
-    switcher = {
-        "eth_price_samples": [
-            create_eth_price_process(timesteps=timesteps, dt=dt, rng=rng_generator())
-            for _ in range(runs)
-        ],
-        "validator_samples": [
-            create_validator_process(timesteps=timesteps, dt=dt, rng=rng_generator())
-            for _ in range(runs)
-        ],
-        "validator_uptime_samples": [
-            rng_generator().uniform(0.96, 0.99, timesteps * dt + 1) for _ in range(runs)
-        ],
-    }
-
-    return switcher.get(process, "Invalid Process")
-
-
-
-def create_validator_process(timesteps=360, dt=225, rng=Generator(PCG64), validator_adoption_rate=4) -
-
-

Configure environmental validator staking process

-
-

A Poisson process with rate lambda is a count of occurrences of i.i.d. exponential random variables with mean 1/lambda. This class generates samples of times for which cumulative exponential random variables occur.

-
-

See https://stochastic.readthedocs.io/en/latest/continuous.html

-
- -Expand source code - -
def create_validator_process(
-    timesteps=simulation.TIMESTEPS,
-    dt=simulation.DELTA_TIME,
-    rng=np.random.default_rng(1),
-    validator_adoption_rate=4,
-):
-    """Configure environmental validator staking process
-
-    > A Poisson process with rate lambda is a count of occurrences of i.i.d. exponential random variables with mean 1/lambda. This class generates samples of times for which cumulative exponential random variables occur.
-
-    See https://stochastic.readthedocs.io/en/latest/continuous.html
-    """
-    process = processes.continuous.PoissonProcess(
-        rate=1 / validator_adoption_rate, rng=rng
-    )
-    samples = process.sample(timesteps * dt + 1)
-    samples = np.diff(samples)
-    samples = [int(sample) for sample in samples]
-    return samples
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/system_parameters.html b/docs/model/system_parameters.html deleted file mode 100644 index 47580d4b..00000000 --- a/docs/model/system_parameters.html +++ /dev/null @@ -1,1021 +0,0 @@ - - - - - - -model.system_parameters API documentation - - - - - - - - - - - -
-
-
-

Module model.system_parameters

-
-
-

Definition of System Parameters, their types, and default values.

-

By using a dataclass to represent the System Parameters: -* We can use types for Python type hints -* Set default values -* Ensure that all System Parameters are initialized

-
- -Expand source code - -
"""
-Definition of System Parameters, their types, and default values.
-
-By using a dataclass to represent the System Parameters:
-* We can use types for Python type hints
-* Set default values
-* Ensure that all System Parameters are initialized
-"""
-
-
-import logging
-import numpy as np
-from dataclasses import dataclass
-from datetime import datetime
-
-import model.constants as constants
-import experiments.simulation_configuration as simulation
-from model.types import (
-    Run,
-    Timestep,
-    Percentage,
-    Gwei,
-    Gas,
-    Gwei_per_Gas,
-    ETH,
-    USD_per_epoch,
-    Percentage_per_epoch,
-    ValidatorEnvironment,
-    List,
-    Callable,
-    Epoch,
-    Stage,
-)
-from model.utils import default
-from data.historical_values import eth_price_mean
-
-
-# Configure validator environment distribution
-validator_environments = [
-    # Configure a custom validator environment using the following as a template:
-    # ValidatorEnvironment(
-    #     type="custom",
-    #     percentage_distribution=0.01,  # 1%
-    #     hardware_costs_per_epoch=0.0014,
-    #     cloud_costs_per_epoch=0,
-    #     third_party_costs_per_epoch=0,
-    # ),
-    ValidatorEnvironment(
-        type="diy_hardware",
-        percentage_distribution=0.37,
-        hardware_costs_per_epoch=0.0014,
-    ),
-    ValidatorEnvironment(
-        type="diy_cloud",
-        percentage_distribution=0.13,
-        cloud_costs_per_epoch=0.00027,
-    ),
-    ValidatorEnvironment(
-        type="pool_staas",
-        percentage_distribution=0.27,
-        third_party_costs_per_epoch=0.12,
-    ),
-    ValidatorEnvironment(
-        type="pool_hardware",
-        percentage_distribution=0.05,
-        hardware_costs_per_epoch=0.0007,
-    ),
-    ValidatorEnvironment(
-        type="pool_cloud",
-        percentage_distribution=0.02,
-        cloud_costs_per_epoch=0.00136,
-    ),
-    ValidatorEnvironment(
-        type="staas_full",
-        percentage_distribution=0.08,
-        third_party_costs_per_epoch=0.15,
-    ),
-    ValidatorEnvironment(
-        type="staas_self_custodied",
-        percentage_distribution=0.08,
-        third_party_costs_per_epoch=0.12,
-    ),
-]
-"""Validator environment configuration
-
-See ASSUMPTIONS.md document for details of validator environment configuration and assumptions.
-"""
-
-# Normalise percentage distribution to a total of 100%
-total_percentage_distribution = sum(
-    [validator.percentage_distribution for validator in validator_environments]
-)
-
-if total_percentage_distribution < 1:
-    logging.warning(
-        """
-        Parameter validator.percentage_distribution normalized due to sum not being equal to 100%
-        """
-    )
-    for validator in validator_environments:
-        validator.percentage_distribution /= total_percentage_distribution
-
-# Using list comprehension, map the validator types to each parameter
-validator_percentage_distribution = [
-    np.array(
-        [validator.percentage_distribution for validator in validator_environments],
-        dtype=Percentage,
-    )
-]
-validator_hardware_costs_per_epoch = [
-    np.array(
-        [validator.hardware_costs_per_epoch for validator in validator_environments],
-        dtype=USD_per_epoch,
-    )
-]
-validator_cloud_costs_per_epoch = [
-    np.array(
-        [validator.cloud_costs_per_epoch for validator in validator_environments],
-        dtype=USD_per_epoch,
-    )
-]
-validator_third_party_costs_per_epoch = [
-    np.array(
-        [validator.third_party_costs_per_epoch for validator in validator_environments],
-        dtype=Percentage_per_epoch,
-    )
-]
-
-
-@dataclass
-class Parameters:
-    """System Parameters
-    Each System Parameter is defined as:
-    system parameter key: system parameter type = default system parameter value
-
-    Because lists are mutable, we need to wrap each parameter list in the `default(...)` method.
-    """
-
-    # Time parameters
-    dt: List[Epoch] = default([simulation.DELTA_TIME])
-    """
-    Simulation timescale / timestep unit of time, in epochs.
-
-    Used to scale calculations that depend on the number of epochs that have passed.
-
-    For example, for dt = 100, each timestep equals 100 epochs.
-
-    By default set to constants.epochs_per_day (~= 225)
-    """
-
-    stage: List[Stage] = default([Stage.PROOF_OF_STAKE])
-    """
-    Which stage or stages of the network upgrade process to simulate.
-
-    By default set to PROOF_OF_STAKE stage, where EIP1559 is enabled and POW issuance is disabled.
-
-    See model.types.Stage Enum for further documentation.
-    """
-
-    date_start: List[datetime] = default([datetime.now()])
-    """Start date for simulation as Python datetime"""
-
-    date_eip1559: List[datetime] = default(
-        [datetime.strptime("2021/07/14", "%Y/%m/%d")]
-    )
-    """
-    EIP1559 activation date as Python datetime.
-
-    Source: https://github.com/ethereum/pm/issues/245#issuecomment-825751460
-    """
-
-    date_pos: List[datetime] = default([datetime.strptime("2021/12/1", "%Y/%m/%d")])
-    """
-    Eth1/Eth2 merge date as Python datetime, after which POW is disabled and POS is enabled.
-
-    Source: https://twitter.com/drakefjustin/status/1379052831982956547
-    """
-
-    # Environmental processes
-    eth_price_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda _run, _timestep: eth_price_mean]
-    )
-    """
-    A process that returns the ETH spot price at each epoch.
-    
-    By default set to average ETH price over the last 12 months from Etherscan.
-    """
-
-    eth_staked_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda _run, _timestep: None]
-    )
-    """
-    A process that returns the ETH staked at each epoch.
-
-    If set to `none`, the model is driven by the validator process,
-    where new validators enter the system and stake accordingly.
-
-    This process is used for simulating a series of ETH staked values directly.
-    """
-
-    validator_process: List[Callable[[Run, Timestep], int]] = default(
-        [
-            # From https://beaconscan.com/statistics as of 20/04/21
-            lambda _run, _timestep: 3,
-        ]
-    )
-    """
-    A process that returns the number of new validators per epoch.
-
-    Used if model not driven using `eth_staked_process`.
-
-    By default set to a static value from https://beaconscan.com/statistics.
-    """
-
-    # Ethereum system parameters
-    daily_pow_issuance: List[ETH] = default([13_550])
-    """
-    The average daily Proof of Work issuance in ETH.
-
-    See https://etherscan.io/chart/blockreward
-    """
-
-    # Parameters from the Eth2 specification
-    # Uppercase used for all parameters from Eth2 specification
-    BASE_REWARD_FACTOR: List[int] = default([64])
-    """
-    A parameter used to change the issuance rate of the Ethereum PoS system.
-
-    Most validator rewards and penalties are calculated in terms of the base reward.
-    """
-    MAX_EFFECTIVE_BALANCE: List[Gwei] = default([32 * constants.gwei])
-    """
-    A validators effective balance is used to calculate incentives, and for voting,
-    and is a value less than the total stake/balance.
-
-    The max effective balance of a validator is 32 ETH.
-    """
-    EFFECTIVE_BALANCE_INCREMENT: List[Gwei] = default([1 * constants.gwei])
-    """
-    A validators effective balance can only change in steps of EFFECTIVE_BALANCE_INCREMENT,
-    which reduces the computational load for state updates.
-    """
-    PROPOSER_REWARD_QUOTIENT: List[int] = default([8])
-    """
-    Used to calculate the proportion of rewards distributed between attesters and proposers.
-    """
-    WHISTLEBLOWER_REWARD_QUOTIENT: List[int] = default([512])
-    """
-    Used to calculate the proportion of the effective balance of the slashed validator
-    distributed between the whistleblower and the proposer.
-    """
-    MIN_SLASHING_PENALTY_QUOTIENT: List[int] = default([2 ** 6])
-    """
-    Used to calculate the penalty applied for a slashable offence.
-    """
-    PROPORTIONAL_SLASHING_MULTIPLIER: List[int] = default([2])
-    """
-    Scales the slashing penalty proportional to the total slashings for the current epoch
-
-    i.e. the more slashing events there are, the greater the individual penalty
-    """
-    TIMELY_HEAD_WEIGHT: List[int] = default([14])
-    """
-    Used to calculate the reward received for getting a head vote in time and correctly.
-
-    `head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_SOURCE_WEIGHT: List[int] = default([14])
-    """
-    Used to calculate the reward received for getting a source vote in time and correctly.
-
-    `source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_TARGET_WEIGHT: List[int] = default([26])
-    """
-    Used to calculate the reward received for getting a target vote in time and correctly.
-
-    `target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    SYNC_REWARD_WEIGHT: List[int] = default([2])
-    """
-    Used to calculate the reward received for attesting as part of a sync committee.
-    """
-    PROPOSER_WEIGHT: List[int] = default([8])
-    """
-    Used to calculate the reward received for successfully proposing a block.
-    """
-    WEIGHT_DENOMINATOR: List[int] = default([64])
-    """
-    Used as the denominator in incentive calculations to calculate reward and penalty proportions.
-    """
-    MIN_PER_EPOCH_CHURN_LIMIT: List[int] = default([4])
-    """
-    Used to calculate the churn limit for validator entry and exit. The maximum number of validators that can
-    enter or exit the system per epoch.
-
-    In this system it is used for the validator activation queue process.
-    """
-    CHURN_LIMIT_QUOTIENT: List[int] = default([2 ** 16])
-    """
-    Used in the calculation of the churn limit to set a point at which the limit increases.
-    """
-    BASE_FEE_MAX_CHANGE_DENOMINATOR: List[int] = default([8])
-    """
-    Used to set the maximum rate at which the EIP1559 base fee can change per block, approx. 12.5%.
-    """
-    ELASTICITY_MULTIPLIER: List[int] = default([2])
-    """
-    Used to calculate gas limit from EIP1559 gas target
-    """
-
-    # Validator parameters
-    validator_uptime_process: List[Percentage] = default(
-        [lambda _run, _timestep: max(0.98, 2 / 3)]
-    )
-    """
-    The combination of validator internet, power, and technical uptime, as a percentage.
-
-    Minimum uptime is inactivity leak threshold = 2/3, as this model doesn't model the inactivity leak process.
-    """
-    validator_percentage_distribution: List[np.ndarray] = default(
-        validator_percentage_distribution
-    )
-    """
-    The percentage of validators in each environment, normalized to a total of 100%.
-
-    A vector with a value for each validator environment.
-    """
-    validator_hardware_costs_per_epoch: List[np.ndarray] = default(
-        validator_hardware_costs_per_epoch
-    )
-    """
-    The validator hardware costs per epoch in dollars.
-
-    A vector with a value for each validator environment.
-    """
-    validator_cloud_costs_per_epoch: List[np.ndarray] = default(
-        validator_cloud_costs_per_epoch
-    )
-    """
-    The validator cloud costs per epoch in dollars.
-
-    A vector with a value for each validator environment.
-    """
-    validator_third_party_costs_per_epoch: List[np.ndarray] = default(
-        validator_third_party_costs_per_epoch
-    )
-    """
-    The validator third-party costs as a percentage of total online validator rewards.
-
-    Used for expected Staking-as-a-Service fees.
-
-    A vector with a value for each validator environment.
-    """
-
-    # Rewards, penalties, and slashing
-    slashing_events_per_1000_epochs: List[int] = default([1])  # 1 / 1000 epochs
-    """
-    The number of slashing events per 1000 epochs.
-    """
-
-    # EIP1559 transaction pricing parameters
-    base_fee_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 70]  # Gwei per gas
-    )
-    """
-    The base fee burned, in Gwei per gas, for each transaction.
-
-    An average of 100 Gwei per gas expected to be set as transaction fee cap,
-    split between the base fee and priority fee - the fee cap less the base fee is sent as a priority fee to miners/validators.
-
-    Approximated using average gas price from https://etherscan.io/gastracker as of 20/04/21
-
-    An extract from https://notes.ethereum.org/@vbuterin/eip-1559-faq
-
-    > Each “full block” (ie. a block whose gas is 2x the TARGET) increases the BASEFEE by 1.125x,
-    > so a series of constant full blocks will increase the gas price by a factor of 10 every
-    > ~20 blocks (~4.3 min on average).
-    > Hence, periods of heavy on-chain load will not realistically last longer than ~5 minutes.
-    """
-
-    priority_fee_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 30]  # Gwei per gas
-    )
-    """
-    EIP1559 transaction pricing priority fee, in Gwei per gas.
-
-    Due to MEV, average priority fee expected to be higher than usual as bid for inclusion in blockscpace market.
-
-    The priority fee is the difference between the fee cap set per transaction, and the base fee.
-
-    For PoW system without MEV influence, the priority fee level compensates for uncle risk:
-    See https://notes.ethereum.org/@vbuterin/BkSQmQTS8#Why-would-miners-include-transactions-at-all
-    """
-
-    gas_target_process: List[Callable[[Run, Timestep], Gas]] = default(
-        [lambda _run, _timestep: 15e6]  # Gas per block
-    )
-    """
-    The long-term average gas target per block.
-
-    The current gas limit is replaced by two values:
-    * a “long-term average target” (equal to the current gas limit) == gas target
-    * a “hard per-block cap” (twice the current gas limit) == gas limit
-
-    EIP-1559 gas limit = gas_target * ELASTICITY_MULTIPLIER
-    See https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-
-# Initialize Parameters instance with default values
-parameters = Parameters().__dict__
-
-
-
-
-
-

Global variables

-
-
var validator_environments
-
-

Validator environment configuration

-

See ASSUMPTIONS.md document for details of validator environment configuration and assumptions.

-
-
-
-
-
-
-

Classes

-
-
-class Parameters -(dt: List[int] = <factory>, stage: List[Stage] = <factory>, date_start: List[datetime.datetime] = <factory>, date_eip1559: List[datetime.datetime] = <factory>, date_pos: List[datetime.datetime] = <factory>, eth_price_process: List[Callable[[int, int], float]] = <factory>, eth_staked_process: List[Callable[[int, int], float]] = <factory>, validator_process: List[Callable[[int, int], int]] = <factory>, daily_pow_issuance: List[float] = <factory>, BASE_REWARD_FACTOR: List[int] = <factory>, MAX_EFFECTIVE_BALANCE: List[float] = <factory>, EFFECTIVE_BALANCE_INCREMENT: List[float] = <factory>, PROPOSER_REWARD_QUOTIENT: List[int] = <factory>, WHISTLEBLOWER_REWARD_QUOTIENT: List[int] = <factory>, MIN_SLASHING_PENALTY_QUOTIENT: List[int] = <factory>, PROPORTIONAL_SLASHING_MULTIPLIER: List[int] = <factory>, TIMELY_HEAD_WEIGHT: List[int] = <factory>, TIMELY_SOURCE_WEIGHT: List[int] = <factory>, TIMELY_TARGET_WEIGHT: List[int] = <factory>, SYNC_REWARD_WEIGHT: List[int] = <factory>, PROPOSER_WEIGHT: List[int] = <factory>, WEIGHT_DENOMINATOR: List[int] = <factory>, MIN_PER_EPOCH_CHURN_LIMIT: List[int] = <factory>, CHURN_LIMIT_QUOTIENT: List[int] = <factory>, BASE_FEE_MAX_CHANGE_DENOMINATOR: List[int] = <factory>, ELASTICITY_MULTIPLIER: List[int] = <factory>, validator_uptime_process: List[float] = <factory>, validator_percentage_distribution: List[numpy.ndarray] = <factory>, validator_hardware_costs_per_epoch: List[numpy.ndarray] = <factory>, validator_cloud_costs_per_epoch: List[numpy.ndarray] = <factory>, validator_third_party_costs_per_epoch: List[numpy.ndarray] = <factory>, slashing_events_per_1000_epochs: List[int] = <factory>, base_fee_process: List[Callable[[int, int], float]] = <factory>, priority_fee_process: List[Callable[[int, int], float]] = <factory>, gas_target_process: List[Callable[[int, int], int]] = <factory>) -
-
-

System Parameters -Each System Parameter is defined as: -system parameter key: system parameter type = default system parameter value

-

Because lists are mutable, we need to wrap each parameter list in the default(…) method.

-
- -Expand source code - -
class Parameters:
-    """System Parameters
-    Each System Parameter is defined as:
-    system parameter key: system parameter type = default system parameter value
-
-    Because lists are mutable, we need to wrap each parameter list in the `default(...)` method.
-    """
-
-    # Time parameters
-    dt: List[Epoch] = default([simulation.DELTA_TIME])
-    """
-    Simulation timescale / timestep unit of time, in epochs.
-
-    Used to scale calculations that depend on the number of epochs that have passed.
-
-    For example, for dt = 100, each timestep equals 100 epochs.
-
-    By default set to constants.epochs_per_day (~= 225)
-    """
-
-    stage: List[Stage] = default([Stage.PROOF_OF_STAKE])
-    """
-    Which stage or stages of the network upgrade process to simulate.
-
-    By default set to PROOF_OF_STAKE stage, where EIP-1559 is enabled and POW issuance is disabled.
-
-    See model.types.Stage Enum for further documentation.
-    """
-
-    date_start: List[datetime] = default([datetime.now()])
-    """Start date for simulation as Python datetime"""
-
-    date_eip1559: List[datetime] = default(
-        [datetime.strptime("2021/07/14", "%Y/%m/%d")]
-    )
-    """
-    EIP-1559 activation date as Python datetime.
-
-    Source: https://github.com/ethereum/pm/issues/245#issuecomment-825751460
-    """
-
-    date_pos: List[datetime] = default([datetime.strptime("2021/12/1", "%Y/%m/%d")])
-    """
-    Eth1/Eth2 merge date as Python datetime, after which POW is disabled and POS is enabled.
-
-    Source: https://twitter.com/drakefjustin/status/1379052831982956547
-    """
-
-    # Environmental processes
-    eth_price_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda _run, _timestep: eth_price_mean]
-    )
-    """
-    A process that returns the ETH spot price at each epoch.
-    
-    By default set to average ETH price over the last 12 months from Etherscan.
-    """
-
-    eth_staked_process: List[Callable[[Run, Timestep], ETH]] = default(
-        [lambda _run, _timestep: None]
-    )
-    """
-    A process that returns the ETH staked at each epoch.
-
-    If set to `none`, the model is driven by the validator process,
-    where new validators enter the system and stake accordingly.
-
-    This process is used for simulating a series of ETH staked values directly.
-    """
-
-    validator_process: List[Callable[[Run, Timestep], int]] = default(
-        [
-            # From https://beaconscan.com/statistics as of 20/04/21
-            lambda _run, _timestep: 3,
-        ]
-    )
-    """
-    A process that returns the number of new validators per epoch.
-
-    Used if model not driven using `eth_staked_process`.
-
-    By default set to a static value from https://beaconscan.com/statistics.
-    """
-
-    # Ethereum system parameters
-    daily_pow_issuance: List[ETH] = default([13_550])
-    """
-    The average daily Proof of Work issuance in ETH.
-
-    See https://etherscan.io/chart/blockreward
-    """
-
-    # Parameters from the Eth2 specification
-    # Uppercase used for all parameters from Eth2 specification
-    BASE_REWARD_FACTOR: List[int] = default([64])
-    """
-    A parameter used to change the issuance rate of the Ethereum PoS system.
-
-    Most validator rewards and penalties are calculated in terms of the base reward.
-    """
-    MAX_EFFECTIVE_BALANCE: List[Gwei] = default([32 * constants.gwei])
-    """
-    A validators effective balance is used to calculate incentives, and for voting,
-    and is a value less than the total stake/balance.
-
-    The max effective balance of a validator is 32 ETH.
-    """
-    EFFECTIVE_BALANCE_INCREMENT: List[Gwei] = default([1 * constants.gwei])
-    """
-    A validators effective balance can only change in steps of EFFECTIVE_BALANCE_INCREMENT,
-    which reduces the computational load for state updates.
-    """
-    PROPOSER_REWARD_QUOTIENT: List[int] = default([8])
-    """
-    Used to calculate the proportion of rewards distributed between attesters and proposers.
-    """
-    WHISTLEBLOWER_REWARD_QUOTIENT: List[int] = default([512])
-    """
-    Used to calculate the proportion of the effective balance of the slashed validator
-    distributed between the whistleblower and the proposer.
-    """
-    MIN_SLASHING_PENALTY_QUOTIENT: List[int] = default([2 ** 6])
-    """
-    Used to calculate the penalty applied for a slashable offence.
-    """
-    PROPORTIONAL_SLASHING_MULTIPLIER: List[int] = default([2])
-    """
-    Scales the slashing penalty proportional to the total slashings for the current epoch
-
-    i.e. the more slashing events there are, the greater the individual penalty
-    """
-    TIMELY_HEAD_WEIGHT: List[int] = default([14])
-    """
-    Used to calculate the reward received for getting a head vote in time and correctly.
-
-    `head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_SOURCE_WEIGHT: List[int] = default([14])
-    """
-    Used to calculate the reward received for getting a source vote in time and correctly.
-
-    `source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    TIMELY_TARGET_WEIGHT: List[int] = default([26])
-    """
-    Used to calculate the reward received for getting a target vote in time and correctly.
-
-    `target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward`
-    """
-    SYNC_REWARD_WEIGHT: List[int] = default([2])
-    """
-    Used to calculate the reward received for attesting as part of a sync committee.
-    """
-    PROPOSER_WEIGHT: List[int] = default([8])
-    """
-    Used to calculate the reward received for successfully proposing a block.
-    """
-    WEIGHT_DENOMINATOR: List[int] = default([64])
-    """
-    Used as the denominator in incentive calculations to calculate reward and penalty proportions.
-    """
-    MIN_PER_EPOCH_CHURN_LIMIT: List[int] = default([4])
-    """
-    Used to calculate the churn limit for validator entry and exit. The maximum number of validators that can
-    enter or exit the system per epoch.
-
-    In this system it is used for the validator activation queue process.
-    """
-    CHURN_LIMIT_QUOTIENT: List[int] = default([2 ** 16])
-    """
-    Used in the calculation of the churn limit to set a point at which the limit increases.
-    """
-    BASE_FEE_MAX_CHANGE_DENOMINATOR: List[int] = default([8])
-    """
-    Used to set the maximum rate at which the EIP-1559 base fee can change per block, approx. 12.5%.
-    """
-    ELASTICITY_MULTIPLIER: List[int] = default([2])
-    """
-    Used to calculate gas limit from EIP-1559 gas target
-    """
-
-    # Validator parameters
-    validator_uptime_process: List[Percentage] = default(
-        [lambda _run, _timestep: max(0.98, 2 / 3)]
-    )
-    """
-    The combination of validator internet, power, and technical uptime, as a percentage.
-
-    Minimum uptime is inactivity leak threshold = 2/3, as this model doesn't model the inactivity leak process.
-    """
-    validator_percentage_distribution: List[np.ndarray] = default(
-        validator_percentage_distribution
-    )
-    """
-    The percentage of validators in each environment, normalized to a total of 100%.
-
-    A vector with a value for each validator environment.
-    """
-    validator_hardware_costs_per_epoch: List[np.ndarray] = default(
-        validator_hardware_costs_per_epoch
-    )
-    """
-    The validator hardware costs per epoch in dollars.
-
-    A vector with a value for each validator environment.
-    """
-    validator_cloud_costs_per_epoch: List[np.ndarray] = default(
-        validator_cloud_costs_per_epoch
-    )
-    """
-    The validator cloud costs per epoch in dollars.
-
-    A vector with a value for each validator environment.
-    """
-    validator_third_party_costs_per_epoch: List[np.ndarray] = default(
-        validator_third_party_costs_per_epoch
-    )
-    """
-    The validator third-party costs as a percentage of total online validator rewards.
-
-    Used for expected Staking-as-a-Service fees.
-
-    A vector with a value for each validator environment.
-    """
-
-    # Rewards, penalties, and slashing
-    slashing_events_per_1000_epochs: List[int] = default([1])  # 1 / 1000 epochs
-    """
-    The number of slashing events per 1000 epochs.
-    """
-
-    # EIP-1559 transaction pricing parameters
-    base_fee_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 70]  # Gwei per gas
-    )
-    """
-    The base fee burned, in Gwei per gas, for each transaction.
-
-    An average of 100 Gwei per gas expected to be set as transaction fee cap,
-    split between the base fee and priority fee - the fee cap less the base fee is sent as a priority fee to miners/validators.
-
-    Approximated using average gas price from https://etherscan.io/gastracker as of 20/04/21
-
-    An extract from https://notes.ethereum.org/@vbuterin/eip-1559-faq
-
-    > Each “full block” (ie. a block whose gas is 2x the TARGET) increases the BASEFEE by 1.125x,
-    > so a series of constant full blocks will increase the gas price by a factor of 10 every
-    > ~20 blocks (~4.3 min on average).
-    > Hence, periods of heavy on-chain load will not realistically last longer than ~5 minutes.
-    """
-
-    priority_fee_process: List[Callable[[Run, Timestep], Gwei_per_Gas]] = default(
-        [lambda _run, _timestep: 30]  # Gwei per gas
-    )
-    """
-    EIP-1559 transaction pricing priority fee, in Gwei per gas.
-
-    Due to MEV, average priority fee expected to be higher than usual as bid for inclusion in blockscpace market.
-
-    The priority fee is the difference between the fee cap set per transaction, and the base fee.
-
-    For PoW system without MEV influence, the priority fee level compensates for uncle risk:
-    See https://notes.ethereum.org/@vbuterin/BkSQmQTS8#Why-would-miners-include-transactions-at-all
-    """
-
-    gas_target_process: List[Callable[[Run, Timestep], Gas]] = default(
-        [lambda _run, _timestep: 15e6]  # Gas per block
-    )
-    """
-    The long-term average gas target per block.
-
-    The current gas limit is replaced by two values:
-    * a “long-term average target” (equal to the current gas limit) == gas target
-    * a “hard per-block cap” (twice the current gas limit) == gas limit
-
-    EIP-1559 gas limit = gas_target * ELASTICITY_MULTIPLIER
-    See https://eips.ethereum.org/EIPS/eip-1559
-    """
-
-

Class variables

-
-
var BASE_FEE_MAX_CHANGE_DENOMINATOR : List[int]
-
-

Used to set the maximum rate at which the EIP1559 base fee can change per block, approx. 12.5%.

-
-
var BASE_REWARD_FACTOR : List[int]
-
-

A parameter used to change the issuance rate of the Ethereum PoS system.

-

Most validator rewards and penalties are calculated in terms of the base reward.

-
-
var CHURN_LIMIT_QUOTIENT : List[int]
-
-

Used in the calculation of the churn limit to set a point at which the limit increases.

-
-
var EFFECTIVE_BALANCE_INCREMENT : List[float]
-
-

A validators effective balance can only change in steps of EFFECTIVE_BALANCE_INCREMENT, -which reduces the computational load for state updates.

-
-
var ELASTICITY_MULTIPLIER : List[int]
-
-

Used to calculate gas limit from EIP1559 gas target

-
-
var MAX_EFFECTIVE_BALANCE : List[float]
-
-

A validators effective balance is used to calculate incentives, and for voting, -and is a value less than the total stake/balance.

-

The max effective balance of a validator is 32 ETH.

-
-
var MIN_PER_EPOCH_CHURN_LIMIT : List[int]
-
-

Used to calculate the churn limit for validator entry and exit. The maximum number of validators that can -enter or exit the system per epoch.

-

In this system it is used for the validator activation queue process.

-
-
var MIN_SLASHING_PENALTY_QUOTIENT : List[int]
-
-

Used to calculate the penalty applied for a slashable offence.

-
-
var PROPORTIONAL_SLASHING_MULTIPLIER : List[int]
-
-

Scales the slashing penalty proportional to the total slashings for the current epoch

-

i.e. the more slashing events there are, the greater the individual penalty

-
-
var PROPOSER_REWARD_QUOTIENT : List[int]
-
-

Used to calculate the proportion of rewards distributed between attesters and proposers.

-
-
var PROPOSER_WEIGHT : List[int]
-
-

Used to calculate the reward received for successfully proposing a block.

-
-
var SYNC_REWARD_WEIGHT : List[int]
-
-

Used to calculate the reward received for attesting as part of a sync committee.

-
-
var TIMELY_HEAD_WEIGHT : List[int]
-
-

Used to calculate the reward received for getting a head vote in time and correctly.

-

head_reward = (TIMELY_HEAD_WEIGHT / WEIGHT_DENOMINATOR) * base_reward

-
-
var TIMELY_SOURCE_WEIGHT : List[int]
-
-

Used to calculate the reward received for getting a source vote in time and correctly.

-

source_reward = (TIMELY_SOURCE_WEIGHT / WEIGHT_DENOMINATOR) * base_reward

-
-
var TIMELY_TARGET_WEIGHT : List[int]
-
-

Used to calculate the reward received for getting a target vote in time and correctly.

-

target_reward = (TIMELY_TARGET_WEIGHT / WEIGHT_DENOMINATOR) * base_reward

-
-
var WEIGHT_DENOMINATOR : List[int]
-
-

Used as the denominator in incentive calculations to calculate reward and penalty proportions.

-
-
var WHISTLEBLOWER_REWARD_QUOTIENT : List[int]
-
-

Used to calculate the proportion of the effective balance of the slashed validator -distributed between the whistleblower and the proposer.

-
-
var base_fee_process : List[Callable[[int, int], float]]
-
-

The base fee burned, in Gwei per gas, for each transaction.

-

An average of 100 Gwei per gas expected to be set as transaction fee cap, -split between the base fee and priority fee - the fee cap less the base fee is sent as a priority fee to miners/validators.

-

Approximated using average gas price from https://etherscan.io/gastracker as of 20/04/21

-

An extract from https://notes.ethereum.org/@vbuterin/eip-1559-faq

-
-

Each “full block” (ie. a block whose gas is 2x the TARGET) increases the BASEFEE by 1.125x, -so a series of constant full blocks will increase the gas price by a factor of 10 every -~20 blocks (~4.3 min on average). -Hence, periods of heavy on-chain load will not realistically last longer than ~5 minutes.

-
-
-
var daily_pow_issuance : List[float]
-
-

The average daily Proof of Work issuance in ETH.

-

See https://etherscan.io/chart/blockreward

-
-
var date_eip1559 : List[datetime.datetime]
-
-

EIP1559 activation date as Python datetime.

-

Source: https://github.com/ethereum/pm/issues/245#issuecomment-825751460

-
-
var date_pos : List[datetime.datetime]
-
-

Eth1/Eth2 merge date as Python datetime, after which POW is disabled and POS is enabled.

-

Source: https://twitter.com/drakefjustin/status/1379052831982956547

-
-
var date_start : List[datetime.datetime]
-
-

Start date for simulation as Python datetime

-
-
var dt : List[int]
-
-

Simulation timescale / timestep unit of time, in epochs.

-

Used to scale calculations that depend on the number of epochs that have passed.

-

For example, for dt = 100, each timestep equals 100 epochs.

-

By default set to constants.epochs_per_day (~= 225)

-
-
var eth_price_process : List[Callable[[int, int], float]]
-
-

A process that returns the ETH spot price at each epoch.

-

By default set to average ETH price over the last 12 months from Etherscan.

-
-
var eth_staked_process : List[Callable[[int, int], float]]
-
-

A process that returns the ETH staked at each epoch.

-

If set to none, the model is driven by the validator process, -where new validators enter the system and stake accordingly.

-

This process is used for simulating a series of ETH staked values directly.

-
-
var gas_target_process : List[Callable[[int, int], int]]
-
-

The long-term average gas target per block.

-

The current gas limit is replaced by two values: -* a “long-term average target” (equal to the current gas limit) == gas target -* a “hard per-block cap” (twice the current gas limit) == gas limit

-

EIP1559 gas limit = gas_target * ELASTICITY_MULTIPLIER -See https://eips.ethereum.org/EIPS/eip-1559

-
-
var priority_fee_process : List[Callable[[int, int], float]]
-
-

EIP1559 transaction pricing priority fee, in Gwei per gas.

-

Due to MEV, average priority fee expected to be higher than usual as bid for inclusion in blockscpace market.

-

The priority fee is the difference between the fee cap set per transaction, and the base fee.

-

For PoW system without MEV influence, the priority fee level compensates for uncle risk: -See https://notes.ethereum.org/@vbuterin/BkSQmQTS8#Why-would-miners-include-transactions-at-all

-
-
var slashing_events_per_1000_epochs : List[int]
-
-

The number of slashing events per 1000 epochs.

-
-
var stage : List[Stage]
-
-

Which stage or stages of the network upgrade process to simulate.

-

By default set to PROOF_OF_STAKE stage, where EIP1559 is enabled and POW issuance is disabled.

-

See model.types.Stage Enum for further documentation.

-
-
var validator_cloud_costs_per_epoch : List[numpy.ndarray]
-
-

The validator cloud costs per epoch in dollars.

-

A vector with a value for each validator environment.

-
-
var validator_hardware_costs_per_epoch : List[numpy.ndarray]
-
-

The validator hardware costs per epoch in dollars.

-

A vector with a value for each validator environment.

-
-
var validator_percentage_distribution : List[numpy.ndarray]
-
-

The percentage of validators in each environment, normalized to a total of 100%.

-

A vector with a value for each validator environment.

-
-
var validator_process : List[Callable[[int, int], int]]
-
-

A process that returns the number of new validators per epoch.

-

Used if model not driven using eth_staked_process.

-

By default set to a static value from https://beaconscan.com/statistics.

-
-
var validator_third_party_costs_per_epoch : List[numpy.ndarray]
-
-

The validator third-party costs as a percentage of total online validator rewards.

-

Used for expected Staking-as-a-Service fees.

-

A vector with a value for each validator environment.

-
-
var validator_uptime_process : List[float]
-
-

The combination of validator internet, power, and technical uptime, as a percentage.

-

Minimum uptime is inactivity leak threshold = 2/3, as this model doesn't model the inactivity leak process.

-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/types.html b/docs/model/types.html deleted file mode 100644 index a194aad3..00000000 --- a/docs/model/types.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - -model.types API documentation - - - - - - - - - - - -
-
-
-

Module model.types

-
-
-

Various Python types used in the model

-
- -Expand source code - -
"""
-Various Python types used in the model
-"""
-
-import numpy as np
-import sys
-
-# See https://docs.python.org/3/library/dataclasses.html
-from dataclasses import dataclass, field
-from enum import Enum
-
-# If Python version is greater than equal to 3.8, import from typing module
-# Else also import from typing_extensions module
-if sys.version_info >= (3, 8):
-    from typing import TypedDict, List, Callable, NamedTuple
-else:
-    from typing import List, NamedTuple
-    from typing_extensions import TypedDict, Callable
-
-
-# Generic types
-Uninitialized = np.nan
-Percentage = float
-Percentage_per_epoch = float
-
-# Ethereum system types
-Gas = int
-Wei = int
-Gwei = float
-Gwei_per_Gas = float
-ETH = float
-
-
-class Stage(Enum):
-    """Stages of the Ethereum network upgrade process finite-state machine"""
-
-    ALL = 1
-    """Transition through all stages"""
-    BEACON_CHAIN = 2
-    """Beacon Chain implemented; EIP-1559 disabled; POW issuance enabled"""
-    EIP-1559 = 3
-    """Beacon Chain implemented; EIP1559 enabled; POW issuance enabled"""
-    PROOF_OF_STAKE = 4
-    """Beacon Chain implemented; EIP1559 enabled; POW issuance disabled"""
-
-
-# US Dollar types
-USD = float
-USD_per_ETH = float
-USD_per_epoch = float
-
-# Simulation types
-Run = int
-Timestep = int
-
-# BeaconState types
-Epoch = int
-
-# Validator types
-ValidatorIndex = int
-
-
-# Validator environment class used for configuring distribution of validators as parameters
-@dataclass
-class ValidatorEnvironment:
-    # Set the type (e.g. Percentage) and default value (e.g. 0.0) for each field
-    type: str = ""
-    percentage_distribution: Percentage = 0.0
-    hardware_costs_per_epoch: USD_per_epoch = 0.0
-    cloud_costs_per_epoch: USD_per_epoch = 0.0
-    third_party_costs_per_epoch: Percentage_per_epoch = 0.0
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class Stage -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Stages of the Ethereum network upgrade process finite-state machine

-
- -Expand source code - -
class Stage(Enum):
-    """Stages of the Ethereum network upgrade process finite-state machine"""
-
-    ALL = 1
-    """Transition through all stages"""
-    BEACON_CHAIN = 2
-    """Beacon Chain implemented; EIP1559 disabled; POW issuance enabled"""
-    EIP1559 = 3
-    """Beacon Chain implemented; EIP1559 enabled; POW issuance enabled"""
-    PROOF_OF_STAKE = 4
-    """Beacon Chain implemented; EIP1559 enabled; POW issuance disabled"""
-
-

Ancestors

-
    -
  • enum.Enum
  • -
-

Class variables

-
-
var ALL
-
-

Transition through all stages

-
-
var BEACON_CHAIN
-
-

Beacon Chain implemented; EIP1559 disabled; POW issuance enabled

-
-
var EIP1559
-
-

Beacon Chain implemented; EIP1559 enabled; POW issuance enabled

-
-
var PROOF_OF_STAKE
-
-

Beacon Chain implemented; EIP1559 enabled; POW issuance disabled

-
-
-
-
-class ValidatorEnvironment -(type: str = '', percentage_distribution: float = 0.0, hardware_costs_per_epoch: float = 0.0, cloud_costs_per_epoch: float = 0.0, third_party_costs_per_epoch: float = 0.0) -
-
-

ValidatorEnvironment(type: str = '', percentage_distribution: float = 0.0, hardware_costs_per_epoch: float = 0.0, cloud_costs_per_epoch: float = 0.0, third_party_costs_per_epoch: float = 0.0)

-
- -Expand source code - -
class ValidatorEnvironment:
-    # Set the type (e.g. Percentage) and default value (e.g. 0.0) for each field
-    type: str = ""
-    percentage_distribution: Percentage = 0.0
-    hardware_costs_per_epoch: USD_per_epoch = 0.0
-    cloud_costs_per_epoch: USD_per_epoch = 0.0
-    third_party_costs_per_epoch: Percentage_per_epoch = 0.0
-
-

Class variables

-
-
var cloud_costs_per_epoch : float
-
-
-
-
var hardware_costs_per_epoch : float
-
-
-
-
var percentage_distribution : float
-
-
-
-
var third_party_costs_per_epoch : float
-
-
-
-
var type : str
-
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model/utils.html b/docs/model/utils.html deleted file mode 100644 index 86f11d1f..00000000 --- a/docs/model/utils.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - -model.utils API documentation - - - - - - - - - - - -
-
-
-

Module model.utils

-
-
-

Misc. utility and helper functions

-
- -Expand source code - -
"""
-Misc. utility and helper functions
-"""
-
-import copy
-from dataclasses import field
-from functools import partial
-
-
-def _update_from_signal(
-    state_variable,
-    signal_key,
-    params,
-    substep,
-    state_history,
-    previous_state,
-    policy_input,
-):
-    return state_variable, policy_input[signal_key]
-
-
-def update_from_signal(state_variable, signal_key=None):
-    """A generic State Update Function to update a State Variable directly from a Policy Signal
-
-    Args:
-        state_variable (str): State Variable key
-        signal_key (str, optional): Policy Signal key. Defaults to None.
-
-    Returns:
-        Callable: A generic State Update Function
-    """
-    if not signal_key:
-        signal_key = state_variable
-    return partial(_update_from_signal, state_variable, signal_key)
-
-
-def local_variables(_locals):
-    return {
-        key: _locals[key]
-        for key in [_key for _key in _locals.keys() if "__" not in _key]
-    }
-
-
-def default(obj):
-    return field(default_factory=lambda: copy.copy(obj))
-
-
-
-
-
-
-
-

Functions

-
-
-def default(obj) -
-
-
-
- -Expand source code - -
def default(obj):
-    return field(default_factory=lambda: copy.copy(obj))
-
-
-
-def local_variables(_locals) -
-
-
-
- -Expand source code - -
def local_variables(_locals):
-    return {
-        key: _locals[key]
-        for key in [_key for _key in _locals.keys() if "__" not in _key]
-    }
-
-
-
-def update_from_signal(state_variable, signal_key=None) -
-
-

A generic State Update Function to update a State Variable directly from a Policy Signal

-

Args

-
-
state_variable : str
-
State Variable key
-
signal_key : str, optional
-
Policy Signal key. Defaults to None.
-
-

Returns

-
-
Callable
-
A generic State Update Function
-
-
- -Expand source code - -
def update_from_signal(state_variable, signal_key=None):
-    """A generic State Update Function to update a State Variable directly from a Policy Signal
-
-    Args:
-        state_variable (str): State Variable key
-        signal_key (str, optional): Policy Signal key. Defaults to None.
-
-    Returns:
-        Callable: A generic State Update Function
-    """
-    if not signal_key:
-        signal_key = state_variable
-    return partial(_update_from_signal, state_variable, signal_key)
-
-
-
-
-
-
-
- -
- - - \ No newline at end of file diff --git a/docs/model_specification/img/cadcad_canvas_legend.png b/docs/model_specification/img/cadcad_canvas_legend.png new file mode 100644 index 00000000..222b95bb Binary files /dev/null and b/docs/model_specification/img/cadcad_canvas_legend.png differ diff --git a/docs/model_specification/img/differential_model_specification.png b/docs/model_specification/img/differential_model_specification.png new file mode 100644 index 00000000..bcbeb951 Binary files /dev/null and b/docs/model_specification/img/differential_model_specification.png differ diff --git a/docs/model_specification/img/eth_rewards.png b/docs/model_specification/img/eth_rewards.png new file mode 100644 index 00000000..4cedf084 Binary files /dev/null and b/docs/model_specification/img/eth_rewards.png differ diff --git a/docs/model_specification/img/psub_1.png b/docs/model_specification/img/psub_1.png new file mode 100644 index 00000000..312e945f Binary files /dev/null and b/docs/model_specification/img/psub_1.png differ diff --git a/docs/model_specification/img/psub_10.png b/docs/model_specification/img/psub_10.png new file mode 100644 index 00000000..2df954d2 Binary files /dev/null and b/docs/model_specification/img/psub_10.png differ diff --git a/docs/model_specification/img/psub_2.png b/docs/model_specification/img/psub_2.png new file mode 100644 index 00000000..11946dfc Binary files /dev/null and b/docs/model_specification/img/psub_2.png differ diff --git a/docs/model_specification/img/psub_3.png b/docs/model_specification/img/psub_3.png new file mode 100644 index 00000000..d6001bfd Binary files /dev/null and b/docs/model_specification/img/psub_3.png differ diff --git a/docs/model_specification/img/psub_4.png b/docs/model_specification/img/psub_4.png new file mode 100644 index 00000000..d565c4bd Binary files /dev/null and b/docs/model_specification/img/psub_4.png differ diff --git a/docs/model_specification/img/psub_7.png b/docs/model_specification/img/psub_7.png new file mode 100644 index 00000000..c8ed9520 Binary files /dev/null and b/docs/model_specification/img/psub_7.png differ diff --git a/docs/model_specification/img/psub_8.png b/docs/model_specification/img/psub_8.png new file mode 100644 index 00000000..e6ddd3f0 Binary files /dev/null and b/docs/model_specification/img/psub_8.png differ diff --git a/docs/model_specification/img/psub_9.png b/docs/model_specification/img/psub_9.png new file mode 100644 index 00000000..fa77e32a Binary files /dev/null and b/docs/model_specification/img/psub_9.png differ diff --git a/docs/model_specification/img/psubs_5_6.png b/docs/model_specification/img/psubs_5_6.png new file mode 100644 index 00000000..ea80feab Binary files /dev/null and b/docs/model_specification/img/psubs_5_6.png differ diff --git a/docs/model_specification/img/system_metrics.png b/docs/model_specification/img/system_metrics.png new file mode 100644 index 00000000..44df689f Binary files /dev/null and b/docs/model_specification/img/system_metrics.png differ diff --git a/docs/model_specification/mathematical_specification.md b/docs/model_specification/mathematical_specification.md index cab94667..eaac8846 100644 --- a/docs/model_specification/mathematical_specification.md +++ b/docs/model_specification/mathematical_specification.md @@ -1,12 +1,7 @@ # Mathematical Model Specification -[![hackmd-github-sync-badge](https://hackmd.io/wHM-t557Tp2BH1gItdRvFA/badge)](https://hackmd.io/wHM-t557Tp2BH1gItdRvFA) Mathematical Model Specification for the [CADLabs Ethereum Economic Model](https://github.com/CADLabs/ethereum-economic-model/releases/tag/v1.1.0) version v1.1.0. -:::info -If you are not viewing this document in HackMD, it was formatted using Markdown and LaTeX to be rendered in HackMD. For the best viewing experience see https://hackmd.io/@CADLabs/ryLrPm2T_ -::: - ## Overview This Mathematical Model Specification articulates the relevant Ethereum validator economics system dynamics as a [state-space representation](https://en.wikipedia.org/wiki/State-space_representation). Given the iterative nature of dynamical systems modelling workflows, we expect to make adjustments to this Mathematical Model Specification as we build and validate the cadCAD model. @@ -51,7 +46,7 @@ We define the State Variables' domain, range, and units. The "variable" column v | Name | Symbol | Domain | Unit | Variable | Description | | -------- | -------- | -------- | -------- | -------- | --------| -| ETH Price | $P$ | $\mathbb{R}^+$ | $$/\text{ETH}$ | `eth_price` | ETH spot price sample (from exogenous process) | +| ETH Price | $P$ | $\mathbb{R}^+$ | $/\text{ETH}$ | `eth_price` | ETH spot price sample (from exogenous process) | | ETH Supply | $S$ | $\mathbb{R}^+$ | $\text{ETH}$ | `eth_supply` | ETH supply with inflation/deflation | | ETH Staked | $X$ | $\mathbb{R}^+$ | $\text{ETH}$ |`eth_staked` | Total ETH staked ("active balance") by all active validators | @@ -212,8 +207,8 @@ All System Parameters in this category use the same uppercase snake-case variabl | Variable | Unit | Description | | -------- | -------- | -------- | | `validator_percentage_distribution` | $\begin{bmatrix} \% \end{bmatrix}$ | The distribution of the total number of validators per validator type. Vector sum is a total of 100%. | -| `validator_hardware_costs_per_epoch` | $$\begin{bmatrix} USD \end{bmatrix}$$ | The per-epoch costs for DIY hardware infrastructure per validator type | -| `validator_cloud_costs_per_epoch` | $$\begin{bmatrix} USD \end{bmatrix}$$ | The per-epoch costs for cloud computing resources per validator type | +| `validator_hardware_costs_per_epoch` | $\begin{bmatrix} USD \end{bmatrix}$ | The per-epoch costs for DIY hardware infrastructure per validator type | +| `validator_cloud_costs_per_epoch` | $\begin{bmatrix} USD \end{bmatrix}$ | The per-epoch costs for cloud computing resources per validator type | | `validator_third_party_costs_per_epoch` | $\begin{bmatrix} \% \end{bmatrix}$ | A percentage value of the total validator rewards that goes to third-party service providers as a fee per validator type | ### Validator Performance System Parameters @@ -239,7 +234,7 @@ To visualize the State Update Logic, we use a differential specification diagram The [model's cadCAD Canvas / Differential Model Specification](https://lucid.app/lucidchart/c7656072-e601-4ec4-a44b-0a15c9a5700d/view) is accessible via LucidChart. Below is an illustrative screenshot. -![](https://i.imgur.com/DQWxj7W.png) +![Differential Model Specification](img/differential_model_specification.png) We describe the State Update Logic along the columns of the model's cadCAD Canvas, also known as "Partial State Update Blocks" (PSUB). One round of execution of these Partial State Update Blocks would represent the state transition from one epoch to the next. @@ -247,7 +242,7 @@ We describe the State Update Logic along the columns of the model's cadCAD Canva Extracts from the cadCAD Canvas have been included for each PSUB below when deriving the Policy and State Update Logic, and the following is the legend included with the cadCAD Canvas: -psub +![cadCAD Canvas Legend](img/cadcad_canvas_legend.png){h=500px} #### Constants @@ -260,7 +255,7 @@ The following constants are used in the derivation of the State Update Logic. ### PSUB 1: Network Upgrade Stages -psub +![PSUB 1: Network Upgrade Stages](img/psub_1.png){h=500px} The Upgrade Stages Policy is essentially a [Finite-state Machine](https://en.wikipedia.org/wiki/Finite-state_machine) that handles the transition from on stage in the Ethereum network upgrade process to the next for time-domain analyses, or simply selecting a single stage for phase-space analyses. @@ -273,7 +268,7 @@ Each stage has a corresponding date, set using the `date_{}` System Parameters. ### PSUB 2: Validator Process -psub +![PSUB 2: Validator Process](img/psub_2.png){h=500px} Validators that deposit their initial stake first enter into an activation queue before being considered active validators and having their stake as part of the effective balance used when calculating validator rewards and penalties. @@ -298,19 +293,19 @@ V_{offline} &= V^+ - V_{online} ### PSUB 3: Ethereum Processes -psub - +![PSUB 3: Ethereum Processes](img/psub_3.png){h=500px} The ETH price is driven by an environmental process, defined earlier in the Model Specification, that updates the ETH price at each timestep. The total ETH staked is the number of activate validators multiplied by the average effective balance in ETH: + $$ X = V \times \frac{\bar{B}}{10^9} $$ ### PSUB 4: Base Reward -psub +![PSUB 4: Base Reward](img/psub_4.png){h=500px} The following mathematical pseudo-code is used to calculate the aggregate average effective balance of the system: @@ -330,13 +325,13 @@ $$ ### PSUBs 5 & 6: Attestation, Block Proposal & Sync Committee Rewards -psub +![PSUBs 5 & 6: Attestation, Block Proposal & Sync Committee Rewards](img/psubs_5_6.png){h=500px} The rewards and penalties from PoS block proposal, attestation, and sync committees, are approximated and aggregated across all validators at each epoch. It is useful seeing the rewards as a pie-chart, where the combined rewards are equal to one base reward (see [source](https://github.com/ethereum/annotated-spec/blob/master/altair/beacon-chain.md)): -![](https://i.imgur.com/mxv9zGd.png) +![ETH rewards pie-chart](img/eth_rewards.png){h=300px} #### Source, Target, and Head Rewards @@ -380,7 +375,7 @@ W_h &= \text{TIMELY_HEAD_WEIGHT}\\ ### PSUB 7: Attestation & Sync Committee Penalties -psub +![PSUB 7: Attestation & Sync Committee Penalties](img/psub_7.png){h=500px} #### Attestation penalties @@ -410,7 +405,7 @@ W_{sync} &= \text{SYNC_REWARD_WEIGHT} ### PSUB 8: Validating Reward & Penalty Aggregation -psub +![PSUB 8: Validating Reward & Penalty Aggregation](img/psub_8.png){h=500px} #### Validating Rewards @@ -430,7 +425,7 @@ $$ ### PSUB 9: Slashing Rewards & Penalties -psub +![PSUB 9: Slashing Rewards & Penalties](img/psub_9.png){h=500px} First, we calculate the slashing reward for a single slashing event, indicated by $'$: @@ -461,7 +456,6 @@ N &= \frac{\text{slashing_events_per_1000_epochs}}{1000} \qquad (\text{slashing \end{aligned} \end{equation} - Finally, the individual slashing penalty is calculated as the sum of the individual slashing and proportional slashing penalties: $$ @@ -479,7 +473,7 @@ R_w &= R'_w \times N\\ ### PSUB 10: EIP1559 Transaction Pricing -psub +![PSUB 10: EIP1559 Transaction Pricing](img/psub_10.png){h=500px} EIP-1559 replaces the current transaction gas price (in Gwei per gas), with two values: a dynamic base fee that is burned and applied to all transactions, and a priority fee per transaction that is paid to miners/validators. @@ -490,6 +484,7 @@ The current gas limit is replaced by two values: The long-term average gas target per block is set to 15m gas; by default we assume the gas used per block will on average be equal to the gas target. Pre-merge, while Proof-of-Work is still active, miners receive the priority fee, and the gas used is calculated according to block-time: + \begin{equation} \begin{aligned} \text{gas used} &= \text{blocks per epoch} \times \text{gas target}\\ @@ -499,6 +494,7 @@ T = T_m &= \text{gas used} \times t\\ \end{equation} Post-merge, when Proof-of-Work is deprecated and Proof-of-Stake validators start including transactions, validators receive the priority fee, and the gas used is calculated according to slot-time: + \begin{equation} \begin{aligned} \text{gas used} &= \text{slots per epoch} \times \text{gas target}\\ @@ -511,7 +507,7 @@ T = T_v &= \text{gas used} \times t\\ System Metrics are computed from State Variables in order to assess the performance of the system. The calculation of our System Metrics is also represented in the [model's cadCAD Canvas / Differential Model Specification](https://lucid.app/lucidchart/c7656072-e601-4ec4-a44b-0a15c9a5700d/view) and accessible via LucidChart. Below is an illustrative screenshot. -![](https://i.imgur.com/5xAaCCm.png) +![System Metrics](img/system_metrics.png){h=200px} The following state-update logic for system metric State Variables could also be performed in post-processing, assuming there are no feedback loops that influence the metrics, to improve run-time performance. @@ -526,6 +522,7 @@ $$ #### Ethereum Issuance The **ETH supply** at the next epoch is equal to the sum of the ETH supply at the current epoch and the net network issuance: + $$ S^+ = S + (R_v + R_w - Z - \psi - F) $$ @@ -533,11 +530,13 @@ $$ #### Validator Costs The **validator costs** is the sum of hardware, cloud, and third-party costs per validator type: + $$ \vec{C} = \vec{C}_{hardware} + \vec{C}_{cloud} + \vec{C}_{third-party} \qquad ([$]) $$ The **total network costs** is the sum of validator costs over all validator types (row index $i$): + $$ C = \sum_{i}{\vec{C}_{ij}} \qquad ($) $$ @@ -545,21 +544,25 @@ $$ #### Validator Revenue and Profit The **validator revenue** is the rewards for online validators in ETH, $R_o / 10^9$, distributed according to the validator percentage distribution multiplied by the current ETH price $P$: + $$ \vec{K}_r = \text{validator_percentage_distribution} \times R_o / 10^9 \times P \qquad ([$]) $$ The **validator profit** is the validator revenue less the validator costs: + $$ \vec{K}_p = \vec{K}_r - \vec{C} \qquad ([$]) $$ The **total revenue** is the sum of validator revenue over all validator types: + $$ K_r = \sum_{i}{\vec{K}_{r,ij}} \qquad ($) $$ The **total profit** is the total revenue less the total network costs: + $$ K_p = K_r - C \qquad ($) $$ @@ -567,584 +570,11 @@ $$ #### Validator Revenue and Profit Yields The per-validator **revenue and profit yields** are calculated and annualized as the validator profit and revenue multiplied by the number of epochs in a year divided by the validator ETH staked, $\sigma$, in dollars: + $$\vec{Y}_r = \frac{\vec{K}_r \times E_{year}}{\sigma \times P} \qquad ([\%])$$ $$\vec{Y}_p = \frac{\vec{K}_p \times E_{year}}{\sigma \times P} \qquad ([\%])$$ The total **revenue and profit yields** are calculated and annualized as the total profit and revenue multiplied by the number of epochs in a year divided by the total ETH staked, $X$, in dollars: -$$Y_r = \frac{K_r \times E_{year}}{X \times P} \qquad (\%)$$ -$$Y_p = \frac{K_p \times E_{year}}{X \times P} \qquad (\%)$$ -# Mathematical Model Specification -[![hackmd-github-sync-badge](https://hackmd.io/wHM-t557Tp2BH1gItdRvFA/badge)](https://hackmd.io/wHM-t557Tp2BH1gItdRvFA) - -Mathematical Model Specification for the [CADLabs Ethereum Economic Model](https://github.com/CADLabs/ethereum-economic-model/releases/tag/v1.0.0) version v1.0.0. - -:::info -If you are not viewing this document in HackMD, it was formatted using Markdown and LaTeX to be rendered in HackMD. For the best viewing experience see https://hackmd.io/@CADLabs/ryLrPm2T_ -::: - -## Overview - -This Mathematical Model Specification articulates the relevant Ethereum validator economics system dynamics as a [state-space representation](https://en.wikipedia.org/wiki/State-space_representation). Given the iterative nature of dynamical systems modelling workflows, we expect to make adjustments to this Mathematical Model Specification as we build and validate the cadCAD model. - -### Assumptions - -The model implements the official [Ethereum Specification](https://github.com/ethereum/eth2.0-specs) wherever possible, but rests on a few default system-level and validator-level assumptions detailed in the model [ASSUMPTIONS.md](https://github.com/CADLabs/ethereum-economic-model/blob/main/ASSUMPTIONS.md) document. - -### Level of Aggregation - -Although cadCAD technically supports several computational modelling paradigms (e.g. agent-based modelling, population-level modelling, system dynamics modelling, hybrid modelling, etc.) we adopt an aggregate system dynamics lens. Rather than modelling the behaviour of individual agents, we consider what is often called a "representative agent" in economics literature. This allows us to apply aggregation and approximation for groups of agents, or in our case - validators aggregated as validator environments. - -### Epoch-level Granularity - -Unless specified otherwise in the Mathematical Model Specification, all State Variables, System Metrics, and System Parameters are time-dependent and calculated at per-epoch granularity. For ease of notation, units of time will be assumed implicitly. In the model implementation, calculations can be aggregated across epochs where necessary - for example for performance reasons. - -## Notation - -The Mathematical Model Specification uses the following notation: -* A list / vector of units or in calculations is represented using the matrix symbol: $\begin{bmatrix} x \end{bmatrix}$ -* A list or vector variable is represented using the vector symbol: $\vec{x}$ -* A $\Rightarrow$ symbol represents a function that returns a value, ignoring the arguments. For example a Python lambda function `lambda x: 1` would be represented as: $\Rightarrow 1$ -* The superscript $S^+$ is used to define a state transition from state $S$ at the current epoch $e$, to the state at the next epoch $e + 1$ -* The superscript $S'$ is used to define an individual element to be aggregated in order to get the final state $S$ - -The following domain notation is used in the Mathematical Model Specification: -* $\mathbb{Z}$ - positive and negative integers -* $\mathbb{R}$ - positive and negative real numbers -* $\mathbb{Z}^+$ - positive integers -* $\mathbb{R}^-$ - negative real numbers -* etc. - -## System States - -To create a [state-space representation](https://en.wikipedia.org/wiki/State-space_representation), we first describe the system's [state-space](https://www.google.com/search?q=state+space+state+variables&oq=state+space+state+variables) in the form of a set of State Variables. A state-space is a data structure that consists of all possible values of State Variables. The state of the system can be represented as a state vector within the state-space. - -For reasons of clarity and comprehensibility we categorize State Variables as follows: ETH State Variables, Validator State Variables, Reward and Penalty State Variables, EIP-1559 State Variables, and System Metric State Variables. - -We define the State Variables' domain, range, and units. The "variable" column values are direct referrences to the cadCAD model code. - -### ETH State Variables - -| Name | Symbol | Domain | Unit | Variable | Description | -| -------- | -------- | -------- | -------- | -------- | --------| -| ETH Price | $P$ | $\mathbb{R}^+$ | $$/\text{ETH}$ | `eth_price` | ETH spot price sample (from exogenous process) | -| ETH Supply | $S$ | $\mathbb{R}^+$ | $\text{ETH}$ | `eth_supply` | ETH supply with inflation/deflation | -| ETH Staked | $X$ | $\mathbb{R}^+$ | $\text{ETH}$ |`eth_staked` | Total ETH staked ("active balance") by all active validators | - -### Validator State Variables - -| Name | Symbol | Domain | Unit | Variable | -| -------- | -------- | -------- | -------- | --------| -| # Validators in Activation Queue | $V_{queue}$ | $\mathbb{R}^+$ | None | `number_of_validators_in_activation_queue` | -| # Validators | $V$ | $\mathbb{R}^+$ | None | `number_of_validators` | -| # Validators Online | $V_{online}$ | $\mathbb{R}^+$ | None | `number_of_validators_online` | -| # Validators Offline | $V_{offline}$ | $\mathbb{R}^+$ | None | `number_of_validators_offline` | -| Average Effective Balance | $\bar{B}$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `average_effective_balance` | -| Validator ETH Staked | $\vec{\sigma}$ | $\mathbb{R}^+$ | $[\text{ETH}]$ | `validator_eth_staked` | - -### Reward and Penalty State Variables - -| Name | Symbol | Domain | Unit | Variable | -| ------------------------------ | ------- | -------------- | ------------- | -------------------------------- | -| Base Reward | $\beta$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `base_reward` | -| Source Reward | $r_s$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `source_reward` | -| Target Reward | $r_t$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `target_reward` | -| Head Reward | $r_h$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `head_reward` | -| Block Proposer Reward | $r_p$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `block_proposer_reward` | -| Sync Reward | $r_{sync}$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `sync_reward` | -| Amount Slashed | $\psi$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `amount_slashed` | -| Validating Rewards | $R_v$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `validating_rewards` | -| Whistleblower Rewards | $R_w$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `whistleblower_rewards` | -| Attestation Penalties | $Z_a$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `attestation_penalties` | -| Sync Committee Penalties | $Z_{sync}$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `sync_committee_penalties` | -| Validating Penalties | $Z$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `validating_penalties` | -| Total Online Validator Rewards | $R_o$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `total_online_validator_rewards` | - -### EIP1559 State Variables - -| Name | Symbol | Domain | Unit | Variable | -| -------- | -------- | -------- | -------- | --------| -| Base Fee per Gas | $f$ | $\mathbb{R}^+$ | $\text{Gwei/gas}$ | `base_fee_per_gas` | -| Total Base Fee | $F$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `total_base_fee` | -| Priority Fee per Gas | $t$ | $\mathbb{R}^+$ | $\text{Gwei/gas}$ | `base_fee_per_gas` | -| Total Priority Fee to Validators | $T_v$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `total_priority_fee_to_validators` | -| Total Priority Fee to Miners | $T_m$ | $\mathbb{R}^+$ | $\text{Gwei}$ | `total_priority_fee_to_miners` | - -### System Metric State Variables - -We first define System Metrics on the level of the following 7 validator environments, using Numpy array matrix algebra: - -1. DIY Hardware -2. DIY Cloud -3. Pool StaaS -4. Pool Hardware -5. Pool Cloud -6. StaaS Full -7. StaaS Self-Custodied - -We then define network-level System Metrics through aggregation. - -#### Validator Environment Level - -The State Variables in this category have the following vector form: - -\begin{bmatrix} -\text{DIY Hardware}\\ -...\\ -\text{StaaS Self-Custodied} -\end{bmatrix} - -| Name | Symbol | Domain | Unit | Variable | -| -------- | -------- | -------- | -------- | --------| -| Validator Revenue | $\vec{K_r}$ | $\mathbb{R}$ | [$] | `validator_revenue` | -| Validator Profit | $\vec{K_p}$ | $\mathbb{R}$ | [$] | `validator_profit` | -| Validator Revenue Yields | $\vec{Y_r}$ | $\mathbb{R}$ | $[\%]$ | `validator_revenue_yields` | -| Validator Profit Yields | $\vec{Y_p}$ | $\mathbb{R}$ | $[\%]$ | `validator_profit_yields` | -| Validator Count Distribution | $\vec{V}$ | $\mathbb{R}^+$ | $[\%]$ | `validator_count_distribution` | -| Validator Hardware Costs | $\vec{C}_{hardware}$ | $\mathbb{R}^+$ | [$] | `validator_hardware_costs` | -| Validator Cloud Costs | $\vec{C}_{cloud}$ | $\mathbb{R}^+$ | [$] | `validator_cloud_costs` | -| Validator Third-Party Costs | $\vec{C}_{third-party}$ | $\mathbb{R}^+$ | $[\%]$ | `validator_third_party_costs` | -| Validator Costs | $\vec{C}$ | $\mathbb{R}^+$ | [$] | `validator_costs` | - -#### Aggregate Network Level - -The above validator-environment-level System Metrics are then aggregated into scalar values to define aggregate network-level system metrics. For example, the validator costs $\vec{C}$ becomes the total network costs $C$ when summed over all 7 validator types. - -| Name | Symbol | Domain | Unit | Variable | -| -------- | -------- | -------- | -------- | --------| -| ETH Supply Inflation | $I$ | $\mathbb{R}$ | $\%$ | `supply_inflation` | -| Total Revenue | $K_r$ | $\mathbb{R}$ | $ | `total_revenue` | -| Total Profit | $K_p$ | $\mathbb{R}$ | $ | `total_profit` | -| Total Network Costs | $C$ | $\mathbb{R}^+$ | $ | `total_network_costs` | -| Total Revenue Yields | $Y_r$ | $\mathbb{R}$ | $\%$ | `total_revenue_yields` | -| Total Profit Yields | $Y_p$ | $\mathbb{R}$ | $\%$ | `total_profit_yields` | - -## System Inputs - -By defining State Variables we have defined the system's state-space and with it, system boundaries. System inputs are not dependent on the system's State Variables. Their logic is defined by Policy Functions in our cadCAD model, and they update the model's State Variables via State Update Functions. - -We describe three environmental processes as System Inputs, updating the ETH Price and ETH Staked State Variables. - -### Validator Adoption Process & ETH Staked Process - -For the purpose of the model, we define environmental processes for both Validator Adoption and ETH Staked. A certain level of Validator Adoption has an implied ETH Staked value. We use the ETH Staked process to drive the model when performing phase-space analyses of a range of ETH staked values, and the Validator Adoption process when performing time-domain analyses where the validator activation mechanism also comes into play. - -The ETH Staked environmental process, represented in the model as a Python lambda function, is called at each epoch with the current run and timestep, and returns the change in ETH staked to update the ETH Staked State Variable during runtime. On the other hand, the Validator Adoption environmental process returns the number of validators being added to the activation queue at each epoch. - -For the MVP implementation of our model we assume a representative agent that remains within the system once entered, and we use a monotonically increasing function as a standard adoption model. We also plan the option for the user to manually define validator adoption levels to emulate custom scenarios. - -In future model implementations, we could imagine adding feedback loops from State Variables - for instance, capital efficient validators will likely stake and unstake ETH based on the development of validator returns. - -### ETH Price Process - -For the MVP implementation of our model we use tiered ETH price levels to represent the relevant spectrum of market conditions, similar to [Hoban/Borgers' Economic Model](https://drive.google.com/file/d/1pwt-EdnjhDLc_Mi2ydHus0_Cm14rs1Aq/view). We also plan the option for the user to manually select ETH price ranges to emulate custom scenarios. - -This environmental process, represented in the model as a Python lambda function, is called at each epoch with the current run and timestep, and returns an ETH price sample to update the ETH Price state variable during runtime. - -## System Parameters - -System Parameters are used as configurable variables in the model's System Input (Policy Function) and State Update (State Update Function) logic. An example of a parameter would be the `BASE_REWARD_FACTOR`, used to calculate and update the base reward State Variable. - -In a cadCAD model, parameters are lists of Python types that can be swept, or in the case of a stochastic process used to perform a Monte Carlo simulation. For the purpose of experimentation we've set defaults, and will sweep parameters within reasonable ranges around these defaults. - -Any parameter with the suffix `_process` can be assumed to be a Python lambda function used to generate a series of values for said parameter, indexed by the run and/or timestep. An illustrative example: - -```python -import random - -TIMESTEPS = 100 -samples = random.sample(range(95, 99), TIMESTEPS + 1) -validator_uptime_process = lambda _run, timestep: samples[timestep] / 100 -``` - -For reasons of clarity and comprehensibility we categorize parameters as either Ethereum Official Specification, Validator Environment, Validator Performance, or Transaction Pricing System Parameters. - -### Ethereum Official Specification System Parameters - -All System Parameters in this category use the same uppercase snake-case variable naming seen in the [Ethereum Official Specification](https://github.com/ethereum/eth2.0-specs) for easy recognition. See the [annotated-spec](https://github.com/ethereum/annotated-spec/blob/master/altair/beacon-chain.md) repository and [Benjaminion's annotated spec](https://benjaminion.xyz/eth2-annotated-spec/phase0/beacon-chain/) for further reference. - -| Variable | Default Value | Unit | -| -------- | -------- | -------- | -| `BASE_REWARD_FACTOR` | `64` | None | -| `MAX_EFFECTIVE_BALANCE` | `32e9` | $\text{Gwei}$ | -| `EFFECTIVE_BALANCE_INCREMENT` | `1e9` | $\text{Gwei}$ | -| `PROPOSER_REWARD_QUOTIENT` | `8` | None | -| `WHISTLEBLOWER_REWARD_QUOTIENT` | `512` | None | -| `MIN_SLASHING_PENALTY_QUOTIENT` | `64` | None | -| `PROPORTIONAL_SLASHING_MULTIPLIER` | `2` | None | -| `TIMELY_HEAD_WEIGHT` | `14` | None | -| `TIMELY_SOURCE_WEIGHT` | `14` | None | -| `TIMELY_TARGET_WEIGHT` | `26` | None | -| `SYNC_REWARD_WEIGHT` | `2` | None | -| `PROPOSER_WEIGHT` | `8` | None | -| `WEIGHT_DENOMINATOR` | `64` | None | -| `MIN_PER_EPOCH_CHURN_LIMIT` | `4` | None | -| `CHURN_LIMIT_QUOTIENT` | `2 ** 16` | None | -| `BASE_FEE_MAX_CHANGE_DENOMINATOR` | `8` | None | -| `ELASTICITY_MULTIPLIER` | `2` | None | - -### Validator Environment System Parameters - -| Variable | Unit | Description | -| -------- | -------- | -------- | -| `validator_percentage_distribution` | $\begin{bmatrix} \% \end{bmatrix}$ | The distribution of the total number of validators per validator type. Vector sum is a total of 100%. | -| `validator_hardware_costs_per_epoch` | $$\begin{bmatrix} USD \end{bmatrix}$$ | The per-epoch costs for DIY hardware infrastructure per validator type | -| `validator_cloud_costs_per_epoch` | $$\begin{bmatrix} USD \end{bmatrix}$$ | The per-epoch costs for cloud computing resources per validator type | -| `validator_third_party_costs_per_epoch` | $\begin{bmatrix} \% \end{bmatrix}$ | A percentage value of the total validator rewards that goes to third-party service providers as a fee per validator type | - -### Validator Performance System Parameters - -| Variable | Default Value | Unit | Description | -| -------- | -------- | -------- | -------- | -| `validator_uptime_process` | `max(0.98, 2 / 3)` | $\%$ | The expected average validator uptime. A combination of validator internet, power, and technical uptime: 99.9 * 99.9 * 98.2. Minimum uptime is inactivity leak threshold of `2/3`, as this model doesn't model the inactivity leak mechanism. | -| `slashing_events_per_1000_epochs` | `1` | $\frac{1}{1000}\text{epochs}$ | The expected number of validator actions that result in slashing per 1000 epochs | - -### Transaction Pricing System Parameters - -| Variable | Default Value | Unit | Description | -| -------- | -------- | -------- | -------- | -| `base_fee_process` | `25` | $\text{Gwei/gas}$ | EIP-1559 transaction pricing base fee burned for each transaction. Default value approximated using average historical gas price - see [assumptions doc](https://github.com/CADLabs/ethereum-economic-model/blob/main/ASSUMPTIONS.md). | -| `priority_fee_process` | `2` | $\text{Gwei/gas}$ | EIP-1559 transaction pricing priority fee. Default value approximated using average gas price - see [assumptions doc](https://github.com/CADLabs/ethereum-economic-model/blob/main/ASSUMPTIONS.md). | -| `gas_target_process` | `15e6` | $\text{Gas}$ | The long-term average gas target per block. Simplifying assumption that gas used per block will equal gas target on average over long-term. | - -## State Update Logic - -After defining the model's state-space in the form of System States, we describe the State Update Logic, represented as cadCAD Policy and State Update Functions (sometimes also called "mechanisms"). - -To visualize the State Update Logic, we use a differential specification diagram (also known as a "cadCAD Canvas" at cadCAD Edu). This diagram will accompany the derivation of the Mathematical Model Specification of the model mechanisms. - -The [model's cadCAD Canvas / Differential Model Specification](https://lucid.app/lucidchart/c7656072-e601-4ec4-a44b-0a15c9a5700d/view) is accessible via LucidChart. Below is an illustrative screenshot. - -![](https://i.imgur.com/DQWxj7W.png) - -We describe the State Update Logic along the columns of the model's cadCAD Canvas, also known as "Partial State Update Blocks" (PSUB). One round of execution of these Partial State Update Blocks would represent the state transition from one epoch to the next. - -#### cadCAD Canvas Legend - -Extracts from the cadCAD Canvas have been included for each PSUB below when deriving the Policy and State Update Logic, and the following is the legend included with the cadCAD Canvas: - -psub -#### Constants - -The following constants are used in the derivation of the State Update Logic. - -| Name | Symbol | Domain | Unit | Variable | Value | -| -------- | -------- | -------- | -------- | --------| --------| -| Epochs per year | $E_{year}$ | $\mathbb{Z}^+$ | $\text{epochs}$ | `epochs_per_year` | 82180 | -| Epochs per day | $E_{day}$ | $\mathbb{Z}^+$ | $\text{epochs}$ | `epochs_per_day` | 225 | - -### PSUB 1: Network Upgrade Stages - -psub - -The Upgrade Stages Policy is essentially a [Finite-state Machine](https://en.wikipedia.org/wiki/Finite-state_machine) that handles the transition from on stage in the Ethereum network upgrade process to the next for time-domain analyses, or simply selecting a single stage for phase-space analyses. - -The model has three stages, configured using the `Stage` Python Enum. The Enum option `ALL` transitions through all stages in order: -1. `BEACON_CHAIN`: Beacon Chain implemented; EIP1559 disabled; POW issuance enabled -2. `EIP1559`: Beacon Chain implemented; EIP1559 enabled; POW issuance enabled -3. `PROOF_OF_STAKE`: Beacon Chain implemented; EIP1559 enabled; POW issuance disabled - -Each stage has a corresponding date, set using the `date_{}` System Parameters. - -### PSUB 2: Validator Process - -psub - -Validators that deposit their initial stake first enter into an activation queue before being considered active validators and having their stake as part of the effective balance used when calculating validator rewards and penalties. - -\begin{equation} -\begin{aligned} -\text{churn limit } &= \text{max(MIN_PER_EPOCH_CHURN_LIMIT, $V$ // CHURN_LIMIT_QUOTIENT)}\\ -\text{new validators } &= \text{validator_process(run, timestep)} \\ -v &= \text{min($V_{queue} +$ new validators, churn limit)}\\ -V^+ &= V + v\\ -V_{queue}^+ &= V_{queue} - v\\ -\end{aligned} -\end{equation} - -The number of validators is equal to the sum of the number of validators online and offline: - -\begin{equation} -\begin{aligned} -V_{online} &= V^+ \times \text{validator uptime} \\ -V_{offline} &= V^+ - V_{online} -\end{aligned} -\end{equation} - -### PSUB 3: Ethereum Processes - -psub - - -The ETH price is driven by an environmental process, defined earlier in the Model Specification, that updates the ETH price at each timestep. - -The total ETH staked is the number of activate validators multiplied by the average effective balance in ETH: -$$ -X = V \times \frac{\bar{B}}{10^9} -$$ - -### PSUB 4: Base Reward - -psub - -The following mathematical pseudo-code is used to calculate the aggregate average effective balance of the system: - -\begin{equation} -\begin{aligned} -\bar{B} &= \frac{\text{min(total_effective_balance, MAX_EFFECTIVE_BALANCE $\times V$)}}{V} \\ -\text{where}: \\ -\text{total_effective_balance} &= X \times 10^9 - X \times 10^9 \quad mod \quad \text{EFFECTIVE_BALANCE_INCREMENT} \\ -\end{aligned} -\end{equation} - -The base reward is calculated as the average effective balance multiplied by the ratio of the base reward factor to the square-root of the total ETH staked multiplied by the base rewards per epoch (the higher the ETH Staked, the lower the base reward): - -$$ -\beta = \frac{\text{min($\bar{B}$, MAX_EFFECTIVE_BALANCE)} \times \text{BASE_REWARD_FACTOR}}{\sqrt{X}} -$$ - -### PSUBs 5 & 6: Attestation, Block Proposal & Sync Committee Rewards - -psub - -The rewards and penalties from PoS block proposal, attestation, and sync committees, are approximated and aggregated across all validators at each epoch. - -It is useful seeing the rewards as a pie-chart, where the combined rewards are equal to one base reward (see [source](https://github.com/ethereum/annotated-spec/blob/master/altair/beacon-chain.md)): - -![](https://i.imgur.com/mxv9zGd.png) - -#### Source, Target, and Head Rewards - -To approximate the source, target, and head vote rewards, it is assumed that all online validators get a source, target, and head vote in time and correctly once per epoch. The calculation for reward per epoch is the same, replacing the `TIMELY_SOURCE_WEIGHT` with the appropriate reward weight: - -\begin{equation} -\begin{aligned} -r_s &= \frac{\text{TIMELY_SOURCE_WEIGHT}}{\text{WEIGHT_DENOMINATOR}} \times \beta \qquad &(\text{proportion of base reward})\\ -&\times \frac{V_{online}}{V} \qquad &\text{(scale by proportion of online valdiators)}\\ -&\times V_{online} \qquad &\text{(aggregation over all online validators)}\\ -\end{aligned} -\end{equation} - -#### Sync Committee Reward - -\begin{equation} -\begin{aligned} -r_{sync} &= \frac{\text{SYNC_REWARD_WEIGHT}}{\text{WEIGHT_DENOMINATOR}} \times \beta \times V \qquad &(\text{proportion of total base rewards})\\ -&\times \frac{V_{online}}{V} \qquad &\text{(scale by proportion of online valdiators)}\\ -\end{aligned} -\end{equation} - -#### Block Proposer Reward - -\begin{equation} -\begin{aligned} -r_p &= \beta \times \text{(W_s + W_t + W_h)} \\ -&\times V_{online} \\ -&\times \frac{1}{(W_d - W_p) * W_d // W_p} \qquad &(\text{normalize by the sum of weights so that}\\ -& \qquad &\text{proposer rewards are 1/8th of base reward})\\\\ -&+ r_{sync} \times W_p // (W_d - W_p) \qquad &(\text{add block proposer reward for}\\ -& \qquad &\text{including sync committee attestations})\\ -\text{where:} \\ -W_d &= \text{WEIGHT_DENOMINATOR}\\ -W_p &= \text{PROPOSER_WEIGHT}\\ -W_s &= \text{TIMELY_SOURCE_WEIGHT}\\ -W_t &= \text{TIMELY_TARGET_WEIGHT}\\ -W_h &= \text{TIMELY_HEAD_WEIGHT}\\ -\end{aligned} -\end{equation} - -### PSUB 7: Attestation & Sync Committee Penalties - -psub - -#### Attestation penalties - -\begin{equation} -\begin{aligned} -Z_a &= \frac{W_s + W_t + W_h}{\text{WEIGHT_DENOMINATOR}} \times \beta \qquad &(\text{proportion of base reward}) \\ -&\times V_{offline} \qquad &(\text{aggregated over all offline validators})\\ -\text{where:} \\ -W_s &= \text{TIMELY_SOURCE_WEIGHT} \\ -W_t &= \text{TIMELY_TARGET_WEIGHT} \\ -W_h &= \text{TIMELY_HEAD_WEIGHT} \\ -\end{aligned} -\end{equation} - -#### Sync committee penalties - -It is assumed that all offline validators are penalized for not attesting to the source, target, and head: - -\begin{equation} -\begin{aligned} -Z_s &= \frac{W_{sync}}{\text{WEIGHT_DENOMINATOR}} \times \beta \times V \qquad &(\text{proportion of total base rewards}) \\ -&\times \frac{V_{offline}}{V} \qquad &(\text{scaled by % of offline validators}) \\ -\text{where:} \\ -W_{sync} &= \text{SYNC_REWARD_WEIGHT} -\end{aligned} -\end{equation} - -### PSUB 8: Validating Reward & Penalty Aggregation - -psub - -#### Validating Rewards - -The **total validating rewards** is calculated as the sum of all validator reward State Variables: - -$$ -R_v = r_p + r_s + r_t + r_h + r_{sync} -$$ - -#### Validating Penalties - -The **total validating penalties** is the sum of attestation and sync-committee penalties: - -$$ -Z = Z_a + Z_{sync} -$$ - -### PSUB 9: Slashing Rewards & Penalties - -psub - -First, we calculate the slashing reward for a single slashing event, indicated by $'$: - -\begin{equation} -\begin{aligned} -\psi' &= \frac{\bar{B}}{\text{MIN_SLASHING_PENALTY_QUOTIENT}}\\ -\end{aligned} -\end{equation} - -The **whistleblower rewards** are made up of both a reward for the whistleblower, and for the proposer: - -\begin{equation} -\begin{aligned} -R'_w &= \frac{\bar{B}}{\text{WHISTLEBLOWER_REWARD_QUOTIENT}} \qquad &(\text{reward for whistleblower})\\ -&+ \psi' \times \frac{\text{PROPOSER_WEIGHT}}{\text{WEIGHT_DENOMINATOR}} \qquad &(\text{reward for proposer}\\ -&&\text{who includes slashing})\\ -\end{aligned} -\end{equation} - -In addition to the **slashing penalty**, there is a slashing penalty proportional to the total slashings in the current time period using the `PROPORTIONAL_SLASHING_MULTIPLIER`: - -\begin{equation} -\begin{aligned} -N &= \frac{\text{slashing_events_per_1000_epochs}}{1000} \qquad (\text{slashing events in epoch})\\ -\psi'_{proportional} &= \frac{\bar{B}}{\text{EFFECTIVE_BALANCE_INCREMENT}}\\ -&\times min(\psi' \times N \times \text{PROPORTIONAL_SLASHING_MULTIPLIER},X)\\ -&\times \frac{\text{EFFECTIVE_BALANCE_INCREMENT}}{X} -\end{aligned} -\end{equation} - - -Finally, the individual slashing penalty is calculated as the sum of the individual slashing and proportional slashing penalties: - -$$ -\psi' = \psi' + \psi'_{proportional} -$$ - -To calculate the **total amount slashed** and **whistleblower rewards** for the epoch, we scale by the number of slashing events per epoch: - -\begin{equation} -\begin{aligned} -\psi &= \psi' \times N\\ -R_w &= R'_w \times N\\ -\end{aligned} -\end{equation} - -### PSUB 10: EIP1559 Transaction Pricing - -psub - -EIP-1559 replaces the current transaction gas price (in Gwei per gas), with two values: a dynamic base fee that is burned and applied to all transactions, and a priority fee per transaction that is paid to miners/validators. - -The current gas limit is replaced by two values: -* a “long-term average target” equal to the current gas limit -* a “hard per-block cap” which is twice the current gas limit - -The long-term average gas target per block is set to 15m gas; by default we assume the gas used per block will on average be equal to the gas target. - -Pre-merge, while Proof-of-Work is still active, miners receive the priority fee, and the gas used is calculated according to block-time: -\begin{equation} -\begin{aligned} -\text{gas used} &= \text{blocks per epoch} \times \text{gas target}\\ -F &= \text{gas used} \times f\\ -T = T_m &= \text{gas used} \times t\\ -\end{aligned} -\end{equation} - -Post-merge, when Proof-of-Work is deprecated and Proof-of-Stake validators start including transactions, validators receive the priority fee, and the gas used is calculated according to slot-time: -\begin{equation} -\begin{aligned} -\text{gas used} &= \text{slots per epoch} \times \text{gas target}\\ -F &= \text{gas used} \times f\\ -T = T_v &= \text{gas used} \times t\\ -\end{aligned} -\end{equation} - -## System Metrics - -System Metrics are computed from State Variables in order to assess the performance of the system. The calculation of our System Metrics is also represented in the [model's cadCAD Canvas / Differential Model Specification](https://lucid.app/lucidchart/c7656072-e601-4ec4-a44b-0a15c9a5700d/view) and accessible via LucidChart. Below is an illustrative screenshot. - -![](https://i.imgur.com/5xAaCCm.png) - -The following state-update logic for system metric State Variables could also be performed in post-processing, assuming there are no feedback loops that influence the metrics, to improve run-time performance. - -#### Validator Reward Aggregation - -The **total online validator rewards** is the *net* rewards and penalties awarded to online validators accounting for validating, whistleblowing, and priority fees post-merge: - -$$ -R_o = R_v + R_w + T - Z -$$ - -#### Ethereum Issuance - -The **ETH supply** at the next epoch is equal to the sum of the ETH supply at the current epoch and the net network issuance: -$$ -S^+ = S + (R_v + R_w - Z - \psi - F) -$$ - -#### Validator Costs - -The **validator costs** is the sum of hardware, cloud, and third-party costs per validator type: -$$ -\vec{C} = \vec{C}_{hardware} + \vec{C}_{cloud} + \vec{C}_{third-party} \qquad ([$]) -$$ - -The **total network costs** is the sum of validator costs over all validator types (row index $i$): -$$ -C = \sum_{i}{\vec{C}_{ij}} \qquad ($) -$$ - -#### Validator Revenue and Profit - -The **validator revenue** is the rewards for online validators in ETH, $R_o / 10^9$, distributed according to the validator percentage distribution multiplied by the current ETH price $P$: -$$ -\vec{K}_r = \text{validator_percentage_distribution} \times R_o / 10^9 \times P \qquad ([$]) -$$ - -The **validator profit** is the validator revenue less the validator costs: -$$ -\vec{K}_p = \vec{K}_r - \vec{C} \qquad ([$]) -$$ - -The **total revenue** is the sum of validator revenue over all validator types: -$$ -K_r = \sum_{i}{\vec{K}_{r,ij}} \qquad ($) -$$ - -The **total profit** is the total revenue less the total network costs: -$$ -K_p = K_r - C \qquad ($) -$$ - -#### Validator Revenue and Profit Yields - -The per-validator **revenue and profit yields** are calculated and annualized as the validator profit and revenue multiplied by the number of epochs in a year divided by the validator ETH staked, $\sigma$, in dollars: -$$\vec{Y}_r = \frac{\vec{K}_r \times E_{year}}{\sigma \times P} \qquad ([\%])$$ -$$\vec{Y}_p = \frac{\vec{K}_p \times E_{year}}{\sigma \times P} \qquad ([\%])$$ - -The total **revenue and profit yields** are calculated and annualized as the total profit and revenue multiplied by the number of epochs in a year divided by the total ETH staked, $X$, in dollars: $$Y_r = \frac{K_r \times E_{year}}{X \times P} \qquad (\%)$$ $$Y_p = \frac{K_p \times E_{year}}{X \times P} \qquad (\%)$$ diff --git a/docs/notebooks/.gitkeep b/docs/notebooks/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/experiments/notebooks/setup.py b/experiments/notebooks/setup.py index 73cc6300..e5c52306 100644 --- a/experiments/notebooks/setup.py +++ b/experiments/notebooks/setup.py @@ -6,7 +6,7 @@ ipython = get_ipython() # Find performance bottlenecks by timing Python cell execution -ipython.magic("load_ext autotime") # ipython.magic("...") is equivalent to % in Jupyter cell +# ipython.magic("load_ext autotime") # ipython.magic("...") is equivalent to % in Jupyter cell # Reload all modules (except those excluded by %aimport) every time before executing the Python code typed # See https://ipython.org/ipython-doc/stable/config/extensions/autoreload.html diff --git a/model/stochastic_processes.py b/model/stochastic_processes.py index 3f31865b..5947df1d 100644 --- a/model/stochastic_processes.py +++ b/model/stochastic_processes.py @@ -1,5 +1,5 @@ """ -Helper functions to generate stochastic environmental processes +Helper functions to generate stochastic environmental processes. """ import numpy as np diff --git a/model/types.py b/model/types.py index c7c81ebb..ad9b4bc7 100644 --- a/model/types.py +++ b/model/types.py @@ -1,5 +1,5 @@ """ -Various Python types used in the model +Various Python types used in the model. """ import numpy as np