package config import ( "bytes" "encoding/hex" "encoding/json" "fmt" "math" "net/http" "net/http/httptest" "reflect" "testing" "github.com/OffchainLabs/prysm/v6/api/server/structs" "github.com/OffchainLabs/prysm/v6/config/params" "github.com/OffchainLabs/prysm/v6/consensus-types/primitives" "github.com/OffchainLabs/prysm/v6/testing/assert" "github.com/OffchainLabs/prysm/v6/testing/require" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" log "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" ) func TestGetDepositContract(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig().Copy() config.DepositChainID = uint64(10) config.DepositContractAddress = "0x4242424242424242424242424242424242424242" params.OverrideBeaconConfig(config) request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/config/deposit_contract", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} GetDepositContract(writer, request) require.Equal(t, http.StatusOK, writer.Code) response := structs.GetDepositContractResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &response)) assert.Equal(t, "10", response.Data.ChainId) assert.Equal(t, "0x4242424242424242424242424242424242424242", response.Data.Address) } func TestGetSpec(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig().Copy() config.ConfigName = "ConfigName" config.PresetBase = "PresetBase" config.MaxCommitteesPerSlot = 1 config.TargetCommitteeSize = 2 config.MaxValidatorsPerCommittee = 3 config.MinPerEpochChurnLimit = 4 config.ChurnLimitQuotient = 5 config.ShuffleRoundCount = 6 config.MinGenesisActiveValidatorCount = 7 config.MinGenesisTime = 8 config.HysteresisQuotient = 9 config.HysteresisDownwardMultiplier = 10 config.HysteresisUpwardMultiplier = 11 config.Eth1FollowDistance = 13 config.TargetAggregatorsPerCommittee = 14 config.RandomSubnetsPerValidator = 15 config.EpochsPerRandomSubnetSubscription = 16 config.SecondsPerETH1Block = 17 config.DepositChainID = 18 config.DepositNetworkID = 19 config.DepositContractAddress = "DepositContractAddress" config.MinDepositAmount = 20 config.MaxEffectiveBalance = 21 config.EjectionBalance = 22 config.EffectiveBalanceIncrement = 23 config.GenesisForkVersion = []byte("GenesisForkVersion") config.AltairForkVersion = []byte("AltairForkVersion") config.AltairForkEpoch = 100 config.BellatrixForkVersion = []byte("BellatrixForkVersion") config.BellatrixForkEpoch = 101 config.CapellaForkVersion = []byte("CapellaForkVersion") config.CapellaForkEpoch = 103 config.DenebForkVersion = []byte("DenebForkVersion") config.DenebForkEpoch = 105 config.ElectraForkVersion = []byte("ElectraForkVersion") config.ElectraForkEpoch = 107 config.FuluForkVersion = []byte("FuluForkVersion") config.FuluForkEpoch = 109 config.BLSWithdrawalPrefixByte = byte('b') config.ETH1AddressWithdrawalPrefixByte = byte('c') config.GenesisDelay = 24 config.SecondsPerSlot = 25 config.MinAttestationInclusionDelay = 26 config.SlotsPerEpoch = 27 config.MinSeedLookahead = 28 config.MaxSeedLookahead = 29 config.EpochsPerEth1VotingPeriod = 30 config.SlotsPerHistoricalRoot = 31 config.MinValidatorWithdrawabilityDelay = 32 config.ShardCommitteePeriod = 33 config.MinEpochsToInactivityPenalty = 34 config.EpochsPerHistoricalVector = 35 config.EpochsPerSlashingsVector = 36 config.HistoricalRootsLimit = 37 config.ValidatorRegistryLimit = 38 config.BaseRewardFactor = 39 config.WhistleBlowerRewardQuotient = 40 config.ProposerRewardQuotient = 41 config.InactivityPenaltyQuotient = 42 config.MinSlashingPenaltyQuotient = 44 config.ProportionalSlashingMultiplier = 46 config.MaxProposerSlashings = 48 config.MaxAttesterSlashings = 49 config.MaxAttestations = 50 config.MaxDeposits = 51 config.MaxVoluntaryExits = 52 config.TimelyHeadFlagIndex = 53 config.TimelySourceFlagIndex = 54 config.TimelyTargetFlagIndex = 55 config.TimelyHeadWeight = 56 config.TimelySourceWeight = 57 config.TimelyTargetWeight = 58 config.SyncRewardWeight = 59 config.WeightDenominator = 60 config.TargetAggregatorsPerSyncSubcommittee = 61 config.SyncCommitteeSubnetCount = 62 config.SyncCommitteeSize = 63 config.InactivityScoreBias = 65 config.EpochsPerSyncCommitteePeriod = 66 config.InactivityPenaltyQuotientAltair = 67 config.MinSlashingPenaltyQuotientAltair = 68 config.ProportionalSlashingMultiplierAltair = 69 config.InactivityScoreRecoveryRate = 70 config.MinSyncCommitteeParticipants = 71 config.TerminalBlockHash = common.HexToHash("TerminalBlockHash") config.TerminalBlockHashActivationEpoch = 72 config.TerminalTotalDifficulty = "73" config.DefaultFeeRecipient = common.HexToAddress("DefaultFeeRecipient") config.MaxWithdrawalsPerPayload = 74 config.MaxBlsToExecutionChanges = 75 config.MaxValidatorsPerWithdrawalsSweep = 76 config.MinSlashingPenaltyQuotientElectra = 77 config.MaxEffectiveBalanceElectra = 78 config.CompoundingWithdrawalPrefixByte = byte('d') config.WhistleBlowerRewardQuotientElectra = 79 config.PendingPartialWithdrawalsLimit = 80 config.MinActivationBalance = 81 config.PendingDepositsLimit = 82 config.MaxPendingPartialsPerWithdrawalsSweep = 83 config.PendingConsolidationsLimit = 84 config.MaxPartialWithdrawalsPerPayload = 85 config.FullExitRequestAmount = 86 config.MaxConsolidationsRequestsPerPayload = 87 config.MaxAttesterSlashingsElectra = 88 config.MaxAttestationsElectra = 89 config.MaxWithdrawalRequestsPerPayload = 90 config.MaxCellsInExtendedMatrix = 91 config.UnsetDepositRequestsStartIndex = 92 config.MaxDepositRequestsPerPayload = 93 config.MaxPendingDepositsPerEpoch = 94 config.MaxBlobCommitmentsPerBlock = 95 config.MaxBytesPerTransaction = 96 config.MaxExtraDataBytes = 97 config.BytesPerLogsBloom = 98 config.MaxTransactionsPerPayload = 99 config.FieldElementsPerBlob = 100 config.KzgCommitmentInclusionProofDepth = 101 config.BlobsidecarSubnetCount = 102 config.BlobsidecarSubnetCountElectra = 103 var dbp [4]byte copy(dbp[:], []byte{'0', '0', '0', '1'}) config.DomainBeaconProposer = dbp var dba [4]byte copy(dba[:], []byte{'0', '0', '0', '2'}) config.DomainBeaconAttester = dba var dr [4]byte copy(dr[:], []byte{'0', '0', '0', '3'}) config.DomainRandao = dr var dd [4]byte copy(dd[:], []byte{'0', '0', '0', '4'}) config.DomainDeposit = dd var dve [4]byte copy(dve[:], []byte{'0', '0', '0', '5'}) config.DomainVoluntaryExit = dve var dsp [4]byte copy(dsp[:], []byte{'0', '0', '0', '6'}) config.DomainSelectionProof = dsp var daap [4]byte copy(daap[:], []byte{'0', '0', '0', '7'}) config.DomainAggregateAndProof = daap var dam [4]byte copy(dam[:], []byte{'1', '0', '0', '0'}) config.DomainApplicationMask = dam params.OverrideBeaconConfig(config) request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/config/spec", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} GetSpec(writer, request) require.Equal(t, http.StatusOK, writer.Code) resp := structs.GetSpecResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &resp)) data, ok := resp.Data.(map[string]interface{}) require.Equal(t, true, ok) assert.Equal(t, 176, len(data)) for k, v := range data { t.Run(k, func(t *testing.T) { switch k { case "CONFIG_NAME": assert.Equal(t, "ConfigName", v) case "PRESET_BASE": assert.Equal(t, "PresetBase", v) case "MAX_COMMITTEES_PER_SLOT": assert.Equal(t, "1", v) case "TARGET_COMMITTEE_SIZE": assert.Equal(t, "2", v) case "MAX_VALIDATORS_PER_COMMITTEE": assert.Equal(t, "3", v) case "MIN_PER_EPOCH_CHURN_LIMIT": assert.Equal(t, "4", v) case "CHURN_LIMIT_QUOTIENT": assert.Equal(t, "5", v) case "SHUFFLE_ROUND_COUNT": assert.Equal(t, "6", v) case "MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": assert.Equal(t, "7", v) case "MIN_GENESIS_TIME": assert.Equal(t, "8", v) case "HYSTERESIS_QUOTIENT": assert.Equal(t, "9", v) case "HYSTERESIS_DOWNWARD_MULTIPLIER": assert.Equal(t, "10", v) case "HYSTERESIS_UPWARD_MULTIPLIER": assert.Equal(t, "11", v) case "SAFE_SLOTS_TO_UPDATE_JUSTIFIED": assert.Equal(t, "0", v) case "ETH1_FOLLOW_DISTANCE": assert.Equal(t, "13", v) case "TARGET_AGGREGATORS_PER_COMMITTEE": assert.Equal(t, "14", v) case "RANDOM_SUBNETS_PER_VALIDATOR": assert.Equal(t, "15", v) case "EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION": assert.Equal(t, "16", v) case "SECONDS_PER_ETH1_BLOCK": assert.Equal(t, "17", v) case "DEPOSIT_CHAIN_ID": assert.Equal(t, "18", v) case "DEPOSIT_NETWORK_ID": assert.Equal(t, "19", v) case "DEPOSIT_CONTRACT_ADDRESS": assert.Equal(t, "DepositContractAddress", v) case "MIN_DEPOSIT_AMOUNT": assert.Equal(t, "20", v) case "MAX_EFFECTIVE_BALANCE": assert.Equal(t, "21", v) case "EJECTION_BALANCE": assert.Equal(t, "22", v) case "EFFECTIVE_BALANCE_INCREMENT": assert.Equal(t, "23", v) case "GENESIS_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("GenesisForkVersion")), v) case "ALTAIR_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("AltairForkVersion")), v) case "ALTAIR_FORK_EPOCH": assert.Equal(t, "100", v) case "BELLATRIX_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("BellatrixForkVersion")), v) case "BELLATRIX_FORK_EPOCH": assert.Equal(t, "101", v) case "CAPELLA_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("CapellaForkVersion")), v) case "CAPELLA_FORK_EPOCH": assert.Equal(t, "103", v) case "DENEB_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("DenebForkVersion")), v) case "DENEB_FORK_EPOCH": assert.Equal(t, "105", v) case "ELECTRA_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("ElectraForkVersion")), v) case "ELECTRA_FORK_EPOCH": assert.Equal(t, "107", v) case "FULU_FORK_VERSION": assert.Equal(t, "0x"+hex.EncodeToString([]byte("FuluForkVersion")), v) case "FULU_FORK_EPOCH": assert.Equal(t, "109", v) case "MIN_ANCHOR_POW_BLOCK_DIFFICULTY": assert.Equal(t, "1000", v) case "BLS_WITHDRAWAL_PREFIX": assert.Equal(t, "0x62", v) case "ETH1_ADDRESS_WITHDRAWAL_PREFIX": assert.Equal(t, "0x63", v) case "GENESIS_DELAY": assert.Equal(t, "24", v) case "SECONDS_PER_SLOT": assert.Equal(t, "25", v) case "MIN_ATTESTATION_INCLUSION_DELAY": assert.Equal(t, "26", v) case "SLOTS_PER_EPOCH": assert.Equal(t, "27", v) case "MIN_SEED_LOOKAHEAD": assert.Equal(t, "28", v) case "MAX_SEED_LOOKAHEAD": assert.Equal(t, "29", v) case "EPOCHS_PER_ETH1_VOTING_PERIOD": assert.Equal(t, "30", v) case "SLOTS_PER_HISTORICAL_ROOT": assert.Equal(t, "31", v) case "MIN_VALIDATOR_WITHDRAWABILITY_DELAY": assert.Equal(t, "32", v) case "SHARD_COMMITTEE_PERIOD": assert.Equal(t, "33", v) case "MIN_EPOCHS_TO_INACTIVITY_PENALTY": assert.Equal(t, "34", v) case "EPOCHS_PER_HISTORICAL_VECTOR": assert.Equal(t, "35", v) case "EPOCHS_PER_SLASHINGS_VECTOR": assert.Equal(t, "36", v) case "HISTORICAL_ROOTS_LIMIT": assert.Equal(t, "37", v) case "VALIDATOR_REGISTRY_LIMIT": assert.Equal(t, "38", v) case "BASE_REWARD_FACTOR": assert.Equal(t, "39", v) case "WHISTLEBLOWER_REWARD_QUOTIENT": assert.Equal(t, "40", v) case "PROPOSER_REWARD_QUOTIENT": assert.Equal(t, "41", v) case "INACTIVITY_PENALTY_QUOTIENT": assert.Equal(t, "42", v) case "HF1_INACTIVITY_PENALTY_QUOTIENT": assert.Equal(t, "43", v) case "MIN_SLASHING_PENALTY_QUOTIENT": assert.Equal(t, "44", v) case "HF1_MIN_SLASHING_PENALTY_QUOTIENT": assert.Equal(t, "45", v) case "PROPORTIONAL_SLASHING_MULTIPLIER": assert.Equal(t, "46", v) case "HF1_PROPORTIONAL_SLASHING_MULTIPLIER": assert.Equal(t, "47", v) case "MAX_PROPOSER_SLASHINGS": assert.Equal(t, "48", v) case "MAX_ATTESTER_SLASHINGS": assert.Equal(t, "49", v) case "MAX_ATTESTATIONS": assert.Equal(t, "50", v) case "MAX_DEPOSITS": assert.Equal(t, "51", v) case "MAX_VOLUNTARY_EXITS": assert.Equal(t, "52", v) case "MAX_BLOBS_PER_BLOCK": assert.Equal(t, "6", v) case "TIMELY_HEAD_FLAG_INDEX": assert.Equal(t, "0x35", v) case "TIMELY_SOURCE_FLAG_INDEX": assert.Equal(t, "0x36", v) case "TIMELY_TARGET_FLAG_INDEX": assert.Equal(t, "0x37", v) case "TIMELY_HEAD_WEIGHT": assert.Equal(t, "56", v) case "TIMELY_SOURCE_WEIGHT": assert.Equal(t, "57", v) case "TIMELY_TARGET_WEIGHT": assert.Equal(t, "58", v) case "SYNC_REWARD_WEIGHT": assert.Equal(t, "59", v) case "WEIGHT_DENOMINATOR": assert.Equal(t, "60", v) case "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": assert.Equal(t, "61", v) case "SYNC_COMMITTEE_SUBNET_COUNT": assert.Equal(t, "62", v) case "SYNC_COMMITTEE_SIZE": assert.Equal(t, "63", v) case "SYNC_PUBKEYS_PER_AGGREGATE": assert.Equal(t, "64", v) case "INACTIVITY_SCORE_BIAS": assert.Equal(t, "65", v) case "EPOCHS_PER_SYNC_COMMITTEE_PERIOD": assert.Equal(t, "66", v) case "INACTIVITY_PENALTY_QUOTIENT_ALTAIR": assert.Equal(t, "67", v) case "MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR": assert.Equal(t, "68", v) case "PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR": assert.Equal(t, "69", v) case "INACTIVITY_SCORE_RECOVERY_RATE": assert.Equal(t, "70", v) case "MIN_SYNC_COMMITTEE_PARTICIPANTS": assert.Equal(t, "71", v) case "PROPOSER_WEIGHT": assert.Equal(t, "8", v) case "DOMAIN_BEACON_PROPOSER": assert.Equal(t, "0x30303031", v) case "DOMAIN_BEACON_ATTESTER": assert.Equal(t, "0x30303032", v) case "DOMAIN_RANDAO": assert.Equal(t, "0x30303033", v) case "DOMAIN_DEPOSIT": assert.Equal(t, "0x30303034", v) case "DOMAIN_VOLUNTARY_EXIT": assert.Equal(t, "0x30303035", v) case "DOMAIN_SELECTION_PROOF": assert.Equal(t, "0x30303036", v) case "DOMAIN_AGGREGATE_AND_PROOF": assert.Equal(t, "0x30303037", v) case "DOMAIN_APPLICATION_MASK": assert.Equal(t, "0x31303030", v) case "DOMAIN_SYNC_COMMITTEE": assert.Equal(t, "0x07000000", v) case "DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF": assert.Equal(t, "0x08000000", v) case "DOMAIN_CONTRIBUTION_AND_PROOF": assert.Equal(t, "0x09000000", v) case "DOMAIN_BLS_TO_EXECUTION_CHANGE": assert.Equal(t, "0x0a000000", v) case "DOMAIN_APPLICATION_BUILDER": assert.Equal(t, "0x00000001", v) case "DOMAIN_BLOB_SIDECAR": assert.Equal(t, "0x00000000", v) case "TRANSITION_TOTAL_DIFFICULTY": assert.Equal(t, "0", v) case "TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH": assert.Equal(t, "72", v) case "TERMINAL_BLOCK_HASH": s, ok := v.(string) require.Equal(t, true, ok) assert.Equal(t, common.HexToHash("TerminalBlockHash"), common.HexToHash(s)) case "TERMINAL_TOTAL_DIFFICULTY": assert.Equal(t, "73", v) case "DefaultFeeRecipient": assert.Equal(t, common.HexToAddress("DefaultFeeRecipient"), v) case "PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX": assert.Equal(t, "3", v) case "MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX": assert.Equal(t, "32", v) case "INACTIVITY_PENALTY_QUOTIENT_BELLATRIX": assert.Equal(t, "16777216", v) case "PROPOSER_SCORE_BOOST": assert.Equal(t, "40", v) case "INTERVALS_PER_SLOT": assert.Equal(t, "3", v) case "MAX_WITHDRAWALS_PER_PAYLOAD": assert.Equal(t, "74", v) case "MAX_BLS_TO_EXECUTION_CHANGES": assert.Equal(t, "75", v) case "MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP": assert.Equal(t, "76", v) case "REORG_MAX_EPOCHS_SINCE_FINALIZATION": assert.Equal(t, "2", v) case "REORG_HEAD_WEIGHT_THRESHOLD": assert.Equal(t, "20", v) case "REORG_PARENT_WEIGHT_THRESHOLD": assert.Equal(t, "160", v) case "MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT": assert.Equal(t, "8", v) case "MAX_REQUEST_LIGHT_CLIENT_UPDATES": assert.Equal(t, "128", v) case "SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY": case "NODE_ID_BITS": assert.Equal(t, "256", v) case "ATTESTATION_SUBNET_EXTRA_BITS": assert.Equal(t, "0", v) case "ATTESTATION_SUBNET_PREFIX_BITS": assert.Equal(t, "6", v) case "SUBNETS_PER_NODE": assert.Equal(t, "2", v) case "EPOCHS_PER_SUBNET_SUBSCRIPTION": assert.Equal(t, "256", v) case "MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS": assert.Equal(t, "4096", v) case "MAX_REQUEST_BLOB_SIDECARS": assert.Equal(t, "768", v) case "MESSAGE_DOMAIN_INVALID_SNAPPY": assert.Equal(t, "0x00000000", v) case "MESSAGE_DOMAIN_VALID_SNAPPY": assert.Equal(t, "0x01000000", v) case "ATTESTATION_PROPAGATION_SLOT_RANGE": assert.Equal(t, "32", v) case "RESP_TIMEOUT": assert.Equal(t, "10", v) case "TTFB_TIMEOUT": assert.Equal(t, "5", v) case "MIN_EPOCHS_FOR_BLOCK_REQUESTS": assert.Equal(t, "33024", v) case "MAX_PAYLOAD_SIZE": assert.Equal(t, "10485760", v) case "ATTESTATION_SUBNET_COUNT": assert.Equal(t, "64", v) case "MAXIMUM_GOSSIP_CLOCK_DISPARITY": assert.Equal(t, "500", v) case "MAX_REQUEST_BLOCKS": assert.Equal(t, "1024", v) case "MAX_REQUEST_BLOCKS_DENEB": assert.Equal(t, "128", v) case "NUMBER_OF_COLUMNS": assert.Equal(t, "128", v) case "MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA": assert.Equal(t, "128000000000", v) case "MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT": assert.Equal(t, "256000000000", v) case "DATA_COLUMN_SIDECAR_SUBNET_COUNT": assert.Equal(t, "128", v) case "MAX_REQUEST_DATA_COLUMN_SIDECARS": assert.Equal(t, "16384", v) case "MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA": assert.Equal(t, "77", v) case "MAX_EFFECTIVE_BALANCE_ELECTRA": assert.Equal(t, "78", v) case "COMPOUNDING_WITHDRAWAL_PREFIX": assert.Equal(t, "0x64", v) case "WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA": assert.Equal(t, "79", v) case "PENDING_PARTIAL_WITHDRAWALS_LIMIT": assert.Equal(t, "80", v) case "MIN_ACTIVATION_BALANCE": assert.Equal(t, "81", v) case "PENDING_DEPOSITS_LIMIT": assert.Equal(t, "82", v) case "MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP": assert.Equal(t, "83", v) case "PENDING_CONSOLIDATIONS_LIMIT": assert.Equal(t, "84", v) case "MAX_PARTIAL_WITHDRAWALS_PER_PAYLOAD": assert.Equal(t, "85", v) case "FULL_EXIT_REQUEST_AMOUNT": assert.Equal(t, "86", v) case "MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD": assert.Equal(t, "87", v) case "MAX_ATTESTER_SLASHINGS_ELECTRA": assert.Equal(t, "88", v) case "MAX_ATTESTATIONS_ELECTRA": assert.Equal(t, "89", v) case "MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD": assert.Equal(t, "90", v) case "MAX_CELLS_IN_EXTENDED_MATRIX": assert.Equal(t, "91", v) case "UNSET_DEPOSIT_REQUESTS_START_INDEX": assert.Equal(t, "92", v) case "MAX_DEPOSIT_REQUESTS_PER_PAYLOAD": assert.Equal(t, "93", v) case "MAX_PENDING_DEPOSITS_PER_EPOCH": assert.Equal(t, "94", v) case "TARGET_BLOBS_PER_BLOCK_ELECTRA": assert.Equal(t, "6", v) case "MAX_BLOBS_PER_BLOCK_ELECTRA": assert.Equal(t, "9", v) case "MAX_REQUEST_BLOB_SIDECARS_ELECTRA": assert.Equal(t, "1152", v) case "NUMBER_OF_CUSTODY_GROUPS": assert.Equal(t, "128", v) case "BALANCE_PER_ADDITIONAL_CUSTODY_GROUP": assert.Equal(t, "32000000000", v) case "CUSTODY_REQUIREMENT": assert.Equal(t, "4", v) case "SAMPLES_PER_SLOT": assert.Equal(t, "8", v) case "VALIDATOR_CUSTODY_REQUIREMENT": assert.Equal(t, "8", v) case "MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS": assert.Equal(t, "4096", v) case "MAX_BLOB_COMMITMENTS_PER_BLOCK": assert.Equal(t, "95", v) case "MAX_BYTES_PER_TRANSACTION": assert.Equal(t, "96", v) case "MAX_EXTRA_DATA_BYTES": assert.Equal(t, "97", v) case "BYTES_PER_LOGS_BLOOM": assert.Equal(t, "98", v) case "MAX_TRANSACTIONS_PER_PAYLOAD": assert.Equal(t, "99", v) case "FIELD_ELEMENTS_PER_BLOB": assert.Equal(t, "100", v) case "KZG_COMMITMENT_INCLUSION_PROOF_DEPTH": assert.Equal(t, "101", v) case "MAX_BLOBS_PER_BLOCK_FULU": assert.Equal(t, "12", v) case "BLOB_SIDECAR_SUBNET_COUNT": assert.Equal(t, "102", v) case "BLOB_SIDECAR_SUBNET_COUNT_ELECTRA": assert.Equal(t, "103", v) case "BLOB_SCHEDULE": // BLOB_SCHEDULE should be an empty slice when no schedule is defined blobSchedule, ok := v.([]interface{}) assert.Equal(t, true, ok) assert.Equal(t, 0, len(blobSchedule)) default: t.Errorf("Incorrect key: %s", k) } }) } } func TestForkSchedule_Ok(t *testing.T) { t.Run("ok", func(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig().Copy() config.InitializeForkSchedule() request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/config/fork_schedule", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} genesisStr, firstStr, secondStr := hexutil.Encode(config.GenesisForkVersion), hexutil.Encode(config.AltairForkVersion), hexutil.Encode(config.BellatrixForkVersion) GetForkSchedule(writer, request) require.Equal(t, http.StatusOK, writer.Code) resp := &structs.GetForkScheduleResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) schedule := params.SortedForkSchedule() require.Equal(t, len(schedule), len(resp.Data)) fork := resp.Data[0] assert.Equal(t, genesisStr, fork.PreviousVersion) assert.Equal(t, genesisStr, fork.CurrentVersion) assert.Equal(t, fmt.Sprintf("%d", config.GenesisEpoch), fork.Epoch) fork = resp.Data[1] assert.Equal(t, genesisStr, fork.PreviousVersion) assert.Equal(t, firstStr, fork.CurrentVersion) assert.Equal(t, fmt.Sprintf("%d", config.AltairForkEpoch), fork.Epoch) fork = resp.Data[2] assert.Equal(t, firstStr, fork.PreviousVersion) assert.Equal(t, secondStr, fork.CurrentVersion) assert.Equal(t, fmt.Sprintf("%d", config.BellatrixForkEpoch), fork.Epoch) }) t.Run("correct number of forks", func(t *testing.T) { request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/config/fork_schedule", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} GetForkSchedule(writer, request) require.Equal(t, http.StatusOK, writer.Code) resp := &structs.GetForkScheduleResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) os := params.SortedForkSchedule() assert.Equal(t, len(os), len(resp.Data)) }) } func TestGetSpec_BlobSchedule(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig().Copy() config.FuluForkEpoch = 1 // Set up a blob schedule with test data config.BlobSchedule = []params.BlobScheduleEntry{ { Epoch: primitives.Epoch(100), MaxBlobsPerBlock: 6, }, { Epoch: primitives.Epoch(200), MaxBlobsPerBlock: 9, }, } params.OverrideBeaconConfig(config) request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/config/spec", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} GetSpec(writer, request) require.Equal(t, http.StatusOK, writer.Code) resp := structs.GetSpecResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &resp)) data, ok := resp.Data.(map[string]interface{}) require.Equal(t, true, ok) // Verify BLOB_SCHEDULE is present and properly formatted blobScheduleValue, exists := data["BLOB_SCHEDULE"] require.Equal(t, true, exists) // Verify it's a slice of maps (actual JSON object, not string) // The JSON unmarshaling converts it to []interface{} with map[string]interface{} entries blobScheduleSlice, ok := blobScheduleValue.([]interface{}) require.Equal(t, true, ok) // Convert to generic interface for easier testing var blobSchedule []map[string]interface{} for _, entry := range blobScheduleSlice { entryMap, ok := entry.(map[string]interface{}) require.Equal(t, true, ok) blobSchedule = append(blobSchedule, entryMap) } // Verify the blob schedule content require.Equal(t, 2, len(blobSchedule)) // Check first entry - values should be strings for consistent API output assert.Equal(t, "100", blobSchedule[0]["EPOCH"]) assert.Equal(t, "6", blobSchedule[0]["MAX_BLOBS_PER_BLOCK"]) // Check second entry - values should be strings for consistent API output assert.Equal(t, "200", blobSchedule[1]["EPOCH"]) assert.Equal(t, "9", blobSchedule[1]["MAX_BLOBS_PER_BLOCK"]) } func TestGetSpec_BlobSchedule_NotFulu(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig().Copy() // Fulu not scheduled (default: math.MaxUint64) config.FuluForkEpoch = math.MaxUint64 config.BlobSchedule = []params.BlobScheduleEntry{ {Epoch: primitives.Epoch(100), MaxBlobsPerBlock: 6}, } params.OverrideBeaconConfig(config) request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/config/spec", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} GetSpec(writer, request) require.Equal(t, http.StatusOK, writer.Code) resp := structs.GetSpecResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &resp)) data, ok := resp.Data.(map[string]interface{}) require.Equal(t, true, ok) _, exists := data["BLOB_SCHEDULE"] require.Equal(t, false, exists) } func TestConvertValueForJSON_NoErrorLogsForStrings(t *testing.T) { logHook := logTest.NewLocal(log.StandardLogger()) defer logHook.Reset() stringTestCases := []struct { tag string value string }{ {"CONFIG_NAME", "mainnet"}, {"PRESET_BASE", "mainnet"}, {"DEPOSIT_CONTRACT_ADDRESS", "0x00000000219ab540356cBB839Cbe05303d7705Fa"}, {"TERMINAL_TOTAL_DIFFICULTY", "58750000000000000000000"}, } for _, tc := range stringTestCases { t.Run(tc.tag, func(t *testing.T) { logHook.Reset() // Convert the string value v := reflect.ValueOf(tc.value) result := convertValueForJSON(v, tc.tag) // Verify the result is correct require.Equal(t, tc.value, result) // Verify NO error was logged about unsupported field kind require.LogsDoNotContain(t, logHook, "Unsupported config field kind") require.LogsDoNotContain(t, logHook, "kind=string") }) } }