mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Fix validator export failing when validator.db is in nested directory (#15351)
* files changed * added changelog file * added tests and formatting changes * fixed bugs * fixed formatting
This commit is contained in:
@@ -58,10 +58,11 @@ func exportSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
// Ensure that the database is found under the specified dir or its subdirectories
|
||||
var matchPath string
|
||||
if isDatabaseMinimal {
|
||||
found, _, err = file.RecursiveDirFind(filesystem.DatabaseDirName, dataDir)
|
||||
found, matchPath, err = file.RecursiveDirFind(filesystem.DatabaseDirName, dataDir)
|
||||
} else {
|
||||
found, _, err = file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
found, matchPath, err = file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -74,6 +75,12 @@ func exportSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
databaseFileDir = filesystem.DatabaseDirName
|
||||
}
|
||||
return fmt.Errorf("%s (validator database) was not found at path %s, so nothing to export", databaseFileDir, dataDir)
|
||||
} else {
|
||||
if !isDatabaseMinimal {
|
||||
matchPath = filepath.Dir(matchPath) // strip the file name
|
||||
}
|
||||
dataDir = matchPath
|
||||
log.Infof("Found validator database at path %s", dataDir)
|
||||
}
|
||||
|
||||
// Open the validator database.
|
||||
|
||||
@@ -3,6 +3,7 @@ package historycmd
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/cmd"
|
||||
"github.com/OffchainLabs/prysm/v6/cmd/validator/flags"
|
||||
@@ -45,16 +46,29 @@ func importSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
// Ensure that the database is found under the specified directory or its subdirectories
|
||||
var matchPath string
|
||||
if isDatabaseMinimal {
|
||||
found, _, err = file.RecursiveDirFind(filesystem.DatabaseDirName, dataDir)
|
||||
found, matchPath, err = file.RecursiveDirFind(filesystem.DatabaseDirName, dataDir)
|
||||
} else {
|
||||
found, _, err = file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
found, matchPath, err = file.RecursiveFileFind(kv.ProtectionDbFileName, dataDir)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error finding validator database at path %s", dataDir)
|
||||
}
|
||||
|
||||
if !found {
|
||||
databaseFileDir := kv.ProtectionDbFileName
|
||||
if isDatabaseMinimal {
|
||||
databaseFileDir = filesystem.DatabaseDirName
|
||||
}
|
||||
return fmt.Errorf("%s (validator database) was not found at path %s, so nothing to export", databaseFileDir, dataDir)
|
||||
} else {
|
||||
if !isDatabaseMinimal {
|
||||
matchPath = filepath.Dir(matchPath) // strip the file name
|
||||
}
|
||||
dataDir = matchPath
|
||||
log.Infof("Found validator database at path %s", dataDir)
|
||||
}
|
||||
message := "Found existing database inside of %s"
|
||||
if !found {
|
||||
message = "Did not find existing database inside of %s, creating a new one"
|
||||
|
||||
@@ -3,9 +3,6 @@ package historycmd
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/cmd"
|
||||
"github.com/OffchainLabs/prysm/v6/cmd/validator/flags"
|
||||
"github.com/OffchainLabs/prysm/v6/io/file"
|
||||
@@ -16,6 +13,8 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v6/validator/slashing-protection-history/format"
|
||||
mocks "github.com/OffchainLabs/prysm/v6/validator/testing"
|
||||
"github.com/urfave/cli/v2"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupCliCtx(
|
||||
@@ -41,76 +40,100 @@ func setupCliCtx(
|
||||
// slashing protection history database will keep only the latest signed block slot / attestations,
|
||||
// and thus will not be able to export the same data as the original file.
|
||||
func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) {
|
||||
numValidators := 10
|
||||
outputPath := filepath.Join(t.TempDir(), "slashing-exports")
|
||||
err := file.MkdirAll(outputPath)
|
||||
require.NoError(t, err)
|
||||
protectionFileName := "slashing_history_import.json"
|
||||
|
||||
// Create some mock slashing protection history. and JSON file
|
||||
pubKeys, err := mocks.CreateRandomPubKeys(numValidators)
|
||||
require.NoError(t, err)
|
||||
attestingHistory, proposalHistory := mocks.MockAttestingAndProposalHistories(pubKeys)
|
||||
require.NoError(t, err)
|
||||
mockJSON, err := mocks.MockSlashingProtectionJSON(pubKeys, attestingHistory, proposalHistory)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We JSON encode the protection file and save it to disk as a JSON file.
|
||||
encoded, err := json.Marshal(mockJSON)
|
||||
require.NoError(t, err)
|
||||
|
||||
protectionFilePath := filepath.Join(outputPath, protectionFileName)
|
||||
err = file.WriteFile(protectionFilePath, encoded)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We create a CLI context with the required values, such as the database datadir and output directory.
|
||||
isSlashingProtectionMinimal := false
|
||||
validatorDB := dbTest.SetupDB(t, pubKeys, isSlashingProtectionMinimal)
|
||||
dbPath := validatorDB.DatabasePath()
|
||||
require.NoError(t, validatorDB.Close())
|
||||
cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath)
|
||||
|
||||
// We import the slashing protection history file via CLI.
|
||||
err = importSlashingProtectionJSON(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We export the slashing protection history file via CLI.
|
||||
err = exportSlashingProtectionJSON(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Attempt to read the exported file from the output directory.
|
||||
enc, err := file.ReadFileAsBytes(filepath.Join(outputPath, jsonExportFileName))
|
||||
require.NoError(t, err)
|
||||
|
||||
receivedJSON := &format.EIPSlashingProtectionFormat{}
|
||||
err = json.Unmarshal(enc, receivedJSON)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We verify the parsed JSON file matches. Given there is no guarantee of order,
|
||||
// we will have to carefully compare and sort values as needed.
|
||||
//
|
||||
// First, we compare basic data such as the MetadataV0 value in the JSON file.
|
||||
require.DeepEqual(t, mockJSON.Metadata, receivedJSON.Metadata)
|
||||
wantedHistoryByPublicKey := make(map[string]*format.ProtectionData)
|
||||
for _, item := range mockJSON.Data {
|
||||
wantedHistoryByPublicKey[item.Pubkey] = item
|
||||
layouts := []struct {
|
||||
name string
|
||||
mutate func(t *testing.T) (string, string)
|
||||
}{
|
||||
{
|
||||
name: "flat",
|
||||
mutate: func(t *testing.T) (string, string) {
|
||||
dbPath := t.TempDir()
|
||||
return dbPath, dbPath
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested",
|
||||
mutate: func(t *testing.T) (string, string) {
|
||||
dbPath := t.TempDir()
|
||||
nestedDir := filepath.Join(dbPath, "data", "direct")
|
||||
return dbPath, nestedDir
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range layouts {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
numValidators := 10
|
||||
outputPath := filepath.Join(t.TempDir(), "slashing-exports")
|
||||
err := file.MkdirAll(outputPath)
|
||||
require.NoError(t, err)
|
||||
protectionFileName := "slashing_history_import.json"
|
||||
|
||||
// Next, we compare all the data for each validator public key.
|
||||
for _, item := range receivedJSON.Data {
|
||||
wanted, ok := wantedHistoryByPublicKey[item.Pubkey]
|
||||
require.Equal(t, true, ok)
|
||||
wantedAttsByRoot := make(map[string]*format.SignedAttestation)
|
||||
for _, att := range wanted.SignedAttestations {
|
||||
wantedAttsByRoot[att.SigningRoot] = att
|
||||
}
|
||||
for _, att := range item.SignedAttestations {
|
||||
wantedAtt, ok := wantedAttsByRoot[att.SigningRoot]
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, wantedAtt, att)
|
||||
}
|
||||
require.Equal(t, len(wanted.SignedBlocks), len(item.SignedBlocks))
|
||||
require.DeepEqual(t, wanted.SignedBlocks, item.SignedBlocks)
|
||||
// Create some mock slashing protection history. and JSON file
|
||||
pubKeys, err := mocks.CreateRandomPubKeys(numValidators)
|
||||
require.NoError(t, err)
|
||||
attestingHistory, proposalHistory := mocks.MockAttestingAndProposalHistories(pubKeys)
|
||||
require.NoError(t, err)
|
||||
mockJSON, err := mocks.MockSlashingProtectionJSON(pubKeys, attestingHistory, proposalHistory)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We JSON encode the protection file and save it to disk as a JSON file.
|
||||
encoded, err := json.Marshal(mockJSON)
|
||||
require.NoError(t, err)
|
||||
|
||||
protectionFilePath := filepath.Join(outputPath, protectionFileName)
|
||||
err = file.WriteFile(protectionFilePath, encoded)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We create a CLI context with the required values, such as the database datadir and output directory.
|
||||
isSlashingProtectionMinimal := false
|
||||
dataDir, dataPath := tc.mutate(t)
|
||||
validatorDB := dbTest.SetupDB(t, dataPath, pubKeys, isSlashingProtectionMinimal)
|
||||
require.NoError(t, validatorDB.Close())
|
||||
cliCtx := setupCliCtx(t, dataDir, protectionFilePath, outputPath)
|
||||
|
||||
// We import the slashing protection history file via CLI.
|
||||
err = importSlashingProtectionJSON(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We export the slashing protection history file via CLI.
|
||||
err = exportSlashingProtectionJSON(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Attempt to read the exported file from the output directory.
|
||||
enc, err := file.ReadFileAsBytes(filepath.Join(outputPath, jsonExportFileName))
|
||||
require.NoError(t, err)
|
||||
|
||||
receivedJSON := &format.EIPSlashingProtectionFormat{}
|
||||
err = json.Unmarshal(enc, receivedJSON)
|
||||
require.NoError(t, err)
|
||||
|
||||
// We verify the parsed JSON file matches. Given there is no guarantee of order,
|
||||
// we will have to carefully compare and sort values as needed.
|
||||
//
|
||||
// First, we compare basic data such as the MetadataV0 value in the JSON file.
|
||||
require.DeepEqual(t, mockJSON.Metadata, receivedJSON.Metadata)
|
||||
wantedHistoryByPublicKey := make(map[string]*format.ProtectionData)
|
||||
for _, item := range mockJSON.Data {
|
||||
wantedHistoryByPublicKey[item.Pubkey] = item
|
||||
}
|
||||
|
||||
// Next, we compare all the data for each validator public key.
|
||||
for _, item := range receivedJSON.Data {
|
||||
wanted, ok := wantedHistoryByPublicKey[item.Pubkey]
|
||||
require.Equal(t, true, ok)
|
||||
wantedAttsByRoot := make(map[string]*format.SignedAttestation)
|
||||
for _, att := range wanted.SignedAttestations {
|
||||
wantedAttsByRoot[att.SigningRoot] = att
|
||||
}
|
||||
for _, att := range item.SignedAttestations {
|
||||
wantedAtt, ok := wantedAttsByRoot[att.SigningRoot]
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, wantedAtt, att)
|
||||
}
|
||||
require.Equal(t, len(wanted.SignedBlocks), len(item.SignedBlocks))
|
||||
require.DeepEqual(t, wanted.SignedBlocks, item.SignedBlocks)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +170,7 @@ func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) {
|
||||
|
||||
// We create a CLI context with the required values, such as the database datadir and output directory.
|
||||
isSlashingProtectionMinimal := false
|
||||
validatorDB := dbTest.SetupDB(t, pubKeys, isSlashingProtectionMinimal)
|
||||
validatorDB := dbTest.SetupDB(t, t.TempDir(), pubKeys, isSlashingProtectionMinimal)
|
||||
dbPath := validatorDB.DatabasePath()
|
||||
require.NoError(t, validatorDB.Close())
|
||||
cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath)
|
||||
|
||||
Reference in New Issue
Block a user