package rpc import ( "fmt" "strconv" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/prysm/v5/api/server" "github.com/prysmaticlabs/prysm/v5/api/server/structs" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/validator/keymanager" ) // local keymanager api type ListKeystoresResponse struct { Data []*Keystore `json:"data"` } type Keystore struct { ValidatingPubkey string `json:"validating_pubkey"` DerivationPath string `json:"derivation_path"` } type ImportKeystoresRequest struct { Keystores []string `json:"keystores"` Passwords []string `json:"passwords"` SlashingProtection string `json:"slashing_protection"` } type ImportKeystoresResponse struct { Data []*keymanager.KeyStatus `json:"data"` } type DeleteKeystoresRequest struct { Pubkeys []string `json:"pubkeys"` } type DeleteKeystoresResponse struct { Data []*keymanager.KeyStatus `json:"data"` SlashingProtection string `json:"slashing_protection"` } // voluntary exit keymanager api type SetVoluntaryExitResponse struct { Data *structs.SignedVoluntaryExit `json:"data"` } // gas limit keymanager api type GasLimitMetaData struct { Pubkey string `json:"pubkey"` GasLimit string `json:"gas_limit"` } type GetGasLimitResponse struct { Data *GasLimitMetaData `json:"data"` } type SetGasLimitRequest struct { GasLimit string `json:"gas_limit"` } // remote keymanager api type ListRemoteKeysResponse struct { Data []*RemoteKey `json:"data"` } type RemoteKey struct { Pubkey string `json:"pubkey"` Url string `json:"url"` Readonly bool `json:"readonly"` } type ImportRemoteKeysRequest struct { RemoteKeys []*RemoteKey `json:"remote_keys"` } type DeleteRemoteKeysRequest struct { Pubkeys []string `json:"pubkeys"` } type RemoteKeysResponse struct { Data []*keymanager.KeyStatus `json:"data"` } // Fee Recipient keymanager api type FeeRecipient struct { Pubkey string `json:"pubkey"` Ethaddress string `json:"ethaddress"` } type GetFeeRecipientByPubkeyResponse struct { Data *FeeRecipient `json:"data"` } type SetFeeRecipientByPubkeyRequest struct { Ethaddress string `json:"ethaddress"` } // Graffiti keymanager api type GetGraffitiResponse struct { Data *GraffitiData `json:"data"` } type GraffitiData struct { Pubkey string `json:"pubkey"` Graffiti string `json:"graffiti"` } type BeaconStatusResponse struct { BeaconNodeEndpoint string `json:"beacon_node_endpoint"` Connected bool `json:"connected"` Syncing bool `json:"syncing"` GenesisTime string `json:"genesis_time"` DepositContractAddress string `json:"deposit_contract_address"` ChainHead *ChainHead `json:"chain_head"` } // KeymanagerKind is a type of key manager for the wallet type KeymanagerKind string const ( derivedKeymanagerKind KeymanagerKind = "DERIVED" importedKeymanagerKind KeymanagerKind = "IMPORTED" web3signerKeymanagerKind KeymanagerKind = "WEB3SIGNER" ) type CreateWalletRequest struct { Keymanager KeymanagerKind `json:"keymanager"` WalletPassword string `json:"wallet_password"` Mnemonic string `json:"mnemonic"` NumAccounts uint64 `json:"num_accounts"` MnemonicLanguage string `json:"mnemonic_language"` } type CreateWalletResponse struct { Wallet *WalletResponse `json:"wallet"` } type GenerateMnemonicResponse struct { Mnemonic string `json:"mnemonic"` } type WalletResponse struct { WalletPath string `json:"wallet_path"` KeymanagerKind KeymanagerKind `json:"keymanager_kind"` } type ValidateKeystoresRequest struct { Keystores []string `json:"keystores"` KeystoresPassword string `json:"keystores_password"` } type RecoverWalletRequest struct { Mnemonic string `json:"mnemonic"` NumAccounts uint64 `json:"num_accounts"` WalletPassword string `json:"wallet_password"` Language string `json:"language"` Mnemonic25ThWord string `json:"mnemonic25th_word"` } type ImportSlashingProtectionRequest struct { SlashingProtectionJson string `json:"slashing_protection_json"` } type ExportSlashingProtectionResponse struct { File string `json:"file"` } type BackupAccountsRequest struct { PublicKeys []string `json:"public_keys"` BackupPassword string `json:"backup_password"` } type VoluntaryExitRequest struct { PublicKeys []string `json:"public_keys"` } type BackupAccountsResponse struct { ZipFile string `json:"zip_file"` } type ListAccountsResponse struct { Accounts []*Account `json:"accounts"` NextPageToken string `json:"next_page_token"` TotalSize int32 `json:"total_size"` } type Account struct { ValidatingPublicKey string `json:"validating_public_key"` AccountName string `json:"account_name"` DepositTxData string `json:"deposit_tx_data"` DerivationPath string `json:"derivation_path"` } type VoluntaryExitResponse struct { ExitedKeys [][]byte `protobuf:"bytes,1,rep,name=exited_keys,json=exitedKeys,proto3" json:"exited_keys,omitempty"` } type InitializeAuthResponse struct { HasSignedUp bool `json:"has_signed_up"` HasWallet bool `json:"has_wallet"` } type ValidatorPerformanceResponse struct { CurrentEffectiveBalances []uint64 `json:"current_effective_balances"` InclusionSlots []uint64 `json:"inclusion_slots"` InclusionDistances []uint64 `json:"inclusion_distances"` CorrectlyVotedSource []bool `json:"correctly_voted_source"` CorrectlyVotedTarget []bool `json:"correctly_voted_target"` CorrectlyVotedHead []bool `json:"correctly_voted_head"` BalancesBeforeEpochTransition []uint64 `json:"balances_before_epoch_transition"` BalancesAfterEpochTransition []uint64 `json:"balances_after_epoch_transition"` MissingValidators []string `json:"missing_validators"` AverageActiveValidatorBalance float32 `json:"average_active_validator_balance"` PublicKeys []string `json:"public_keys"` InactivityScores []uint64 `json:"inactivity_scores"` } func ValidatorPerformanceResponseFromConsensus(e *eth.ValidatorPerformanceResponse) *ValidatorPerformanceResponse { inclusionSlots := make([]uint64, len(e.InclusionSlots)) for i, index := range e.InclusionSlots { inclusionSlots[i] = uint64(index) } inclusionDistances := make([]uint64, len(e.InclusionDistances)) for i, index := range e.InclusionDistances { inclusionDistances[i] = uint64(index) } missingValidators := make([]string, len(e.MissingValidators)) for i, key := range e.MissingValidators { missingValidators[i] = hexutil.Encode(key) } publicKeys := make([]string, len(e.PublicKeys)) for i, key := range e.PublicKeys { publicKeys[i] = hexutil.Encode(key) } if len(e.CurrentEffectiveBalances) == 0 { e.CurrentEffectiveBalances = make([]uint64, 0) } if len(e.BalancesBeforeEpochTransition) == 0 { e.BalancesBeforeEpochTransition = make([]uint64, 0) } if len(e.BalancesAfterEpochTransition) == 0 { e.BalancesAfterEpochTransition = make([]uint64, 0) } if len(e.CorrectlyVotedSource) == 0 { e.CorrectlyVotedSource = make([]bool, 0) } if len(e.CorrectlyVotedTarget) == 0 { e.CorrectlyVotedTarget = make([]bool, 0) } if len(e.CorrectlyVotedHead) == 0 { e.CorrectlyVotedHead = make([]bool, 0) } if len(e.InactivityScores) == 0 { e.InactivityScores = make([]uint64, 0) } return &ValidatorPerformanceResponse{ CurrentEffectiveBalances: e.CurrentEffectiveBalances, InclusionSlots: inclusionSlots, InclusionDistances: inclusionDistances, CorrectlyVotedSource: e.CorrectlyVotedSource, CorrectlyVotedTarget: e.CorrectlyVotedTarget, CorrectlyVotedHead: e.CorrectlyVotedHead, BalancesBeforeEpochTransition: e.BalancesBeforeEpochTransition, BalancesAfterEpochTransition: e.BalancesAfterEpochTransition, MissingValidators: missingValidators, AverageActiveValidatorBalance: e.AverageActiveValidatorBalance, PublicKeys: publicKeys, InactivityScores: e.InactivityScores, } } type ValidatorBalancesResponse struct { Epoch uint64 `json:"epoch"` Balances []*ValidatorBalance `json:"balances"` NextPageToken string `json:"next_page_token"` TotalSize int32 `json:"total_size,omitempty"` } type ValidatorBalance struct { PublicKey string `json:"public_key"` Index uint64 `json:"index"` Balance uint64 `json:"balance"` Status string `json:"status"` } func ValidatorBalancesResponseFromConsensus(e *eth.ValidatorBalances) (*ValidatorBalancesResponse, error) { balances := make([]*ValidatorBalance, len(e.Balances)) for i, balance := range e.Balances { balances[i] = &ValidatorBalance{ PublicKey: hexutil.Encode(balance.PublicKey), Index: uint64(balance.Index), Balance: balance.Balance, Status: balance.Status, } } return &ValidatorBalancesResponse{ Epoch: uint64(e.Epoch), Balances: balances, NextPageToken: e.NextPageToken, TotalSize: e.TotalSize, }, nil } type ValidatorsResponse struct { Epoch uint64 `json:"epoch"` ValidatorList []*ValidatorContainer `json:"validator_list"` NextPageToken string `json:"next_page_token"` TotalSize int32 `json:"total_size"` } type ValidatorContainer struct { Index uint64 `json:"index"` Validator *Validator `json:"validator"` } type Validator struct { PublicKey string `json:"public_key,omitempty"` WithdrawalCredentials string `json:"withdrawal_credentials"` EffectiveBalance uint64 `json:"effective_balance"` Slashed bool `json:"slashed"` ActivationEligibilityEpoch uint64 `json:"activation_eligibility_epoch"` ActivationEpoch uint64 `json:"activation_epoch"` ExitEpoch uint64 `json:"exit_epoch"` WithdrawableEpoch uint64 `json:"withdrawable_epoch"` } func ValidatorsResponseFromConsensus(e *eth.Validators) (*ValidatorsResponse, error) { validatorList := make([]*ValidatorContainer, len(e.ValidatorList)) for i, validatorContainer := range e.ValidatorList { val := validatorContainer.Validator validatorList[i] = &ValidatorContainer{ Index: uint64(validatorContainer.Index), Validator: &Validator{ PublicKey: hexutil.Encode(val.PublicKey), WithdrawalCredentials: hexutil.Encode(val.WithdrawalCredentials), EffectiveBalance: val.EffectiveBalance, Slashed: val.Slashed, ActivationEligibilityEpoch: uint64(val.ActivationEligibilityEpoch), ActivationEpoch: uint64(val.ActivationEpoch), ExitEpoch: uint64(val.ExitEpoch), WithdrawableEpoch: uint64(val.WithdrawableEpoch), }, } } return &ValidatorsResponse{ Epoch: uint64(e.Epoch), ValidatorList: validatorList, NextPageToken: e.NextPageToken, TotalSize: e.TotalSize, }, nil } // ChainHead is the response for api endpoint /beacon/chainhead type ChainHead struct { HeadSlot string `json:"head_slot"` HeadEpoch string `json:"head_epoch"` HeadBlockRoot string `json:"head_block_root"` FinalizedSlot string `json:"finalized_slot"` FinalizedEpoch string `json:"finalized_epoch"` FinalizedBlockRoot string `json:"finalized_block_root"` JustifiedSlot string `json:"justified_slot"` JustifiedEpoch string `json:"justified_epoch"` JustifiedBlockRoot string `json:"justified_block_root"` PreviousJustifiedSlot string `json:"previous_justified_slot"` PreviousJustifiedEpoch string `json:"previous_justified_epoch"` PreviousJustifiedBlockRoot string `json:"previous_justified_block_root"` OptimisticStatus bool `json:"optimistic_status"` } func ChainHeadResponseFromConsensus(e *eth.ChainHead) *ChainHead { return &ChainHead{ HeadSlot: fmt.Sprintf("%d", e.HeadSlot), HeadEpoch: fmt.Sprintf("%d", e.HeadEpoch), HeadBlockRoot: hexutil.Encode(e.HeadBlockRoot), FinalizedSlot: fmt.Sprintf("%d", e.FinalizedSlot), FinalizedEpoch: fmt.Sprintf("%d", e.FinalizedEpoch), FinalizedBlockRoot: hexutil.Encode(e.FinalizedBlockRoot), JustifiedSlot: fmt.Sprintf("%d", e.JustifiedSlot), JustifiedEpoch: fmt.Sprintf("%d", e.JustifiedEpoch), JustifiedBlockRoot: hexutil.Encode(e.JustifiedBlockRoot), PreviousJustifiedSlot: fmt.Sprintf("%d", e.PreviousJustifiedSlot), PreviousJustifiedEpoch: fmt.Sprintf("%d", e.PreviousJustifiedEpoch), PreviousJustifiedBlockRoot: hexutil.Encode(e.PreviousJustifiedBlockRoot), OptimisticStatus: e.OptimisticStatus, } } func (m *ChainHead) ToConsensus() (*eth.ChainHead, error) { headSlot, err := strconv.ParseUint(m.HeadSlot, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "HeadSlot") } headEpoch, err := strconv.ParseUint(m.HeadEpoch, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "HeadEpoch") } headBlockRoot, err := bytesutil.DecodeHexWithLength(m.HeadBlockRoot, fieldparams.RootLength) if err != nil { return nil, server.NewDecodeError(err, "HeadBlockRoot") } finalizedSlot, err := strconv.ParseUint(m.FinalizedSlot, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "FinalizedSlot") } finalizedEpoch, err := strconv.ParseUint(m.FinalizedEpoch, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "FinalizedEpoch") } finalizedBlockRoot, err := bytesutil.DecodeHexWithLength(m.FinalizedBlockRoot, fieldparams.RootLength) if err != nil { return nil, server.NewDecodeError(err, "FinalizedBlockRoot") } justifiedSlot, err := strconv.ParseUint(m.JustifiedSlot, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "JustifiedSlot") } justifiedEpoch, err := strconv.ParseUint(m.JustifiedEpoch, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "JustifiedEpoch") } justifiedBlockRoot, err := bytesutil.DecodeHexWithLength(m.JustifiedBlockRoot, fieldparams.RootLength) if err != nil { return nil, server.NewDecodeError(err, "JustifiedBlockRoot") } previousjustifiedSlot, err := strconv.ParseUint(m.PreviousJustifiedSlot, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "PreviousJustifiedSlot") } previousjustifiedEpoch, err := strconv.ParseUint(m.PreviousJustifiedEpoch, 10, 64) if err != nil { return nil, server.NewDecodeError(err, "PreviousJustifiedEpoch") } previousjustifiedBlockRoot, err := bytesutil.DecodeHexWithLength(m.PreviousJustifiedBlockRoot, fieldparams.RootLength) if err != nil { return nil, server.NewDecodeError(err, "PreviousJustifiedBlockRoot") } return ð.ChainHead{ HeadSlot: primitives.Slot(headSlot), HeadEpoch: primitives.Epoch(headEpoch), HeadBlockRoot: headBlockRoot, FinalizedSlot: primitives.Slot(finalizedSlot), FinalizedEpoch: primitives.Epoch(finalizedEpoch), FinalizedBlockRoot: finalizedBlockRoot, JustifiedSlot: primitives.Slot(justifiedSlot), JustifiedEpoch: primitives.Epoch(justifiedEpoch), JustifiedBlockRoot: justifiedBlockRoot, PreviousJustifiedSlot: primitives.Slot(previousjustifiedSlot), PreviousJustifiedEpoch: primitives.Epoch(previousjustifiedEpoch), PreviousJustifiedBlockRoot: previousjustifiedBlockRoot, OptimisticStatus: m.OptimisticStatus, }, nil }