eigenpodproofs

package module
v0.0.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 3, 2024 License: MIT Imports: 33 Imported by: 0

README

WARNING: Please note that this repository is a work in progress and is currently unaudited. Use it with caution as it may contain unfinished features or bugs."

Introduction

This repository allows users to generate the proofs necessary to prove consensus layer state on EigenLayer, specifically the EigenPods system. These proofs are used for verifying 1) that withdrawal credentials of a validator are pointed to an EigenPod, 2) that changes in balance of a validator due to slashing, etc can be propagated to EigenLayer's smart contracts 3) Prove a validator's withdrawal on the consensus layer, which then allows a staker to withdraw their validator's ETH from their EigenPod. Specifically, these proofs are passed as inputs to the verifyWithdrawalCredentials(), verifyBalanceUpdates() and verifyAndProcessWithdrawals() functions, see here for the exact function interface definitions.

How to Retrieve Data

An important note is that this CLI is designed to be used with inputs that can be retrieved from a consensus layer full node, here is the relevant API specification. These are the api ednpoints that are required to retrieve the 3 consensus layer object required to generate proofs with this CLI:

Beacon State

This is the entire consensus layer state object at a given slot. The following endpoint returns this object:

/beacon/eth/v2/debug/beacon/states/[SLOT_NUMBER]
Beacon Block

This is the beacon block object. The following endpoint returns this object:

beacon/eth/v2/beacon/blocks/[SLOT_NUMBER]
Beacon Block Header

This is the block header for a beacon block. The following endpoint returns this object:

/beacon/eth/v1/beacon/headers/[SLOT_NUMBER]

How to Generate the Proofs with the Proof Generation library

This package allows you to generate withdrawal credential proofs, withdrawal proofs and balance update proofs. Please note that in order to run the sample commands, you must unzip the state files included in the data repo. To generate the proofs using this library, run the following commands:

Build the Executable

$ cd generation
$ go build
$ cd ..
Generate Validator Withdrawal Credential Proof

Here is the command:

$ ./generation/generation \
    -command ValidatorFieldsProof \
    -oracleBlockHeaderFile [ORACLE_BLOCK_HEADER_FILE_PATH] \
    -stateFile [STATE_FILE_PATH] \
    -validatorIndex [VALIDATOR_INDEX] \
    -outputFile [OUTPUT_FILE_PATH] \
    -chainID [CHAIN_ID]

Here is a breakdown of the inputs here:

  • “Command” aka the type of proof being generated
  • “oracleBlockHeaderFile” is the path to the oracle block header file, that we are proving all of this against
  • “stateFile” is the consensus state from that slot, containing the validator information
  • “validatorIndex” is the index of the validator being proven inside state.validators
  • “outputFile” - setting this will write the proofs to a json file
  • “chainID” this parameter allows certain constants to be set depending on whether the proof is being generated for a goerli or mainnet state.

Here is an example of running this command with the sample state/block files in the /data folder

./generation/generation \
  -command ValidatorFieldsProof \
  -oracleBlockHeaderFile "./data/deneb_goerli_block_header_7431952.json" \
  -stateFile "./data/deneb_goerli_slot_7431952.json" \
  -validatorIndex 302913 \
  -outputFile "withdrawal_credential_proof_302913.json" \
  -chainID 5
Generate Withdrawal Proof

Here is the command:

$ ./generation/generation \
  -command WithdrawalFieldsProof \
  -oracleBlockHeaderFile [ORACLE_BLOCK_HEADER_FILE_PATH] \
  -stateFile [STATE_FILE_PATH] \
  -validatorIndex [VALIDATOR_INDEX] \
  -outputFile [OUTPUT_FILE_PATH] \
  -chainID [CHAIN_ID] \
  -historicalSummariesIndex [HISTORICAL_SUMMARIES_INDEX] \
  -blockHeaderIndex [BLOCK_HEADER_INDEX] \
  -historicalSummaryStateFile [HISTORICAL_SUMMARY_STATE_FILE_PATH] \
  -blockHeaderFile [BLOCK_HEADER_FILE_PATH] \
  -blockBodyFile [BLOCK_BODY_FILE_PATH] \
  -withdrawalIndex [WITHDRAWAL_INDEX]

Here is an example of running this command with the sample state/block files in the /data folder

./generation/generation \
  -command WithdrawalFieldsProof \
  -oracleBlockHeaderFile ./data/deneb_goerli_block_header_7431952.json \
  -stateFile ./data/deneb_goerli_slot_7431952.json \
  -validatorIndex 627559 \
  -outputFile full_withdrawal_proof_627559.json \
  -chainID 5 \
  -historicalSummariesIndex 271 \
  -blockHeaderIndex 8191 \
  -historicalSummaryStateFile ./data/deneb_goerli_slot_7421952.json \
  -blockHeaderFile  data/deneb_goerli_block_header_7421951.json \
  -blockBodyFile data/deneb_goerli_block_7421951.json \
  -withdrawalIndex 0

Here is a breakdown of the inputs here:

  • “Command” aka the type of proof being generated

  • “oracleBlockHeaderFile” is the path to the oracle block header file, that we are proving all of this against

  • “stateFile” is the consensus state from that slot, containing the validator information

  • “validatorIndex” is the index of the validator being proven inside state.validators

  • “outputFile” - setting this will write the proofs to a json file

  • “chainID” this parameter allows certain constants to be set depending on whether the proof is being generated for a goerli or mainnet state.

  • "historicalSummariesIndex" Is the index in the historical summaries field of the oracle state (“stateFile”). You can calculate this like this:

    (withdrawal_slot - FIRST_CAPELLA_SLOT) // SLOTS_PER_HISTORICAL_ROOT
    

    where FIRST_CAPELLA_SLOT on Mainnet is 6209536, 5193728 on Goerli, 8192 on Holesky and SLOTS_PER_HISTORICAL_ROOT is 8192. Note that withdrawal_slot is the slot number of the block containing the withdrawal you want to prove.

  • "blockHeaderIndex" - this is the blockheaderRoot's index within the historical summaries entry, which can be calculated like this:

    withdrawal_slot mod SLOTS_PER_HISTORICAL_ROOT
    
  • "historicalSummaryStateFile" This is the beacon state at the slot such that: historical_summary_state.slot = SLOTS_PER_HISTORICAL_ROOT * ((withdrawal_slot // SLOTS_PER_HISTORICAL_ROOT) + 1).

  • blockHeaderFile - blockHeader from the withdrawal slot

  • blockBodyFile" Is the block body file from the withdrawal slot

  • withdrawalIndex Is the index of the withdrawal within the block (between 0 and 15)

Generate a Balance Update Proof.
$ ./generation/generation \
  - command BalanceUpdateProof \
  -oracleBlockHeaderFile [ORACLE_BLOCK_HEADER_FILE_PATH] \
  -stateFile [STATE_FILE_PATH] \
  -validatorIndex [VALIDATOR_INDEX] \
  -outputFile [OUTPUT_FILE_PATH] \
  -chainID [CHAIN_ID]

Here is a breakdown of the inputs here:

  • “Command” aka the type of proof being generated
  • “oracleBlockHeaderFile” is the path to the oracle block header file, that we are proving all of this against
  • “stateFile” is the consensus state from that slot, containing the validator information
  • “validatorIndex” is the index of the validator being proven inside state.validators
  • “outputFile” - setting this will write the proofs to a json file
  • “chainID” this parameter allows certain constants to be set depending on whether the proof is being generated for a goerli or mainnet state.

Here is an example of running this command with the sample state/block files in the /data folder:

./generation/generation \
  -command BalanceUpdateProof \
  -oracleBlockHeaderFile "./data/deneb_goerli_block_header_7431952.json" \
  -stateFile "./data/deneb_goerli_slot_7431952.json" \
  -validatorIndex 302913 \
  -outputFile "withdrawal_credential_proof_302913.json" \
  -chainID 5

Proof Generation Input Glossary

  • oracleBlockHeaderFile is the block header of the oracle block header root being used to make the proof
  • stateFile is the associated state file of the oracle block being used
  • validatorIndex is the index of the validator being proven for in the consensus layer
  • outputFile is the location where the generated proof will be written to
  • chainID is the chainID (either goerli = 5 or mainnet = 1) being generated for.
  • historicalSummariesIndex refer to What Are Historical Summary Proofs? secion. This is the index of the historical summary we're gonna use to prove the withdrawal
  • historicalSummaryStateFile state file corresponding to the state_summary_root stored in the historical summary we're gonna use.
  • blockHeaderIndex index of the block header that contains the withdrawal being proven
  • blockHeaderFile file containing the block header that contains the withdrawal being proven
  • blockBodyFile file containing the block body that contains the withdrawal being proven
  • withdrawalIndex index of the withdrawal being proven within the block (there are 16 withdrawals per block).

What Are Historical Summary Proofs?

Every block contains 16 withdrawals and any given beacon state stores the last 8192 block roots. Thus if a withdrawal was within the last 8192 blocks, we can prove any withdrawal against one of the block roots, and then prove that block root against the state root. However, what happens when we need to prove something from further in the past than 8192 blocks? That is where historical summaries come in. Every 8192 blocks, the state transition function takes a “snapshot” of the state.block_roots that are stored in the beacon state by taking the hash tree root of the state.block_roots, and adding that root to state.historical_summaries. Then, state.block_roots is cleared and the next 8192 block_roots will be added to state.block_roots. Thus, to prove an old withdrawal, we need to take the extra step of retrieving the state at the slot at which the snapshot that contains the root of the block when the withdrawal was included. Refer here for the beacon chain specs for historical summaries.

Documentation

Index

Constants

View Source
const (
	BEACON_STATE_ROOT_PREFIX            = "BEACON_STATE_ROOT_"
	BEACON_STATE_TOP_LEVEL_ROOTS_PREFIX = "BEACON_STATE_TOP_LEVEL_ROOTS_"
	VALIDATOR_TREE_PREFIX               = "VALIDATOR_TREE_"
	MAX_ORACLE_STATE_CACHE_SIZE         = 2000000
)
View Source
const FIRST_CAPELLA_SLOT_GOERLI = uint64(5193728)
View Source
const FIRST_CAPELLA_SLOT_HOLESKY = uint64(8192)
View Source
const FIRST_CAPELLA_SLOT_MAINNET = uint64(6209536)

Variables

View Source
var (
	FallbackGasTipCap       = big.NewInt(15000000000)
	ErrCannotGetECDSAPubKey = errors.New("ErrCannotGetECDSAPubKey")
	ErrTransactionFailed    = errors.New("ErrTransactionFailed")
)

Functions

func BigToLittleEndian

func BigToLittleEndian(input *big.Int) [32]byte

func ConvertBytesToStrings

func ConvertBytesToStrings(b [][32]byte) []string

func ConvertTo32ByteArray

func ConvertTo32ByteArray(b []byte) [32]byte

func ConvertUint64ToRoot

func ConvertUint64ToRoot(n uint64) phase0.Root

func ExtractBlockCapella

func ExtractBlockCapella(blockHeaderFile string) (capella.BeaconBlock, error)

func ExtractBlockDeneb

func ExtractBlockDeneb(blockHeaderFile string) (deneb.BeaconBlock, error)

func ExtractBlockHeader

func ExtractBlockHeader(blockHeaderFile string) (phase0.BeaconBlockHeader, error)

func GetDepth

func GetDepth(d uint64) uint8

func GetSlotTimestamp

func GetSlotTimestamp(beaconState *spec.VersionedBeaconState, blockHeader *phase0.BeaconBlockHeader) (uint64, error)

func GetValidatorFields

func GetValidatorFields(v *phase0.Validator) []string

func GetWithdrawalFields

func GetWithdrawalFields(w *capella.Withdrawal) []string

func GetWithdrawalIndex

func GetWithdrawalIndex(validatorIndex uint64, withdrawals []*capella.Withdrawal) uint64

func IsProvableWithdrawal

func IsProvableWithdrawal(latestOracleBeaconSlot, withdrawalSlot uint64) bool

func NextPowerOfTwo

func NextPowerOfTwo(v uint64) uint

func ParseCapellaBeaconStateFromJSON

func ParseCapellaBeaconStateFromJSON(data beaconStateJSONCapella, s *capella.BeaconState) error

func ParseCapellaStateJSONFile

func ParseCapellaStateJSONFile(filePath string) (*beaconStateJSONCapella, error)

func ParseDenebBeaconStateFromJSON

func ParseDenebBeaconStateFromJSON(data beaconStateJSONDeneb, s *deneb.BeaconState) error

nolint:gocyclo

func ParseDenebStateJSONFile

func ParseDenebStateJSONFile(filePath string) (*beaconStateJSONDeneb, error)

func ParseJSONFileCapella

func ParseJSONFileCapella(filePath string) (*beaconStateJSONCapella, error)

func ParseJSONFileDeneb

func ParseJSONFileDeneb(filePath string) (*beaconStateJSONDeneb, error)

Types

type BalanceUpdateProofs

type BalanceUpdateProofs struct {
	ValidatorIndex                         uint64   `json:"validatorIndex"`
	BeaconStateRoot                        string   `json:"beaconStateRoot"`
	LatestBlockHeaderRoot                  string   `json:"latestBlockHeaderRoot"`
	StateRootAgainstLatestBlockHeaderProof []string `json:"StateRootAgainstLatestBlockHeaderProof"`
	ValidatorFieldsProof                   []string `json:"WithdrawalCredentialProof"`
	ValidatorFields                        []string `json:"ValidatorFields"`
}

type Bytes32

type Bytes32 [32]byte

func ConvertUint64ToBytes32

func ConvertUint64ToBytes32(n uint64) Bytes32

func ConvertValidatorToValidatorFields

func ConvertValidatorToValidatorFields(v *phase0.Validator) []Bytes32

func ConvertWithdrawalToWithdrawalFields

func ConvertWithdrawalToWithdrawalFields(w *capella.Withdrawal) []Bytes32

func (*Bytes32) MarshalJSON

func (p *Bytes32) MarshalJSON() ([]byte, error)

func (*Bytes32) UnmarshalJSON

func (p *Bytes32) UnmarshalJSON(data []byte) error

type ChainClient

type ChainClient struct {
	*ethclient.Client

	AccountAddress     common.Address
	NoSendTransactOpts *bind.TransactOpts
	Contracts          map[common.Address]*bind.BoundContract
	// contains filtered or unexported fields
}

func NewChainClient

func NewChainClient(ethClient *ethclient.Client, privateKeyString string) (*ChainClient, error)

func (*ChainClient) EnsureTransactionEvaled

func (c *ChainClient) EnsureTransactionEvaled(tx *types.Transaction, tag string) (*types.Receipt, error)

func (*ChainClient) EstimateGasPriceAndLimitAndSendTx

func (c *ChainClient) EstimateGasPriceAndLimitAndSendTx(
	ctx context.Context,
	tx *types.Transaction,
	tag string,
) (*types.Receipt, error)

EstimateGasPriceAndLimitAndSendTx sends and returns an otherwise identical txn to the one provided but with updated gas prices sampled from the existing network conditions and an accurate gasLimit

Note: tx must be a to a contract, not an EOA

Slightly modified from: https://github.com/ethereum-optimism/optimism/blob/ec266098641820c50c39c31048aa4e953bece464/batch-submitter/drivers/sequencer/driver.go#L314

func (*ChainClient) GetAccountAddress

func (c *ChainClient) GetAccountAddress() common.Address

func (*ChainClient) GetCurrentBlockNumber

func (c *ChainClient) GetCurrentBlockNumber(ctx context.Context) (uint32, error)

func (*ChainClient) GetNoSendTransactOpts

func (c *ChainClient) GetNoSendTransactOpts() *bind.TransactOpts

type EigenPodProofs

type EigenPodProofs struct {
	// contains filtered or unexported fields
}

func NewEigenPodProofs

func NewEigenPodProofs(chainID uint64, oracleStateCacheExpirySeconds int) (*EigenPodProofs, error)

func (*EigenPodProofs) ComputeBeaconStateRoot

func (epp *EigenPodProofs) ComputeBeaconStateRoot(beaconState *deneb.BeaconState) (phase0.Root, error)

func (*EigenPodProofs) ComputeBeaconStateTopLevelRoots

func (epp *EigenPodProofs) ComputeBeaconStateTopLevelRoots(beaconState *spec.VersionedBeaconState) (*beacon.BeaconStateTopLevelRoots, error)

func (*EigenPodProofs) ComputeValidatorTree

func (epp *EigenPodProofs) ComputeValidatorTree(slot phase0.Slot, validators []*phase0.Validator) ([][]phase0.Root, error)

func (*EigenPodProofs) ComputeVersionedBeaconStateTopLevelRoots

func (epp *EigenPodProofs) ComputeVersionedBeaconStateTopLevelRoots(beaconState *spec.VersionedBeaconState) (*beacon.BeaconStateTopLevelRoots, error)

func (*EigenPodProofs) GetWithdrawalProofParams

func (epp *EigenPodProofs) GetWithdrawalProofParams(latestOracleBeaconSlot, withdrawalSlot uint64) (uint64, error)

func (*EigenPodProofs) ProveValidatorAgainstBeaconState

func (epp *EigenPodProofs) ProveValidatorAgainstBeaconState(beaconStateTopLevelRoots *beacon.BeaconStateTopLevelRoots, oracleBeaconStateSlot phase0.Slot, oracleBeaconStateValidators []*phase0.Validator, validatorIndex uint64) (common.Proof, error)

func (*EigenPodProofs) ProveValidatorAgainstValidatorList

func (epp *EigenPodProofs) ProveValidatorAgainstValidatorList(slot phase0.Slot, validators []*phase0.Validator, validatorIndex uint64) (common.Proof, error)

func (*EigenPodProofs) ProveValidatorContainers

func (epp *EigenPodProofs) ProveValidatorContainers(oracleBlockHeader *phase0.BeaconBlockHeader, oracleBeaconState *spec.VersionedBeaconState, validatorIndices []uint64) (*VerifyValidatorFieldsCallParams, error)

func (*EigenPodProofs) ProveWithdrawal

func (epp *EigenPodProofs) ProveWithdrawal(
	oracleBlockHeader *phase0.BeaconBlockHeader,
	oracleBeaconState *spec.VersionedBeaconState,
	oracleBeaconStateTopLevelRoots *beacon.BeaconStateTopLevelRoots,
	historicalSummaryStateBlockRoots []phase0.Root,
	withdrawalBlock *spec.VersionedSignedBeaconBlock,
	validatorIndex uint64,
) (*WithdrawalProof, []Bytes32, error)

ProveWithdrawal generates the proofs required to prove a withdrawal oracleBlockHeader: the root of this is provided by the oracle, we prove the state root against this oracleBeaconState: the state of the block header provided by the oracle historicalSummaryState: the state whose slot at which historicalSummaryState.block_roots was hashed and added to historical_summaries withdrawalBlock: the block containing the withdrawal validatorIndex: the index of the validator that the withdrawal happened for

func (*EigenPodProofs) ProveWithdrawals

func (epp *EigenPodProofs) ProveWithdrawals(
	oracleBlockHeader *phase0.BeaconBlockHeader,
	oracleBeaconState *spec.VersionedBeaconState,
	historicalSummaryStateBlockRoots [][]phase0.Root,
	withdrawalBlocks []*spec.VersionedSignedBeaconBlock,
	validatorIndices []uint64,
) (*VerifyAndProcessWithdrawalCallParams, error)

type InputDataBlockCapella

type InputDataBlockCapella struct {
	Version string `json:"version"`
	Data    struct {
		Message   capella.BeaconBlock `json:"message"`
		Signature string              `json:"signature"`
	} `json:"data"`
	Execution_optimistic bool `json:"execution_optimistic"`
	Finalized            bool `json:"finalized"`
}

type InputDataBlockDeneb

type InputDataBlockDeneb struct {
	Version string `json:"version"`
	Data    struct {
		Message   deneb.BeaconBlock `json:"message"`
		Signature string            `json:"signature"`
	} `json:"data"`
	Execution_optimistic bool `json:"execution_optimistic"`
	Finalized            bool `json:"finalized"`
}

type InputDataBlockHeader

type InputDataBlockHeader struct {
	Data struct {
		Header struct {
			Message phase0.BeaconBlockHeader `json:"message"`
		} `json:"header"`
	} `json:"data"`
}

type StateRootProof

type StateRootProof struct {
	BeaconStateRoot phase0.Root  `json:"beaconStateRoot"`
	StateRootProof  common.Proof `json:"stateRootProof"`
	Slot            phase0.Slot  `json:"slot"`
	SlotRootProof   common.Proof `json:"slotRootProof"` //Note:  this slot root is oracle block root being used to prove partial withdrawals is after the specified range of blocks requested by the user
}

type VerifyAndProcessWithdrawalCallParams

type VerifyAndProcessWithdrawalCallParams struct {
	OracleTimestamp       uint64             `json:"oracleTimestamp"`
	StateRootProof        *StateRootProof    `json:"stateRootProof"`
	WithdrawalProofs      []*WithdrawalProof `json:"withdrawalProofs"`
	ValidatorFieldsProofs []common.Proof     `json:"validatorFieldsProofs"`
	ValidatorFields       [][]Bytes32        `json:"validatorFields"`
	WithdrawalFields      [][]Bytes32        `json:"withdrawalFields"`
}

type VerifyBalanceUpdatesCallParams

type VerifyBalanceUpdatesCallParams = VerifyValidatorFieldsCallParams

type VerifyValidatorFieldsCallParams

type VerifyValidatorFieldsCallParams struct {
	OracleTimestamp       uint64          `json:"oracleTimestamp"`
	StateRootProof        *StateRootProof `json:"stateRootProof"`
	ValidatorIndices      []uint64        `json:"validatorIndices"`
	ValidatorFieldsProofs []common.Proof  `json:"validatorFieldsProofs"`
	ValidatorFields       [][]Bytes32     `json:"validatorFields"`
}

type VerifyWithdrawalCredentialsCallParams

type VerifyWithdrawalCredentialsCallParams = VerifyValidatorFieldsCallParams

We define these two aliases here, since calls to verifyWithdrawalCredentials and verifyBalanceUpdates use the same inputs

type WithdrawalCredentialProofs

type WithdrawalCredentialProofs struct {
	StateRootAgainstLatestBlockHeaderProof []string `json:"StateRootAgainstLatestBlockHeaderProof"`
	BeaconStateRoot                        string   `json:"beaconStateRoot"`
	ValidatorIndex                         uint64   `json:"validatorIndex"`
	WithdrawalCredentialProof              []string `json:"WithdrawalCredentialProof"`
	ValidatorFields                        []string `json:"ValidatorFields"`
}

type WithdrawalProof

type WithdrawalProof struct {
	WithdrawalProof                 common.Proof `json:"withdrawalProof"`
	SlotProof                       common.Proof `json:"slotProof"`
	ExecutionPayloadProof           common.Proof `json:"executionPayloadProof"`
	TimestampProof                  common.Proof `json:"timestampProof"`
	HistoricalSummaryBlockRootProof common.Proof `json:"historicalSummaryBlockRootProof"`
	BlockRootIndex                  uint64       `json:"blockRootIndex"`
	HistoricalSummaryIndex          uint64       `json:"historicalSummaryIndex"`
	WithdrawalIndex                 uint64       `json:"withdrawalIndex"`
	BlockRoot                       phase0.Root  `json:"blockRoot"`
	SlotRoot                        phase0.Root  `json:"slotRoot"`
	TimestampRoot                   phase0.Root  `json:"timestampRoot"`
	ExecutionPayloadRoot            phase0.Root  `json:"executionPayloadRoot"`
}

type WithdrawalProofs

type WithdrawalProofs struct {
	StateRootAgainstLatestBlockHeaderProof []string `json:"StateRootAgainstLatestBlockHeaderProof"`
	SlotAgainstLatestBlockHeaderProof      []string `json:"SlotAgainstLatestBlockHeaderProof"`
	BeaconStateRoot                        string   `json:"beaconStateRoot"`
	WithdrawalProof                        []string `json:"WithdrawalProof"`
	SlotProof                              []string `json:"SlotProof"`
	ExecutionPayloadProof                  []string `json:"ExecutionPayloadProof"`
	TimestampProof                         []string `json:"TimestampProof"`
	HistoricalSummaryProof                 []string `json:"HistoricalSummaryProof"`
	BlockHeaderRootIndex                   uint64   `json:"blockHeaderRootIndex"`
	HistoricalSummaryIndex                 uint64   `json:"historicalSummaryIndex"`
	WithdrawalIndex                        uint64   `json:"withdrawalIndex"`
	BlockHeaderRoot                        string   `json:"blockHeaderRoot"`
	SlotRoot                               string   `json:"slotRoot"`
	TimestampRoot                          string   `json:"timestampRoot"`
	ExecutionPayloadRoot                   string   `json:"executionPayloadRoot"`
	ValidatorProof                         []string `json:"ValidatorProof"`
	ValidatorFields                        []string `json:"ValidatorFields"`
	WithdrawalFields                       []string `json:"WithdrawalFields"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL