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 model.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.