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 &ethpb.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(&ethpb.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 &ethpb.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(&ethpb.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 &ethpb.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(&ethpb.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 &ethpb.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(&ethpb.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 &ethpb.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(&ethpb.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 &ethpb.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: &ethpb.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: &ethpb.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: &ethpb.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: &ethpb.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] = &ethpb.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  }