github.com/prysmaticlabs/prysm@v1.4.4/validator/client/slashing_protection_interchange_test.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "encoding/json" 8 "strings" 9 "testing" 10 11 "github.com/bazelbuild/rules_go/go/tools/bazel" 12 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 13 "github.com/prysmaticlabs/prysm/shared/featureconfig" 14 "github.com/prysmaticlabs/prysm/shared/fileutil" 15 "github.com/prysmaticlabs/prysm/shared/testutil" 16 "github.com/prysmaticlabs/prysm/shared/testutil/require" 17 interchangeformat "github.com/prysmaticlabs/prysm/validator/slashing-protection/local/standard-protection-format" 18 ) 19 20 type eip3076TestCase struct { 21 Name string `json:"name"` 22 GenesisValidatorsRoot string `json:"genesis_validators_root"` 23 Steps []struct { 24 ShouldSucceed bool `json:"should_succeed"` 25 AllowPartialImport bool `json:"allow_partial_import"` 26 Interchange struct { 27 Metadata struct { 28 InterchangeFormatVersion string `json:"interchange_format_version"` 29 GenesisValidatorsRoot string `json:"genesis_validators_root"` 30 } `json:"metadata"` 31 Data []struct { 32 Pubkey string `json:"pubkey"` 33 SignedBlocks []struct { 34 Slot string `json:"slot"` 35 SigningRoot string `json:"signing_root"` 36 } `json:"signed_blocks"` 37 SignedAttestations []struct { 38 SourceEpoch string `json:"source_epoch"` 39 TargetEpoch string `json:"target_epoch"` 40 SigningRoot string `json:"signing_root"` 41 } `json:"signed_attestations"` 42 } `json:"data"` 43 } `json:"interchange"` 44 Blocks []struct { 45 Pubkey string `json:"pubkey"` 46 Slot string `json:"slot"` 47 SigningRoot string `json:"signing_root"` 48 ShouldSucceed bool `json:"should_succeed"` 49 } `json:"blocks"` 50 Attestations []struct { 51 Pubkey string `json:"pubkey"` 52 SourceEpoch string `json:"source_epoch"` 53 TargetEpoch string `json:"target_epoch"` 54 SigningRoot string `json:"signing_root"` 55 ShouldSucceed bool `json:"should_succeed"` 56 } `json:"attestations"` 57 } `json:"steps"` 58 } 59 60 func setupEIP3076SpecTests(t *testing.T) []*eip3076TestCase { 61 testFolders, err := bazel.ListRunfiles() 62 require.NoError(t, err) 63 testCases := make([]*eip3076TestCase, 0) 64 for _, ff := range testFolders { 65 if strings.Contains(ff.ShortPath, "eip3076_spec_tests") && 66 strings.Contains(ff.ShortPath, "generated/") { 67 enc, err := fileutil.ReadFileAsBytes(ff.Path) 68 require.NoError(t, err) 69 testCase := &eip3076TestCase{} 70 require.NoError(t, json.Unmarshal(enc, testCase)) 71 testCases = append(testCases, testCase) 72 } 73 } 74 return testCases 75 } 76 77 func TestEIP3076SpecTests(t *testing.T) { 78 config := &featureconfig.Flags{ 79 SlasherProtection: true, 80 } 81 reset := featureconfig.InitWithReset(config) 82 defer reset() 83 84 testCases := setupEIP3076SpecTests(t) 85 for _, tt := range testCases { 86 t.Run(tt.Name, func(t *testing.T) { 87 if tt.Name == "" { 88 t.Skip("Skipping eip3076TestCase with empty name") 89 } 90 for _, step := range tt.Steps { 91 // Set up validator client, one new validator client per eip3076TestCase. 92 // This ensures we initialize a new (empty) slashing protection database. 93 validator, _, _, _ := setup(t) 94 95 if tt.GenesisValidatorsRoot != "" { 96 r, err := interchangeformat.RootFromHex(tt.GenesisValidatorsRoot) 97 require.NoError(t, validator.db.SaveGenesisValidatorsRoot(context.Background(), r[:])) 98 require.NoError(t, err) 99 } 100 101 // The eip3076TestCase config contains the interchange config in json. 102 // This loads the interchange data via ImportStandardProtectionJSON. 103 interchangeBytes, err := json.Marshal(step.Interchange) 104 if err != nil { 105 t.Fatal(err) 106 } 107 b := bytes.NewBuffer(interchangeBytes) 108 if err := interchangeformat.ImportStandardProtectionJSON(context.Background(), validator.db, b); err != nil { 109 if step.ShouldSucceed { 110 t.Fatal(err) 111 } 112 } else if !step.ShouldSucceed { 113 require.NotNil(t, err, "import standard protection json should have failed") 114 } 115 116 // This loops through a list of block signings to attempt after importing the interchange data above. 117 for _, sb := range step.Blocks { 118 bSlot, err := interchangeformat.SlotFromString(sb.Slot) 119 require.NoError(t, err) 120 pk, err := interchangeformat.PubKeyFromHex(sb.Pubkey) 121 require.NoError(t, err) 122 b := testutil.NewBeaconBlock() 123 b.Block.Slot = bSlot 124 125 var signingRoot [32]byte 126 if sb.SigningRoot != "" { 127 signingRootBytes, err := hex.DecodeString(strings.TrimPrefix(sb.SigningRoot, "0x")) 128 require.NoError(t, err) 129 copy(signingRoot[:], signingRootBytes) 130 } 131 132 err = validator.preBlockSignValidations(context.Background(), pk, b.Block, signingRoot) 133 if sb.ShouldSucceed { 134 require.NoError(t, err) 135 } else { 136 require.NotEqual(t, nil, err, "pre validation should have failed for block") 137 } 138 139 // Only proceed post update if pre validation did not error. 140 if err == nil { 141 err = validator.postBlockSignUpdate(context.Background(), pk, b, signingRoot) 142 if sb.ShouldSucceed { 143 require.NoError(t, err) 144 } else { 145 require.NotEqual(t, nil, err, "post validation should have failed for block") 146 } 147 } 148 } 149 150 // This loops through a list of attestation signings to attempt after importing the interchange data above. 151 for _, sa := range step.Attestations { 152 target, err := interchangeformat.EpochFromString(sa.TargetEpoch) 153 require.NoError(t, err) 154 source, err := interchangeformat.EpochFromString(sa.SourceEpoch) 155 require.NoError(t, err) 156 pk, err := interchangeformat.PubKeyFromHex(sa.Pubkey) 157 require.NoError(t, err) 158 ia := ðpb.IndexedAttestation{ 159 Data: ðpb.AttestationData{ 160 BeaconBlockRoot: make([]byte, 32), 161 Target: ðpb.Checkpoint{Epoch: target, Root: make([]byte, 32)}, 162 Source: ðpb.Checkpoint{Epoch: source, Root: make([]byte, 32)}, 163 }, 164 Signature: make([]byte, 96), 165 } 166 167 var signingRoot [32]byte 168 if sa.SigningRoot != "" { 169 signingRootBytes, err := hex.DecodeString(strings.TrimPrefix(sa.SigningRoot, "0x")) 170 require.NoError(t, err) 171 copy(signingRoot[:], signingRootBytes) 172 } 173 174 err = validator.slashableAttestationCheck(context.Background(), ia, pk, signingRoot) 175 if sa.ShouldSucceed { 176 require.NoError(t, err) 177 } else { 178 require.NotNil(t, err, "pre validation should have failed for attestation") 179 } 180 } 181 require.NoError(t, err, validator.db.Close()) 182 } 183 }) 184 } 185 }