diff --git a/boot_node/src/lib.rs b/boot_node/src/lib.rs index 3d9dada0f..ca8b5082a 100644 --- a/boot_node/src/lib.rs +++ b/boot_node/src/lib.rs @@ -64,6 +64,9 @@ pub fn run( EthSpecId::Gnosis => { main::(lh_matches, bn_matches, eth2_network_config, log) } + EthSpecId::PulseChain => { + main::(lh_matches, bn_matches, eth2_network_config, log) + } } { slog::crit!(slog_scope::logger(), "{}", e); } diff --git a/common/eth2_config/src/lib.rs b/common/eth2_config/src/lib.rs index 13ffe6926..fd8deed7b 100644 --- a/common/eth2_config/src/lib.rs +++ b/common/eth2_config/src/lib.rs @@ -60,6 +60,13 @@ impl Eth2Config { spec: ChainSpec::gnosis(), } } + + pub fn pulsechain() -> Self { + Self { + eth_spec_id: EthSpecId::PulseChain, + spec: ChainSpec::pulsechain(), + } + } } /// A directory that can be built by downloading files via HTTP. @@ -283,7 +290,7 @@ define_hardcoded_nets!( pulsechain_testnet_v3, // The name of the directory in the `eth2_network_config/built_in_network_configs` // directory where the configuration files are located for this network. - "pulsechain_testnet_v3", + "pulsechain-testnet-v3", // Set to `true` if the genesis state can be found in the `built_in_network_configs` // directory. GENESIS_STATE_IS_KNOWN diff --git a/common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/boot_enr.yaml b/common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/boot_enr.yaml similarity index 100% rename from common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/boot_enr.yaml rename to common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/boot_enr.yaml diff --git a/common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/config.yaml b/common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/config.yaml similarity index 94% rename from common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/config.yaml rename to common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/config.yaml index 2f0c40092..36fee66fa 100644 --- a/common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/config.yaml @@ -2,7 +2,7 @@ # Extends the mainnet preset CONFIG_NAME: 'pulsechain-testnet-v3' -PRESET_BASE: 'mainnet' +PRESET_BASE: 'pulsechain' # Transition # --------------------------------------------------------------- @@ -48,8 +48,6 @@ ETH1_FOLLOW_DISTANCE: 2048 # Validator cycle # --------------------------------------------------------------- -# 2**5 * 10**15 (= 32,000,000,000,000,000) Gwei -MAX_EFFECTIVE_BALANCE: 32000000000000000 # 2**2 (= 4) INACTIVITY_SCORE_BIAS: 4 # 2**4 (= 16) diff --git a/common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/deploy_block.txt b/common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/deploy_block.txt similarity index 100% rename from common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/deploy_block.txt rename to common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/deploy_block.txt diff --git a/common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/genesis.ssz.zip b/common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/genesis.ssz.zip similarity index 100% rename from common/eth2_network_config/built_in_network_configs/pulsechain_testnet_v3/genesis.ssz.zip rename to common/eth2_network_config/built_in_network_configs/pulsechain-testnet-v3/genesis.ssz.zip diff --git a/common/eth2_network_config/src/lib.rs b/common/eth2_network_config/src/lib.rs index 7274bbf02..2d02b285f 100644 --- a/common/eth2_network_config/src/lib.rs +++ b/common/eth2_network_config/src/lib.rs @@ -226,7 +226,7 @@ mod tests { use super::*; use ssz::Encode; use tempfile::Builder as TempBuilder; - use types::{Config, Eth1Data, GnosisEthSpec, Hash256, MainnetEthSpec}; + use types::{Config, Eth1Data, GnosisEthSpec, Hash256, MainnetEthSpec, PulseChainEthSpec}; type E = MainnetEthSpec; @@ -275,10 +275,13 @@ mod tests { for net in HARDCODED_NETS { let config = Eth2NetworkConfig::from_hardcoded_net(net) .unwrap_or_else(|_| panic!("{:?}", net.name)); + let preset_base = config.config.preset_base.clone(); // Ensure we can parse the YAML config to a chain spec. - if net.name == types::GNOSIS { + if preset_base == types::GNOSIS { config.chain_spec::().unwrap(); + } else if preset_base == types::PULSECHAIN { + config.chain_spec::().unwrap(); } else { config.chain_spec::().unwrap(); } diff --git a/consensus/types/presets/pulsechain/altair.yaml b/consensus/types/presets/pulsechain/altair.yaml new file mode 100644 index 000000000..fdebb5e5c --- /dev/null +++ b/consensus/types/presets/pulsechain/altair.yaml @@ -0,0 +1,24 @@ +# PulseChain preset - Altair + +# Updated penalty values +# --------------------------------------------------------------- +# 3 * 2**24 (= 50,331,648) +INACTIVITY_PENALTY_QUOTIENT_ALTAIR: 50331648 +# 2**6 (= 64) +MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: 64 +# 2 +PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: 2 + + +# Sync committee +# --------------------------------------------------------------- +# 2**9 (= 512) +SYNC_COMMITTEE_SIZE: 512 +# 2**8 (= 256) +EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256 + + +# Sync protocol +# --------------------------------------------------------------- +# 1 +MIN_SYNC_COMMITTEE_PARTICIPANTS: 1 diff --git a/consensus/types/presets/pulsechain/bellatrix.yaml b/consensus/types/presets/pulsechain/bellatrix.yaml new file mode 100644 index 000000000..ba044047b --- /dev/null +++ b/consensus/types/presets/pulsechain/bellatrix.yaml @@ -0,0 +1,21 @@ +# PulseChain preset - Bellatrix + +# Updated penalty values +# --------------------------------------------------------------- +# 2**24 (= 16,777,216) +INACTIVITY_PENALTY_QUOTIENT_BELLATRIX: 16777216 +# 2**5 (= 32) +MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX: 32 +# 3 +PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX: 3 + +# Execution +# --------------------------------------------------------------- +# 2**30 (= 1,073,741,824) +MAX_BYTES_PER_TRANSACTION: 1073741824 +# 2**20 (= 1,048,576) +MAX_TRANSACTIONS_PER_PAYLOAD: 1048576 +# 2**8 (= 256) +BYTES_PER_LOGS_BLOOM: 256 +# 2**5 (= 32) +MAX_EXTRA_DATA_BYTES: 32 diff --git a/consensus/types/presets/pulsechain/capella.yaml b/consensus/types/presets/pulsechain/capella.yaml new file mode 100644 index 000000000..15b327f44 --- /dev/null +++ b/consensus/types/presets/pulsechain/capella.yaml @@ -0,0 +1,17 @@ +# PulseChain preset - Capella + +# Misc +# Max operations per block +# --------------------------------------------------------------- +# 2**4 (= 16) +MAX_BLS_TO_EXECUTION_CHANGES: 16 + +# Execution +# --------------------------------------------------------------- +# 2**4 (= 16) withdrawals +MAX_WITHDRAWALS_PER_PAYLOAD: 16 + +# Withdrawals processing +# --------------------------------------------------------------- +# 2**14 (= 16384) validators +MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: 16384 diff --git a/consensus/types/presets/pulsechain/phase0.yaml b/consensus/types/presets/pulsechain/phase0.yaml new file mode 100644 index 000000000..2074ca9b0 --- /dev/null +++ b/consensus/types/presets/pulsechain/phase0.yaml @@ -0,0 +1,94 @@ +# PulseChain preset - Phase0 + +# Misc +# --------------------------------------------------------------- +# 2**6 (= 64) +MAX_COMMITTEES_PER_SLOT: 64 +# 2**7 (= 128) +TARGET_COMMITTEE_SIZE: 128 +# 2**11 (= 2,048) +MAX_VALIDATORS_PER_COMMITTEE: 2048 +# See issue 563 +SHUFFLE_ROUND_COUNT: 90 +# 4 +HYSTERESIS_QUOTIENT: 4 +# 1 (minus 0.25) +HYSTERESIS_DOWNWARD_MULTIPLIER: 1 +# 5 (plus 1.25) +HYSTERESIS_UPWARD_MULTIPLIER: 5 + + +# Fork Choice +# --------------------------------------------------------------- +# 2**3 (= 8) +SAFE_SLOTS_TO_UPDATE_JUSTIFIED: 8 + + +# Gwei values +# --------------------------------------------------------------- +# 2**0 * 10**9 (= 1,000,000,000) Gwei +MIN_DEPOSIT_AMOUNT: 1000000000 +# 2**5 * 10**15 (= 32,000,000,000,000,000) Gwei +MAX_EFFECTIVE_BALANCE: 32000000000000000 +# 2**0 * 10**9 (= 1,000,000,000) Gwei +EFFECTIVE_BALANCE_INCREMENT: 1000000000 + + +# Time parameters +# --------------------------------------------------------------- +# 2**0 (= 1) slots 12 seconds +MIN_ATTESTATION_INCLUSION_DELAY: 1 +# 2**5 (= 32) slots 6.4 minutes +SLOTS_PER_EPOCH: 32 +# 2**0 (= 1) epochs 6.4 minutes +MIN_SEED_LOOKAHEAD: 1 +# 2**2 (= 4) epochs 25.6 minutes +MAX_SEED_LOOKAHEAD: 4 +# 2**6 (= 64) epochs ~6.8 hours +EPOCHS_PER_ETH1_VOTING_PERIOD: 64 +# 2**13 (= 8,192) slots ~27 hours +SLOTS_PER_HISTORICAL_ROOT: 8192 +# 2**2 (= 4) epochs 25.6 minutes +MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4 + + +# State list lengths +# --------------------------------------------------------------- +# 2**16 (= 65,536) epochs ~0.8 years +EPOCHS_PER_HISTORICAL_VECTOR: 65536 +# 2**13 (= 8,192) epochs ~36 days +EPOCHS_PER_SLASHINGS_VECTOR: 8192 +# 2**24 (= 16,777,216) historical roots, ~26,131 years +HISTORICAL_ROOTS_LIMIT: 16777216 +# 2**40 (= 1,099,511,627,776) validator spots +VALIDATOR_REGISTRY_LIMIT: 1099511627776 + + +# Reward and penalty quotients +# --------------------------------------------------------------- +# 2**6 (= 64) +BASE_REWARD_FACTOR: 64 +# 2**9 (= 512) +WHISTLEBLOWER_REWARD_QUOTIENT: 512 +# 2**3 (= 8) +PROPOSER_REWARD_QUOTIENT: 8 +# 2**26 (= 67,108,864) +INACTIVITY_PENALTY_QUOTIENT: 67108864 +# 2**7 (= 128) (lower safety margin at Phase 0 genesis) +MIN_SLASHING_PENALTY_QUOTIENT: 128 +# 1 (lower safety margin at Phase 0 genesis) +PROPORTIONAL_SLASHING_MULTIPLIER: 1 + + +# Max operations per block +# --------------------------------------------------------------- +# 2**4 (= 16) +MAX_PROPOSER_SLASHINGS: 16 +# 2**1 (= 2) +MAX_ATTESTER_SLASHINGS: 2 +# 2**7 (= 128) +MAX_ATTESTATIONS: 128 +# 2**4 (= 16) +MAX_DEPOSITS: 16 +# 2**4 (= 16) +MAX_VOLUNTARY_EXITS: 16 diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index 8d59a6586..c22e54a3c 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -871,6 +871,16 @@ impl ChainSpec { domain_bls_to_execution_change: 10, } } + + /// PulseChain specification. + pub fn pulsechain() -> Self { + let mut spec = Self::mainnet(); + spec.config_name = Some("pulsechain".to_string()); + spec.max_effective_balance = + option_wrapper(|| u64::checked_pow(2, 5)?.checked_mul(u64::checked_pow(10, 15)?)) + .expect("calculation does not overflow"); + spec + } } impl Default for ChainSpec { @@ -952,8 +962,6 @@ pub struct Config { #[serde(with = "eth2_serde_utils::quoted_u64")] inactivity_score_recovery_rate: u64, #[serde(with = "eth2_serde_utils::quoted_u64")] - max_effective_balance: u64, - #[serde(with = "eth2_serde_utils::quoted_u64")] ejection_balance: u64, #[serde(with = "eth2_serde_utils::quoted_u64")] min_per_epoch_churn_limit: u64, @@ -1049,6 +1057,7 @@ impl Config { "minimal" => Some(EthSpecId::Minimal), "mainnet" => Some(EthSpecId::Mainnet), "gnosis" => Some(EthSpecId::Gnosis), + "pulsechain" => Some(EthSpecId::PulseChain), _ => None, } } @@ -1089,7 +1098,6 @@ impl Config { inactivity_score_bias: spec.inactivity_score_bias, inactivity_score_recovery_rate: spec.inactivity_score_recovery_rate, - max_effective_balance: spec.max_effective_balance, ejection_balance: spec.ejection_balance, churn_limit_quotient: spec.churn_limit_quotient, min_per_epoch_churn_limit: spec.min_per_epoch_churn_limit, @@ -1135,7 +1143,6 @@ impl Config { eth1_follow_distance, inactivity_score_bias, inactivity_score_recovery_rate, - max_effective_balance, ejection_balance, min_per_epoch_churn_limit, churn_limit_quotient, @@ -1168,7 +1175,6 @@ impl Config { eth1_follow_distance, inactivity_score_bias, inactivity_score_recovery_rate, - max_effective_balance, ejection_balance, min_per_epoch_churn_limit, churn_limit_quotient, diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index 378e8d34b..afd67bd2c 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -14,6 +14,7 @@ pub type U5000 = UInt, B0>, B0>; // 625 * 8 = 5000 const MAINNET: &str = "mainnet"; const MINIMAL: &str = "minimal"; pub const GNOSIS: &str = "gnosis"; +pub const PULSECHAIN: &str = "pulsechain"; /// Used to identify one of the `EthSpec` instances defined here. #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -22,6 +23,7 @@ pub enum EthSpecId { Mainnet, Minimal, Gnosis, + PulseChain, } impl FromStr for EthSpecId { @@ -32,6 +34,7 @@ impl FromStr for EthSpecId { MAINNET => Ok(EthSpecId::Mainnet), MINIMAL => Ok(EthSpecId::Minimal), GNOSIS => Ok(EthSpecId::Gnosis), + PULSECHAIN => Ok(EthSpecId::PulseChain), _ => Err(format!("Unknown eth spec: {}", s)), } } @@ -43,6 +46,7 @@ impl fmt::Display for EthSpecId { EthSpecId::Mainnet => MAINNET, EthSpecId::Minimal => MINIMAL, EthSpecId::Gnosis => GNOSIS, + EthSpecId::PulseChain => PULSECHAIN, }; write!(f, "{}", s) } @@ -383,3 +387,48 @@ impl EthSpec for GnosisEthSpec { EthSpecId::Gnosis } } + +/// PulseChain specification. +#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, arbitrary::Arbitrary)] +pub struct PulseChainEthSpec; + +impl EthSpec for PulseChainEthSpec { + // EthSpec values match MainnetEthSpec. + type JustificationBitsLength = U4; + type SubnetBitfieldLength = U64; + type MaxValidatorsPerCommittee = U2048; + type GenesisEpoch = U0; + type SlotsPerEpoch = U32; + type EpochsPerEth1VotingPeriod = U64; + type SlotsPerHistoricalRoot = U8192; + type EpochsPerHistoricalVector = U65536; + type EpochsPerSlashingsVector = U8192; + type HistoricalRootsLimit = U16777216; + type ValidatorRegistryLimit = U1099511627776; + type MaxProposerSlashings = U16; + type MaxAttesterSlashings = U2; + type MaxAttestations = U128; + type MaxDeposits = U16; + type MaxVoluntaryExits = U16; + type SyncCommitteeSize = U512; + type SyncCommitteeSubnetCount = U4; + type MaxBytesPerTransaction = U1073741824; // 1,073,741,824 + type MaxTransactionsPerPayload = U1048576; // 1,048,576 + type BytesPerLogsBloom = U256; + type GasLimitDenominator = U1024; + type MinGasLimit = U5000; + type MaxExtraDataBytes = U32; + type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count + type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch + type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch + type MaxBlsToExecutionChanges = U16; + type MaxWithdrawalsPerPayload = U16; + + fn default_spec() -> ChainSpec { + ChainSpec::pulsechain() + } + + fn spec_name() -> EthSpecId { + EthSpecId::PulseChain + } +} diff --git a/consensus/types/src/preset.rs b/consensus/types/src/preset.rs index 20c78f051..8f50964c9 100644 --- a/consensus/types/src/preset.rs +++ b/consensus/types/src/preset.rs @@ -208,7 +208,7 @@ impl CapellaPreset { #[cfg(test)] mod test { use super::*; - use crate::{GnosisEthSpec, MainnetEthSpec, MinimalEthSpec}; + use crate::{GnosisEthSpec, MainnetEthSpec, MinimalEthSpec, PulseChainEthSpec}; use serde::de::DeserializeOwned; use std::env; use std::fs::File; @@ -259,4 +259,9 @@ mod test { fn minimal_presets_consistent() { preset_test::(); } + + #[test] + fn pulsechain_presets_consistent() { + preset_test::(); + } } diff --git a/lcli/src/main.rs b/lcli/src/main.rs index cdf9cfa67..b649dc6fe 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -777,6 +777,7 @@ fn main() { EthSpecId::Minimal => run(EnvironmentBuilder::minimal(), &matches), EthSpecId::Mainnet => run(EnvironmentBuilder::mainnet(), &matches), EthSpecId::Gnosis => run(EnvironmentBuilder::gnosis(), &matches), + EthSpecId::PulseChain => run(EnvironmentBuilder::pulsechain(), &matches), }); match result { diff --git a/lighthouse/environment/src/lib.rs b/lighthouse/environment/src/lib.rs index 8ef67e82d..296533c29 100644 --- a/lighthouse/environment/src/lib.rs +++ b/lighthouse/environment/src/lib.rs @@ -21,7 +21,7 @@ use std::path::PathBuf; use std::sync::Arc; use task_executor::{ShutdownReason, TaskExecutor}; use tokio::runtime::{Builder as RuntimeBuilder, Runtime}; -use types::{EthSpec, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec}; +use types::{EthSpec, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec, PulseChainEthSpec}; #[cfg(target_family = "unix")] use { @@ -124,6 +124,19 @@ impl EnvironmentBuilder { } } +impl EnvironmentBuilder { + /// Creates a new builder using the `pulsechain` eth2 specification. + pub fn pulsechain() -> Self { + Self { + runtime: None, + log: None, + eth_spec_instance: PulseChainEthSpec, + eth2_config: Eth2Config::pulsechain(), + eth2_network_config: None, + } + } +} + impl EnvironmentBuilder { /// Specifies that a multi-threaded tokio runtime should be used. Ideal for production uses. /// diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index b05e78fe5..e8a6e60b3 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -58,7 +58,7 @@ fn main() { BLS library: {}\n\ SHA256 hardware acceleration: {}\n\ Allocator: {}\n\ - Specs: mainnet (true), minimal ({}), gnosis ({})", + Specs: mainnet (true), minimal ({}), gnosis ({}), pulsechain (true)", VERSION.replace("Lighthouse/", ""), bls_library_name(), have_sha_extensions(), @@ -363,6 +363,7 @@ fn main() { match eth_spec_id { EthSpecId::Mainnet => run(EnvironmentBuilder::mainnet(), &matches, eth2_network_config), + EthSpecId::PulseChain => run(EnvironmentBuilder::pulsechain(), &matches, eth2_network_config), #[cfg(feature = "gnosis")] EthSpecId::Gnosis => run(EnvironmentBuilder::gnosis(), &matches, eth2_network_config), #[cfg(feature = "spec-minimal")]