mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-14 09:58:12 -05:00
142 lines
4.6 KiB
Go
142 lines
4.6 KiB
Go
package filesystem
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"strings"
|
|
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/validator/db/common"
|
|
"github.com/OffchainLabs/prysm/v7/validator/db/iface"
|
|
"github.com/OffchainLabs/prysm/v7/validator/helpers"
|
|
"github.com/OffchainLabs/prysm/v7/validator/slashing-protection-history/format"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ImportStandardProtectionJSON takes in EIP-3076 compliant JSON file used for slashing protection
|
|
// by Ethereum validators and imports its data into Prysm's internal minimal representation of slashing
|
|
// protection in the validator client's database.
|
|
func (s *Store) ImportStandardProtectionJSON(ctx context.Context, r io.Reader) error {
|
|
// Read the JSON file
|
|
encodedJSON, err := io.ReadAll(r)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not read slashing protection JSON file")
|
|
}
|
|
|
|
// Unmarshal the JSON file
|
|
interchangeJSON := &format.EIPSlashingProtectionFormat{}
|
|
if err := json.Unmarshal(encodedJSON, interchangeJSON); err != nil {
|
|
return errors.Wrap(err, "could not unmarshal slashing protection JSON file")
|
|
}
|
|
|
|
// If there is no data in the JSON file, we can return early.
|
|
if interchangeJSON.Data == nil {
|
|
return nil
|
|
}
|
|
|
|
// We validate the `MetadataV0` field of the slashing protection JSON file.
|
|
if err := helpers.ValidateMetadata(ctx, s, interchangeJSON); err != nil {
|
|
return errors.Wrap(err, "slashing protection JSON metadata was incorrect")
|
|
}
|
|
|
|
// Save blocks proposals and attestations into the database
|
|
bar := common.InitializeProgressBar(len(interchangeJSON.Data), "Save blocks proposals and attestations:")
|
|
for _, item := range interchangeJSON.Data {
|
|
// Update progress bar
|
|
if err := bar.Add(1); err != nil {
|
|
return errors.Wrap(err, "could not update progress bar")
|
|
}
|
|
|
|
// If item is nil, skip
|
|
if item == nil {
|
|
continue
|
|
}
|
|
|
|
// Convert pubkey to bytes array
|
|
pubkeyBytes, err := hexutil.Decode(item.Pubkey)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not decode public key from hex")
|
|
}
|
|
|
|
pubkey := ([fieldparams.BLSPubkeyLength]byte)(pubkeyBytes)
|
|
|
|
// Block proposals
|
|
if err := importBlockProposals(ctx, pubkey, item, s); err != nil {
|
|
return errors.Wrap(err, "could not import block proposals")
|
|
}
|
|
|
|
// Attestations
|
|
if err := importAttestations(ctx, pubkey, item, s); err != nil {
|
|
return errors.Wrap(err, "could not import attestations")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func importBlockProposals(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte, item *format.ProtectionData, validatorDB iface.ValidatorDB) error {
|
|
for _, sb := range item.SignedBlocks {
|
|
// If signing block is nil, return early
|
|
if sb == nil {
|
|
return nil
|
|
}
|
|
|
|
// Convert slot to primitives.Slot
|
|
slot, err := helpers.SlotFromString(sb.Slot)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert slot to primitives.Slot")
|
|
}
|
|
|
|
// Save proposal if not slashable regarding EIP-3076 (minimal database)
|
|
if err := validatorDB.SaveProposalHistoryForSlot(ctx, pubkey, slot, []byte{}); err != nil && !strings.Contains(err.Error(), "could not sign proposal") {
|
|
return errors.Wrap(err, "could not save proposal history from imported JSON to database")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func importAttestations(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte, item *format.ProtectionData, validatorDB iface.ValidatorDB) error {
|
|
atts := make([]*ethpb.IndexedAttestation, len(item.SignedAttestations))
|
|
for i := range item.SignedAttestations {
|
|
// Get signed attestation
|
|
sa := item.SignedAttestations[i]
|
|
|
|
// Convert source epoch to primitives.Epoch
|
|
source, err := helpers.EpochFromString(sa.SourceEpoch)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert source epoch to primitives.Epoch")
|
|
}
|
|
|
|
// Convert target epoch to primitives.Epoch
|
|
target, err := helpers.EpochFromString(sa.TargetEpoch)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not convert target epoch to primitives.Epoch")
|
|
}
|
|
|
|
// Create indexed attestation
|
|
att := ðpb.IndexedAttestation{
|
|
Data: ðpb.AttestationData{
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: source,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: target,
|
|
},
|
|
},
|
|
}
|
|
|
|
atts[i] = att
|
|
}
|
|
|
|
// Save attestations
|
|
if err := validatorDB.SaveAttestationsForPubKey(ctx, pubkey, [][]byte{}, atts); err != nil && !strings.Contains(err.Error(), "could not sign attestation") {
|
|
return errors.Wrap(err, "could not save attestation record from imported JSON to database")
|
|
}
|
|
|
|
return nil
|
|
}
|