github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/helpers/weak_subjectivity_test.go (about) 1 package helpers_test 2 3 import ( 4 "fmt" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 9 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 10 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 11 "github.com/prysmaticlabs/prysm/shared/bytesutil" 12 "github.com/prysmaticlabs/prysm/shared/params" 13 "github.com/prysmaticlabs/prysm/shared/testutil" 14 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 15 "github.com/prysmaticlabs/prysm/shared/testutil/require" 16 ) 17 18 func TestWeakSubjectivity_ComputeWeakSubjectivityPeriod(t *testing.T) { 19 tests := []struct { 20 valCount uint64 21 avgBalance uint64 22 want types.Epoch 23 }{ 24 // Asserting that we get the same numbers as defined in the reference table: 25 // https://github.com/ethereum/eth2.0-specs/blob/master/specs/phase0/weak-subjectivity.md#calculating-the-weak-subjectivity-period 26 {valCount: 32768, avgBalance: 28, want: 504}, 27 {valCount: 65536, avgBalance: 28, want: 752}, 28 {valCount: 131072, avgBalance: 28, want: 1248}, 29 {valCount: 262144, avgBalance: 28, want: 2241}, 30 {valCount: 524288, avgBalance: 28, want: 2241}, 31 {valCount: 1048576, avgBalance: 28, want: 2241}, 32 {valCount: 32768, avgBalance: 32, want: 665}, 33 {valCount: 65536, avgBalance: 32, want: 1075}, 34 {valCount: 131072, avgBalance: 32, want: 1894}, 35 {valCount: 262144, avgBalance: 32, want: 3532}, 36 {valCount: 524288, avgBalance: 32, want: 3532}, 37 {valCount: 1048576, avgBalance: 32, want: 3532}, 38 // Additional test vectors, to check case when T*(200+3*D) >= t*(200+12*D) 39 {valCount: 32768, avgBalance: 22, want: 277}, 40 {valCount: 65536, avgBalance: 22, want: 298}, 41 {valCount: 131072, avgBalance: 22, want: 340}, 42 {valCount: 262144, avgBalance: 22, want: 424}, 43 {valCount: 524288, avgBalance: 22, want: 593}, 44 {valCount: 1048576, avgBalance: 22, want: 931}, 45 } 46 for _, tt := range tests { 47 t.Run(fmt.Sprintf("valCount: %d, avgBalance: %d", tt.valCount, tt.avgBalance), func(t *testing.T) { 48 // Reset committee cache - as we need to recalculate active validator set for each test. 49 helpers.ClearCache() 50 got, err := helpers.ComputeWeakSubjectivityPeriod(genState(t, tt.valCount, tt.avgBalance)) 51 require.NoError(t, err) 52 assert.Equal(t, tt.want, got, "valCount: %v, avgBalance: %v", tt.valCount, tt.avgBalance) 53 }) 54 } 55 } 56 func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) { 57 tests := []struct { 58 name string 59 epoch types.Epoch 60 genWsState func() iface.ReadOnlyBeaconState 61 genWsCheckpoint func() *ethpb.WeakSubjectivityCheckpoint 62 want bool 63 wantedErr string 64 }{ 65 { 66 name: "nil weak subjectivity state", 67 genWsState: func() iface.ReadOnlyBeaconState { 68 return nil 69 }, 70 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 71 return ðpb.WeakSubjectivityCheckpoint{ 72 BlockRoot: make([]byte, 32), 73 StateRoot: make([]byte, 32), 74 Epoch: 42, 75 } 76 }, 77 wantedErr: "invalid weak subjectivity state or checkpoint", 78 }, 79 { 80 name: "nil weak subjectivity checkpoint", 81 genWsState: func() iface.ReadOnlyBeaconState { 82 return genState(t, 128, 32) 83 }, 84 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 85 return nil 86 }, 87 wantedErr: "invalid weak subjectivity state or checkpoint", 88 }, 89 { 90 name: "state and checkpoint roots do not match", 91 genWsState: func() iface.ReadOnlyBeaconState { 92 beaconState := genState(t, 128, 32) 93 require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch)) 94 err := beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{ 95 Slot: 42 * params.BeaconConfig().SlotsPerEpoch, 96 StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32), 97 }) 98 require.NoError(t, err) 99 return beaconState 100 }, 101 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 102 return ðpb.WeakSubjectivityCheckpoint{ 103 StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32), 104 Epoch: 42, 105 } 106 }, 107 wantedErr: fmt.Sprintf("state (%#x) and checkpoint (%#x) roots do not match", 108 bytesutil.PadTo([]byte("stateroot1"), 32), bytesutil.PadTo([]byte("stateroot2"), 32)), 109 }, 110 { 111 name: "state and checkpoint epochs do not match", 112 genWsState: func() iface.ReadOnlyBeaconState { 113 beaconState := genState(t, 128, 32) 114 require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch)) 115 err := beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{ 116 Slot: 42 * params.BeaconConfig().SlotsPerEpoch, 117 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 118 }) 119 require.NoError(t, err) 120 return beaconState 121 }, 122 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 123 return ðpb.WeakSubjectivityCheckpoint{ 124 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 125 Epoch: 43, 126 } 127 }, 128 wantedErr: "state (42) and checkpoint (43) epochs do not match", 129 }, 130 { 131 name: "no active validators", 132 genWsState: func() iface.ReadOnlyBeaconState { 133 beaconState := genState(t, 0, 32) 134 require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch)) 135 err := beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{ 136 Slot: 42 * params.BeaconConfig().SlotsPerEpoch, 137 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 138 }) 139 require.NoError(t, err) 140 return beaconState 141 }, 142 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 143 return ðpb.WeakSubjectivityCheckpoint{ 144 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 145 Epoch: 42, 146 } 147 }, 148 wantedErr: "cannot compute weak subjectivity period: no active validators found", 149 }, 150 { 151 name: "outside weak subjectivity period", 152 epoch: 300, 153 genWsState: func() iface.ReadOnlyBeaconState { 154 beaconState := genState(t, 128, 32) 155 require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch)) 156 err := beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{ 157 Slot: 42 * params.BeaconConfig().SlotsPerEpoch, 158 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 159 }) 160 require.NoError(t, err) 161 return beaconState 162 }, 163 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 164 return ðpb.WeakSubjectivityCheckpoint{ 165 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 166 Epoch: 42, 167 } 168 }, 169 want: false, 170 }, 171 { 172 name: "within weak subjectivity period", 173 epoch: 299, 174 genWsState: func() iface.ReadOnlyBeaconState { 175 beaconState := genState(t, 128, 32) 176 require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch)) 177 err := beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{ 178 Slot: 42 * params.BeaconConfig().SlotsPerEpoch, 179 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 180 }) 181 require.NoError(t, err) 182 return beaconState 183 }, 184 genWsCheckpoint: func() *ethpb.WeakSubjectivityCheckpoint { 185 return ðpb.WeakSubjectivityCheckpoint{ 186 StateRoot: bytesutil.PadTo([]byte("stateroot"), 32), 187 Epoch: 42, 188 } 189 }, 190 want: true, 191 }, 192 } 193 for _, tt := range tests { 194 t.Run(tt.name, func(t *testing.T) { 195 got, err := helpers.IsWithinWeakSubjectivityPeriod(tt.epoch, tt.genWsState(), tt.genWsCheckpoint()) 196 if tt.wantedErr != "" { 197 assert.Equal(t, false, got) 198 assert.ErrorContains(t, tt.wantedErr, err) 199 return 200 } 201 require.NoError(t, err) 202 assert.Equal(t, tt.want, got) 203 }) 204 } 205 } 206 207 func TestWeakSubjectivity_ParseWeakSubjectivityInputString(t *testing.T) { 208 tests := []struct { 209 name string 210 input string 211 checkpt *ethpb.Checkpoint 212 wantedErr string 213 }{ 214 { 215 name: "No column in string", 216 input: "0x111111;123", 217 wantedErr: "did not contain column", 218 }, 219 { 220 name: "Too many columns in string", 221 input: "0x010203:123:456", 222 wantedErr: "weak subjectivity checkpoint input should be in `block_root:epoch_number` format", 223 }, 224 { 225 name: "Incorrect block root length", 226 input: "0x010203:987", 227 wantedErr: "block root is not length of 32", 228 }, 229 { 230 name: "Correct input", 231 input: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789", 232 checkpt: ðpb.Checkpoint{ 233 Root: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 234 Epoch: types.Epoch(123456789), 235 }, 236 }, 237 { 238 name: "Correct input without 0x", 239 input: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789", 240 checkpt: ðpb.Checkpoint{ 241 Root: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 242 Epoch: types.Epoch(123456789), 243 }, 244 }, 245 { 246 name: "Correct input", 247 input: "0xF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789", 248 checkpt: ðpb.Checkpoint{ 249 Root: []byte{0xf0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 250 Epoch: types.Epoch(123456789), 251 }, 252 }, 253 { 254 name: "Correct input without 0x", 255 input: "F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789", 256 checkpt: ðpb.Checkpoint{ 257 Root: []byte{0xf0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, 258 Epoch: types.Epoch(123456789), 259 }, 260 }, 261 } 262 for _, tt := range tests { 263 t.Run(tt.name, func(t *testing.T) { 264 wsCheckpt, err := helpers.ParseWeakSubjectivityInputString(tt.input) 265 if tt.wantedErr != "" { 266 require.ErrorContains(t, tt.wantedErr, err) 267 return 268 } 269 require.NoError(t, err) 270 require.NotNil(t, wsCheckpt) 271 require.DeepEqual(t, tt.checkpt.Root, wsCheckpt.Root, "Roots do not match") 272 require.Equal(t, tt.checkpt.Epoch, wsCheckpt.Epoch, "Epochs do not match") 273 }) 274 } 275 } 276 277 func genState(t *testing.T, valCount, avgBalance uint64) iface.BeaconState { 278 beaconState, err := testutil.NewBeaconState() 279 require.NoError(t, err) 280 281 validators := make([]*ethpb.Validator, valCount) 282 balances := make([]uint64, len(validators)) 283 for i := uint64(0); i < valCount; i++ { 284 validators[i] = ðpb.Validator{ 285 PublicKey: make([]byte, params.BeaconConfig().BLSPubkeyLength), 286 WithdrawalCredentials: make([]byte, 32), 287 EffectiveBalance: avgBalance * 1e9, 288 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 289 } 290 balances[i] = validators[i].EffectiveBalance 291 } 292 293 require.NoError(t, beaconState.SetValidators(validators)) 294 require.NoError(t, beaconState.SetBalances(balances)) 295 296 return beaconState 297 }