Files
prysm/beacon-chain/rpc/eth/config/handlers.go
james-prysm 6139d58fa5 fixing config string parsing regression (#15773)
* adding string parsing and test

* gaz
2025-09-29 16:15:01 +00:00

184 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"fmt"
"math"
"net/http"
"reflect"
"strconv"
"strings"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
"github.com/OffchainLabs/prysm/v6/network/httputil"
"github.com/ethereum/go-ethereum/common/hexutil"
log "github.com/sirupsen/logrus"
)
// GetDepositContract retrieves deposit contract address and genesis fork version.
func GetDepositContract(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "config.GetDepositContract")
defer span.End()
httputil.WriteJson(w, &structs.GetDepositContractResponse{
Data: &structs.DepositContractData{
ChainId: strconv.FormatUint(params.BeaconConfig().DepositChainID, 10),
Address: params.BeaconConfig().DepositContractAddress,
},
})
}
// GetForkSchedule retrieve all scheduled upcoming forks this node is aware of.
func GetForkSchedule(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "config.GetForkSchedule")
defer span.End()
schedule := params.SortedForkSchedule()
data := make([]*structs.Fork, 0, len(schedule))
if len(schedule) == 0 {
httputil.WriteJson(w, &structs.GetForkScheduleResponse{
Data: data,
})
}
previous := schedule[0]
for _, entry := range schedule {
data = append(data, &structs.Fork{
PreviousVersion: hexutil.Encode(previous.ForkVersion[:]),
CurrentVersion: hexutil.Encode(entry.ForkVersion[:]),
Epoch: fmt.Sprintf("%d", entry.Epoch),
})
previous = entry
}
httputil.WriteJson(w, &structs.GetForkScheduleResponse{
Data: data,
})
}
// GetSpec retrieves specification configuration (without Phase 1 params) used on this node. Specification params list
// Values are returned with following format:
// - any value starting with 0x in the spec is returned as a hex string.
// - all other values are returned as number.
func GetSpec(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "config.GetSpec")
defer span.End()
data, err := prepareConfigSpec()
if err != nil {
httputil.HandleError(w, "Could not prepare config spec: "+err.Error(), http.StatusInternalServerError)
return
}
httputil.WriteJson(w, &structs.GetSpecResponse{Data: data})
}
func convertValueForJSON(v reflect.Value, tag string) interface{} {
// Unwrap pointers / interfaces
for v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr {
if v.IsNil() {
return nil
}
v = v.Elem()
}
switch v.Kind() {
// ===== Single byte → 0xAB =====
case reflect.Uint8:
return hexutil.Encode([]byte{uint8(v.Uint())})
// ===== Other unsigned numbers → "123" =====
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(v.Uint(), 10)
// ===== Signed numbers → "123" =====
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10)
// ===== Raw bytes encode to hex =====
case reflect.Slice:
if v.Type().Elem().Kind() == reflect.Uint8 {
return hexutil.Encode(v.Bytes())
}
fallthrough
case reflect.Array:
if v.Type().Elem().Kind() == reflect.Uint8 {
// Need a copy because v.Slice is illegal on arrays directly
tmp := make([]byte, v.Len())
reflect.Copy(reflect.ValueOf(tmp), v)
return hexutil.Encode(tmp)
}
// Generic slice/array handling
n := v.Len()
out := make([]interface{}, n)
for i := 0; i < n; i++ {
out[i] = convertValueForJSON(v.Index(i), tag)
}
return out
// ===== Struct =====
case reflect.Struct:
t := v.Type()
m := make(map[string]interface{}, v.NumField())
for i := 0; i < v.NumField(); i++ {
f := t.Field(i)
if !v.Field(i).CanInterface() {
continue // unexported
}
key := f.Tag.Get("json")
if key == "" || key == "-" {
key = f.Name
}
m[key] = convertValueForJSON(v.Field(i), tag)
}
return m
// ===== String =====
case reflect.String:
return v.String()
// ===== Default =====
default:
log.WithFields(log.Fields{
"fn": "prepareConfigSpec",
"tag": tag,
"kind": v.Kind().String(),
"type": v.Type().String(),
}).Error("Unsupported config field kind; value forwarded verbatim")
return v.Interface()
}
}
func prepareConfigSpec() (map[string]interface{}, error) {
data := make(map[string]interface{})
config := *params.BeaconConfig()
t := reflect.TypeOf(config)
v := reflect.ValueOf(config)
for i := 0; i < t.NumField(); i++ {
tField := t.Field(i)
_, isSpec := tField.Tag.Lookup("spec")
if !isSpec {
continue
}
if shouldSkip(tField) {
continue
}
tag := strings.ToUpper(tField.Tag.Get("yaml"))
val := v.Field(i)
data[tag] = convertValueForJSON(val, tag)
}
return data, nil
}
func shouldSkip(tField reflect.StructField) bool {
// Dynamically skip blob schedule if Fulu is not yet scheduled.
if params.BeaconConfig().FuluForkEpoch == math.MaxUint64 &&
tField.Type == reflect.TypeOf(params.BeaconConfig().BlobSchedule) {
return true
}
return false
}