github.com/prysmaticlabs/prysm@v1.4.4/validator/slashing-protection/cli_import_export_test.go (about) 1 package slashingprotection 2 3 import ( 4 "encoding/json" 5 "flag" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/prysmaticlabs/prysm/cmd/validator/flags" 11 "github.com/prysmaticlabs/prysm/shared/cmd" 12 "github.com/prysmaticlabs/prysm/shared/fileutil" 13 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 14 "github.com/prysmaticlabs/prysm/shared/testutil/require" 15 "github.com/prysmaticlabs/prysm/validator/db/kv" 16 dbTest "github.com/prysmaticlabs/prysm/validator/db/testing" 17 "github.com/prysmaticlabs/prysm/validator/slashing-protection/local/standard-protection-format/format" 18 mocks "github.com/prysmaticlabs/prysm/validator/testing" 19 "github.com/urfave/cli/v2" 20 ) 21 22 func setupCliCtx( 23 tb testing.TB, 24 dbPath, 25 protectionFilePath, 26 outputDir string, 27 ) *cli.Context { 28 app := cli.App{} 29 set := flag.NewFlagSet("test", 0) 30 set.String(cmd.DataDirFlag.Name, dbPath, "") 31 set.String(flags.SlashingProtectionJSONFileFlag.Name, protectionFilePath, "") 32 set.String(flags.SlashingProtectionExportDirFlag.Name, outputDir, "") 33 require.NoError(tb, set.Set(flags.SlashingProtectionJSONFileFlag.Name, protectionFilePath)) 34 assert.NoError(tb, set.Set(cmd.DataDirFlag.Name, dbPath)) 35 assert.NoError(tb, set.Set(flags.SlashingProtectionExportDirFlag.Name, outputDir)) 36 return cli.NewContext(&app, set, nil) 37 } 38 39 func TestImportExportSlashingProtectionCli_RoundTrip(t *testing.T) { 40 numValidators := 10 41 outputPath := filepath.Join(os.TempDir(), "slashing-exports") 42 err := fileutil.MkdirAll(outputPath) 43 require.NoError(t, err) 44 protectionFileName := "slashing_history_import.json" 45 46 // Create some mock slashing protection history. and JSON file 47 pubKeys, err := mocks.CreateRandomPubKeys(numValidators) 48 require.NoError(t, err) 49 attestingHistory, proposalHistory := mocks.MockAttestingAndProposalHistories(pubKeys) 50 require.NoError(t, err) 51 mockJSON, err := mocks.MockSlashingProtectionJSON(pubKeys, attestingHistory, proposalHistory) 52 require.NoError(t, err) 53 54 // We JSON encode the protection file and save it to disk as a JSON file. 55 encoded, err := json.Marshal(mockJSON) 56 require.NoError(t, err) 57 58 protectionFilePath := filepath.Join(outputPath, protectionFileName) 59 err = fileutil.WriteFile(protectionFilePath, encoded) 60 require.NoError(t, err) 61 62 // We create a CLI context with the required values, such as the database datadir and output directory. 63 validatorDB := dbTest.SetupDB(t, pubKeys) 64 dbPath := validatorDB.DatabasePath() 65 require.NoError(t, validatorDB.Close()) 66 cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath) 67 68 // We import the slashing protection history file via CLI. 69 err = ImportSlashingProtectionCLI(cliCtx) 70 require.NoError(t, err) 71 72 // We export the slashing protection history file via CLI. 73 err = ExportSlashingProtectionJSONCli(cliCtx) 74 require.NoError(t, err) 75 76 // Attempt to read the exported file from the output directory. 77 enc, err := fileutil.ReadFileAsBytes(filepath.Join(outputPath, jsonExportFileName)) 78 require.NoError(t, err) 79 80 receivedJSON := &format.EIPSlashingProtectionFormat{} 81 err = json.Unmarshal(enc, receivedJSON) 82 require.NoError(t, err) 83 84 // We verify the parsed JSON file matches. Given there is no guarantee of order, 85 // we will have to carefully compare and sort values as needed. 86 // 87 // First, we compare basic data such as the MetadataV0 value in the JSON file. 88 require.DeepEqual(t, mockJSON.Metadata, receivedJSON.Metadata) 89 wantedHistoryByPublicKey := make(map[string]*format.ProtectionData) 90 for _, item := range mockJSON.Data { 91 wantedHistoryByPublicKey[item.Pubkey] = item 92 } 93 94 // Next, we compare all the data for each validator public key. 95 for _, item := range receivedJSON.Data { 96 wanted, ok := wantedHistoryByPublicKey[item.Pubkey] 97 require.Equal(t, true, ok) 98 wantedAttsByRoot := make(map[string]*format.SignedAttestation) 99 for _, att := range wanted.SignedAttestations { 100 wantedAttsByRoot[att.SigningRoot] = att 101 } 102 for _, att := range item.SignedAttestations { 103 wantedAtt, ok := wantedAttsByRoot[att.SigningRoot] 104 require.Equal(t, true, ok) 105 require.DeepEqual(t, wantedAtt, att) 106 } 107 require.Equal(t, len(wanted.SignedBlocks), len(item.SignedBlocks)) 108 require.DeepEqual(t, wanted.SignedBlocks, item.SignedBlocks) 109 } 110 } 111 112 func TestImportExportSlashingProtectionCli_EmptyData(t *testing.T) { 113 numValidators := 10 114 outputPath := filepath.Join(os.TempDir(), "slashing-exports") 115 err := fileutil.MkdirAll(outputPath) 116 require.NoError(t, err) 117 protectionFileName := "slashing_history_import.json" 118 119 // Create some mock slashing protection history. and JSON file 120 pubKeys, err := mocks.CreateRandomPubKeys(numValidators) 121 require.NoError(t, err) 122 attestingHistory := make([][]*kv.AttestationRecord, 0) 123 proposalHistory := make([]kv.ProposalHistoryForPubkey, len(pubKeys)) 124 for i := 0; i < len(pubKeys); i++ { 125 proposalHistory[i].Proposals = make([]kv.Proposal, 0) 126 } 127 mockJSON, err := mocks.MockSlashingProtectionJSON(pubKeys, attestingHistory, proposalHistory) 128 require.NoError(t, err) 129 130 // We JSON encode the protection file and save it to disk as a JSON file. 131 encoded, err := json.Marshal(mockJSON) 132 require.NoError(t, err) 133 134 protectionFilePath := filepath.Join(outputPath, protectionFileName) 135 err = fileutil.WriteFile(protectionFilePath, encoded) 136 require.NoError(t, err) 137 138 // We create a CLI context with the required values, such as the database datadir and output directory. 139 validatorDB := dbTest.SetupDB(t, pubKeys) 140 dbPath := validatorDB.DatabasePath() 141 require.NoError(t, validatorDB.Close()) 142 cliCtx := setupCliCtx(t, dbPath, protectionFilePath, outputPath) 143 144 // We import the slashing protection history file via CLI. 145 err = ImportSlashingProtectionCLI(cliCtx) 146 require.NoError(t, err) 147 148 // We export the slashing protection history file via CLI. 149 err = ExportSlashingProtectionJSONCli(cliCtx) 150 require.NoError(t, err) 151 152 // Attempt to read the exported file from the output directory. 153 enc, err := fileutil.ReadFileAsBytes(filepath.Join(outputPath, jsonExportFileName)) 154 require.NoError(t, err) 155 156 receivedJSON := &format.EIPSlashingProtectionFormat{} 157 err = json.Unmarshal(enc, receivedJSON) 158 require.NoError(t, err) 159 160 // We verify the parsed JSON file matches. Given there is no guarantee of order, 161 // we will have to carefully compare and sort values as needed. 162 // 163 // First, we compare basic data such as the MetadataV0 value in the JSON file. 164 require.DeepEqual(t, mockJSON.Metadata, receivedJSON.Metadata) 165 wantedHistoryByPublicKey := make(map[string]*format.ProtectionData) 166 for _, item := range mockJSON.Data { 167 wantedHistoryByPublicKey[item.Pubkey] = item 168 } 169 170 // Next, we compare all the data for each validator public key. 171 for _, item := range receivedJSON.Data { 172 wanted, ok := wantedHistoryByPublicKey[item.Pubkey] 173 require.Equal(t, true, ok) 174 require.Equal(t, len(wanted.SignedBlocks), len(item.SignedBlocks)) 175 require.Equal(t, len(wanted.SignedAttestations), len(item.SignedAttestations)) 176 require.DeepEqual(t, make([]*format.SignedBlock, 0), item.SignedBlocks) 177 require.DeepEqual(t, make([]*format.SignedAttestation, 0), item.SignedAttestations) 178 } 179 }