working debug/state/ for early states (#8767)

Added jsonification to beacon state
This commit is contained in:
Giulio rebuffo 2023-11-18 03:08:19 +01:00 committed by GitHub
parent de60e03f03
commit b72b5b95c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 321 additions and 23 deletions

View File

@ -64,7 +64,9 @@ func beaconHandlerWrapper(fn beaconHandlerFn, supportSSZ bool) func(w http.Respo
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(httpStatus)
json.NewEncoder(w).Encode(resp)
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Warn("[Beacon API] failed", "method", r.Method, "err", err, "ssz", isSSZ)
}
}
}

View File

@ -95,8 +95,13 @@ func (a *ApiHandler) init() {
})
})
r.Route("/v2", func(r chi.Router) {
r.Route("/debug", func(r chi.Router) {
r.Route("/beacon", func(r chi.Router) {
r.Get("/states/{state_id}", beaconHandlerWrapper(a.getFullState, true))
})
})
r.Route("/beacon", func(r chi.Router) {
r.Post("/blocks/{slot}", nil) //otterscan
r.Get("/blocks/{slot}", nil) //otterscan
})
r.Route("/validator", func(r chi.Router) {
r.Post("/blocks/{slot}", nil)

View File

@ -52,7 +52,7 @@ func (a *ApiHandler) rootFromStateId(ctx context.Context, tx kv.Tx, stateId *seg
return libcommon.Hash{}, http.StatusInternalServerError, err
}
if root == (libcommon.Hash{}) {
return libcommon.Hash{}, http.StatusNotFound, fmt.Errorf("block not found %d", *stateId.getSlot())
return libcommon.Hash{}, http.StatusNotFound, fmt.Errorf("block not found")
}
return
}
@ -168,3 +168,47 @@ func (a *ApiHandler) getStateRoot(r *http.Request) (data any, finalized *bool, v
httpStatus = http.StatusAccepted
return
}
func (a *ApiHandler) getFullState(r *http.Request) (data any, finalized *bool, version *clparams.StateVersion, httpStatus int, err error) {
var (
tx kv.Tx
blockId *segmentID
root libcommon.Hash
)
ctx := r.Context()
tx, err = a.indiciesDB.BeginRo(ctx)
if err != nil {
httpStatus = http.StatusInternalServerError
return
}
defer tx.Rollback()
blockId, err = stateIdFromRequest(r)
if err != nil {
httpStatus = http.StatusBadRequest
return
}
root, httpStatus, err = a.rootFromStateId(ctx, tx, blockId)
if err != nil {
return
}
blockRoot, err := beacon_indicies.ReadBlockRootByStateRoot(tx, root)
if err != nil {
httpStatus = http.StatusInternalServerError
return
}
data, err = a.forkchoiceStore.GetFullState(blockRoot)
if err != nil {
httpStatus = http.StatusBadRequest
return
}
finalized = new(bool)
*finalized = false
httpStatus = http.StatusAccepted
return
}

View File

@ -9,8 +9,8 @@ import (
)
type HistoricalSummary struct {
BlockSummaryRoot libcommon.Hash
StateSummaryRoot libcommon.Hash
BlockSummaryRoot libcommon.Hash `json:"block_summary_root"`
StateSummaryRoot libcommon.Hash `json:"state_summary_root"`
}
func (h *HistoricalSummary) EncodeSSZ(buf []byte) ([]byte, error) {

View File

@ -1,6 +1,9 @@
package cltypes
import (
"encoding/json"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon-lib/types/clonable"
"github.com/ledgerwatch/erigon/cl/utils"
)
@ -62,3 +65,19 @@ func (j JustificationBits) CheckRange(start int, end int) bool {
func (j JustificationBits) Copy() JustificationBits {
return JustificationBits{j[0], j[1], j[2], j[3]}
}
func (j JustificationBits) MarshalJSON() ([]byte, error) {
enc, err := j.EncodeSSZ(nil)
if err != nil {
return nil, err
}
return json.Marshal(hexutility.Bytes(enc))
}
func (j *JustificationBits) UnmarshalJSON(input []byte) error {
var hex hexutility.Bytes
if err := json.Unmarshal(input, &hex); err != nil {
return err
}
return j.DecodeSSZ(hex, 0)
}

View File

@ -1,9 +1,10 @@
package solid
import (
"github.com/ledgerwatch/erigon-lib/common"
"encoding/json"
"math/bits"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon-lib/types/clonable"
"github.com/ledgerwatch/erigon/cl/merkle_tree"
"github.com/ledgerwatch/erigon/cl/utils"
@ -151,7 +152,8 @@ func (u *BitList) EncodeSSZ(dst []byte) ([]byte, error) {
// DecodeSSZ replaces the underlying byte slice of the BitList with a copy of the input byte slice.
// It then updates the length of the BitList to match the length of the new byte slice.
func (u *BitList) DecodeSSZ(dst []byte, _ int) error {
u.u = common.CopyBytes(dst)
u.u = make([]byte, len(dst))
copy(u.u, dst)
u.l = len(dst)
return nil
}
@ -187,3 +189,19 @@ func (u *BitList) Bits() int {
// bit. Subtract this value by 1 to determine the length of the bitlist.
return 8*(u.l-1) + msb - 1
}
func (u *BitList) MarshalJSON() ([]byte, error) {
enc, err := u.EncodeSSZ(nil)
if err != nil {
return nil, err
}
return json.Marshal(hexutility.Bytes(enc))
}
func (u *BitList) UnmarshalJSON(input []byte) error {
var hex hexutility.Bytes
if err := json.Unmarshal(input, &hex); err != nil {
return err
}
return u.DecodeSSZ(hex, 0)
}

View File

@ -2,7 +2,10 @@ package solid
import (
"encoding/binary"
"encoding/json"
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon-lib/types/clonable"
"github.com/ledgerwatch/erigon-lib/types/ssz"
@ -107,3 +110,35 @@ func (a *PendingAttestation) HashSSZ() (o [32]byte, err error) {
func (*PendingAttestation) Clone() clonable.Clonable {
return &PendingAttestation{}
}
func (a *PendingAttestation) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
AggregationBits hexutility.Bytes `json:"aggregation_bits"`
AttestationData AttestationData `json:"attestation_data"`
InclusionDelay uint64 `json:"inclusion_delay"`
ProposerIndex uint64 `json:"proposer_index"`
}{
AggregationBits: a.AggregationBits(),
AttestationData: a.AttestantionData(),
InclusionDelay: a.InclusionDelay(),
ProposerIndex: a.ProposerIndex(),
})
}
func (a *PendingAttestation) UnmarshalJSON(input []byte) error {
var err error
var tmp struct {
AggregationBits hexutility.Bytes `json:"aggregation_bits"`
AttestationData AttestationData `json:"attestation_data"`
InclusionDelay uint64 `json:"inclusion_delay"`
ProposerIndex uint64 `json:"proposer_index"`
}
if err = json.Unmarshal(input, &tmp); err != nil {
return err
}
a.SetAggregationBits(tmp.AggregationBits)
a.SetAttestationData(tmp.AttestationData)
a.SetInclusionDelay(tmp.InclusionDelay)
a.SetProposerIndex(tmp.ProposerIndex)
return nil
}

View File

@ -1,6 +1,9 @@
package solid
import (
"encoding/json"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/types/clonable"
"github.com/ledgerwatch/erigon-lib/types/ssz"
"github.com/ledgerwatch/erigon/cl/merkle_tree"
@ -12,8 +15,8 @@ const syncCommitteeSize = 48 * 513
type SyncCommittee [syncCommitteeSize]byte
func NewSyncCommitteeFromParameters(
committee [][48]byte,
aggregatePublicKey [48]byte,
committee []libcommon.Bytes48,
aggregatePublicKey libcommon.Bytes48,
) *SyncCommittee {
s := &SyncCommittee{}
s.SetAggregatePublicKey(aggregatePublicKey)
@ -21,26 +24,26 @@ func NewSyncCommitteeFromParameters(
return s
}
func (s *SyncCommittee) GetCommittee() [][48]byte {
committee := make([][48]byte, 512)
func (s *SyncCommittee) GetCommittee() []libcommon.Bytes48 {
committee := make([]libcommon.Bytes48, 512)
for i := range committee {
copy(committee[i][:], s[i*48:])
}
return committee
}
func (s *SyncCommittee) AggregatePublicKey() (out [48]byte) {
func (s *SyncCommittee) AggregatePublicKey() (out libcommon.Bytes48) {
copy(out[:], s[syncCommitteeSize-48:])
return
}
func (s *SyncCommittee) SetCommittee(committee [][48]byte) {
func (s *SyncCommittee) SetCommittee(committee []libcommon.Bytes48) {
for i := range committee {
copy(s[i*48:], committee[i][:])
}
}
func (s *SyncCommittee) SetAggregatePublicKey(k [48]byte) {
func (s *SyncCommittee) SetAggregatePublicKey(k libcommon.Bytes48) {
copy(s[syncCommitteeSize-48:], k[:])
}
@ -89,3 +92,27 @@ func (s *SyncCommittee) HashSSZ() ([32]byte, error) {
func (s *SyncCommittee) Static() bool {
return true
}
func (s *SyncCommittee) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Committee []libcommon.Bytes48 `json:"committee"`
AggregatePublicKey libcommon.Bytes48 `json:"aggregate_public_key"`
}{
Committee: s.GetCommittee(),
AggregatePublicKey: s.AggregatePublicKey(),
})
}
func (s *SyncCommittee) UnmarshalJSON(input []byte) error {
var err error
var tmp struct {
Committee []libcommon.Bytes48 `json:"committee"`
AggregatePublicKey libcommon.Bytes48 `json:"aggregate_public_key"`
}
if err = json.Unmarshal(input, &tmp); err != nil {
return err
}
s.SetAggregatePublicKey(tmp.AggregatePublicKey)
s.SetCommittee(tmp.Committee)
return nil
}

View File

@ -10,7 +10,7 @@ import (
func TestSyncCommittee(t *testing.T) {
// Test NewSyncCommitteeFromParameters
committee := make([][48]byte, 512)
committee := make([]libcommon.Bytes48, 512)
aggregatePublicKey := [48]byte{1, 2, 3} // Example aggregate public key
syncCommittee := NewSyncCommitteeFromParameters(committee, aggregatePublicKey)
assert.NotNil(t, syncCommittee)
@ -20,7 +20,7 @@ func TestSyncCommittee(t *testing.T) {
assert.Equal(t, committee, gotCommittee)
// Test SetCommittee
newCommittee := make([][48]byte, 512)
newCommittee := make([]libcommon.Bytes48, 512)
for i := 0; i < 512; i++ {
copy(newCommittee[i][:], []byte{byte(i)})
}
@ -30,13 +30,13 @@ func TestSyncCommittee(t *testing.T) {
// Test AggregatePublicKey
gotAggregatePublicKey := syncCommittee.AggregatePublicKey()
assert.Equal(t, aggregatePublicKey, gotAggregatePublicKey)
assert.Equal(t, libcommon.Bytes48(aggregatePublicKey), gotAggregatePublicKey)
// Test SetAggregatePublicKey
newAggregatePublicKey := [48]byte{4, 5, 6} // Example new aggregate public key
syncCommittee.SetAggregatePublicKey(newAggregatePublicKey)
updatedAggregatePublicKey := syncCommittee.AggregatePublicKey()
assert.Equal(t, newAggregatePublicKey, updatedAggregatePublicKey)
assert.Equal(t, libcommon.Bytes48(newAggregatePublicKey), updatedAggregatePublicKey)
// Test EncodingSizeSSZ
expectedEncodingSize := syncCommitteeSize
@ -73,3 +73,19 @@ func TestSyncCommittee(t *testing.T) {
// Test Static
assert.True(t, syncCommittee.Static())
}
func TestSyncCommitteeJson(t *testing.T) {
// Test MarshalJSON and UnmarshalJSON
committee := make([]libcommon.Bytes48, 512)
for i := 0; i < 512; i++ {
copy(committee[i][:], []byte{byte(i)})
}
aggregatePublicKey := [48]byte{1, 2, 3} // Example aggregate public key
syncCommittee := NewSyncCommitteeFromParameters(committee, aggregatePublicKey)
encodedData, err := syncCommittee.MarshalJSON()
assert.NoError(t, err)
decodedSyncCommittee := &SyncCommittee{}
err = decodedSyncCommittee.UnmarshalJSON(encodedData)
assert.NoError(t, err)
assert.Equal(t, syncCommittee, decodedSyncCommittee)
}

View File

@ -2,6 +2,7 @@ package solid
import (
"encoding/binary"
"encoding/json"
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/length"
@ -184,3 +185,44 @@ func (v Validator) Active(epoch uint64) bool {
func (v Validator) IsSlashable(epoch uint64) bool {
return !v.Slashed() && (v.ActivationEpoch() <= epoch) && (epoch < v.WithdrawableEpoch())
}
func (v Validator) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
PublicKey common.Bytes48 `json:"public_key"`
WithdrawalCredentials common.Hash `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"`
}{
PublicKey: v.PublicKey(),
WithdrawalCredentials: v.WithdrawalCredentials(),
EffectiveBalance: v.EffectiveBalance(),
Slashed: v.Slashed(),
ActivationEligibilityEpoch: v.ActivationEligibilityEpoch(),
ActivationEpoch: v.ActivationEpoch(),
ExitEpoch: v.ExitEpoch(),
WithdrawableEpoch: v.WithdrawableEpoch(),
})
}
func (v *Validator) UnmarshalJSON(input []byte) error {
var err error
var tmp struct {
PublicKey common.Bytes48 `json:"public_key"`
WithdrawalCredentials common.Hash `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"`
}
if err = json.Unmarshal(input, &tmp); err != nil {
return err
}
*v = NewValidatorFromParameters(tmp.PublicKey, tmp.WithdrawalCredentials, tmp.EffectiveBalance, tmp.Slashed, tmp.ActivationEligibilityEpoch, tmp.ActivationEpoch, tmp.ExitEpoch, tmp.WithdrawableEpoch)
return nil
}

View File

@ -2,6 +2,7 @@ package solid
import (
"bytes"
"encoding/json"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/length"
@ -374,3 +375,23 @@ func (v *ValidatorSet) SetValidatorSlashed(index int, slashed bool) {
v.zeroTreeHash(index)
v.Get(index).SetSlashed(slashed)
}
func (v *ValidatorSet) MarshalJSON() ([]byte, error) {
validators := make([]Validator, v.l)
for i := 0; i < v.l; i++ {
validators[i] = v.Get(i)
}
return json.Marshal(validators)
}
func (v *ValidatorSet) UnmarshalJSON(data []byte) error {
var validators []Validator
if err := json.Unmarshal(data, &validators); err != nil {
return err
}
v.Clear()
for _, val := range validators {
v.Append(val)
}
return nil
}

View File

@ -75,3 +75,15 @@ func TestValidatorSetTest(t *testing.T) {
require.Equal(t, firstHash, secondHash)
}
func TestMarshalUnmarshalJson(t *testing.T) {
validator := NewValidatorFromParameters(
[48]byte{1, 2, 3}, [32]byte{4, 5, 6}, 7, true, 8, 9, 10, 11,
)
encoded, err := validator.MarshalJSON()
require.NoError(t, err)
decoded := NewValidator()
err = decoded.UnmarshalJSON(encoded)
require.NoError(t, err)
assert.Equal(t, validator, decoded)
}

View File

@ -75,7 +75,7 @@ func ReadBlockRootByStateRoot(tx kv.Tx, stateRoot libcommon.Hash) (libcommon.Has
copy(blockRoot[:], bRoot)
return stateRoot, nil
return blockRoot, nil
}
func ReadCanonicalBlockRoot(tx kv.Tx, slot uint64) (libcommon.Hash, error) {

View File

@ -10,6 +10,7 @@ import (
"github.com/ledgerwatch/erigon/cl/phase1/core/state/shuffling"
"github.com/Giulio2002/bls"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/utils"
@ -195,7 +196,7 @@ func (b *CachingBeaconState) ComputeNextSyncCommittee() (*solid.SyncCommittee, e
mix := b.GetRandaoMix(int(mixPosition))
seed := shuffling.GetSeed(b.BeaconConfig(), mix, epoch, beaconConfig.DomainSyncCommittee)
i := uint64(0)
syncCommitteePubKeys := make([][48]byte, 0, cltypes.SyncCommitteeSize)
syncCommitteePubKeys := make([]libcommon.Bytes48, 0, cltypes.SyncCommitteeSize)
preInputs := shuffling.ComputeShuffledIndexPreInputs(b.BeaconConfig(), seed)
for len(syncCommitteePubKeys) < cltypes.SyncCommitteeSize {
shuffledIndex, err := shuffling.ComputeShuffledIndex(
@ -235,7 +236,7 @@ func (b *CachingBeaconState) ComputeNextSyncCommittee() (*solid.SyncCommittee, e
if err != nil {
return nil, err
}
var aggregate [48]byte
var aggregate libcommon.Bytes48
copy(aggregate[:], aggregatePublicKeyBytes)
return solid.NewSyncCommitteeFromParameters(syncCommitteePubKeys, aggregate), nil

View File

@ -1,6 +1,8 @@
package raw
import (
"encoding/json"
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
@ -99,3 +101,48 @@ func (b *BeaconState) init() error {
}
return nil
}
func (b *BeaconState) MarshalJSON() ([]byte, error) {
obj := map[string]interface{}{
"genesis_time": b.genesisTime,
"genesis_validators_root": b.genesisValidatorsRoot,
"slot": b.slot,
"fork": b.fork,
"latest_block_header": b.latestBlockHeader,
"block_roots": b.blockRoots,
"state_roots": b.stateRoots,
"historical_roots": b.historicalRoots,
"eth1_data": b.eth1Data,
"eth1_data_votes": b.eth1DataVotes,
"eth1_deposit_index": b.eth1DepositIndex,
"validators": b.validators,
"balances": b.balances,
"randao_mixes": b.randaoMixes,
"slashings": b.slashings,
"previous_epoch_participation": b.previousEpochParticipation,
"current_epoch_participation": b.currentEpochParticipation,
"justification_bits": b.justificationBits,
"previous_justified_checkpoint": b.previousJustifiedCheckpoint,
"current_justified_checkpoint": b.currentJustifiedCheckpoint,
"finalized_checkpoint": b.finalizedCheckpoint,
}
if b.version == clparams.Phase0Version {
obj["previous_epoch_attestations"] = b.previousEpochAttestations
obj["current_epoch_attestations"] = b.currentEpochAttestations
}
if b.version >= clparams.AltairVersion {
obj["inactivity_scores"] = b.inactivityScores
obj["current_sync_committee"] = b.currentSyncCommittee
obj["next_sync_committee"] = b.nextSyncCommittee
}
if b.version >= clparams.BellatrixVersion {
obj["latest_execution_payload_header"] = b.latestExecutionPayloadHeader
}
if b.version >= clparams.CapellaVersion {
obj["next_withdrawal_index"] = b.nextWithdrawalIndex
obj["next_withdrawal_validator_index"] = b.nextWithdrawalValidatorIndex
obj["historical_summaries"] = b.historicalSummaries
}
return json.Marshal(obj)
}

View File

@ -208,3 +208,9 @@ func (f *ForkChoiceStore) AnchorSlot() uint64 {
defer f.mu.Unlock()
return f.forkGraph.AnchorSlot()
}
func (f *ForkChoiceStore) GetFullState(blockRoot libcommon.Hash) (*state2.CachingBeaconState, error) {
f.mu.Lock()
defer f.mu.Unlock()
return f.forkGraph.GetState(blockRoot, true)
}

View File

@ -2,8 +2,10 @@ package forkchoice
import (
"github.com/ledgerwatch/erigon-lib/common"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
state2 "github.com/ledgerwatch/erigon/cl/phase1/core/state"
"github.com/ledgerwatch/erigon/cl/phase1/execution_client"
)
@ -23,6 +25,7 @@ type ForkChoiceStorageReader interface {
HighestSeen() uint64
JustifiedCheckpoint() solid.Checkpoint
ProposerBoostRoot() common.Hash
GetFullState(blockRoot libcommon.Hash) (*state2.CachingBeaconState, error)
Slot() uint64
Time() uint64
}

View File

@ -38,12 +38,12 @@ var (
BeaconApiReadTimeout = cli.Uint64Flag{
Name: "beacon.api.read.timeout",
Usage: "Sets the seconds for a read time out in the beacon api",
Value: 5,
Value: 60,
}
BeaconApiWriteTimeout = cli.Uint64Flag{
Name: "beacon.api.write.timeout",
Usage: "Sets the seconds for a write time out in the beacon api",
Value: 5,
Value: 60,
}
BeaconApiAddr = cli.StringFlag{
Name: "beacon.api.addr",