github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/state/skip_slot_cache_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"testing"
     7  
     8  	"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
     9  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    11  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    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 TestSkipSlotCache_OK(t *testing.T) {
    19  	state.SkipSlotCache.Enable()
    20  	defer state.SkipSlotCache.Disable()
    21  	bState, privs := testutil.DeterministicGenesisState(t, params.MinimalSpecConfig().MinGenesisActiveValidatorCount)
    22  	pbState, err := v1.ProtobufBeaconState(bState.CloneInnerState())
    23  	require.NoError(t, err)
    24  	originalState, err := v1.InitializeFromProto(pbState)
    25  	require.NoError(t, err)
    26  
    27  	blkCfg := testutil.DefaultBlockGenConfig()
    28  	blkCfg.NumAttestations = 1
    29  
    30  	// First transition will be with an empty cache, so the cache becomes populated
    31  	// with the state
    32  	blk, err := testutil.GenerateFullBlock(bState, privs, blkCfg, originalState.Slot()+10)
    33  	require.NoError(t, err)
    34  	executedState, err := state.ExecuteStateTransition(context.Background(), originalState, wrapper.WrappedPhase0SignedBeaconBlock(blk))
    35  	require.NoError(t, err, "Could not run state transition")
    36  	originalState, ok := executedState.(*v1.BeaconState)
    37  	require.Equal(t, true, ok)
    38  	bState, err = state.ExecuteStateTransition(context.Background(), bState, wrapper.WrappedPhase0SignedBeaconBlock(blk))
    39  	require.NoError(t, err, "Could not process state transition")
    40  
    41  	assert.DeepEqual(t, originalState.CloneInnerState(), bState.CloneInnerState(), "Skipped slots cache leads to different states")
    42  }
    43  
    44  func TestSkipSlotCache_ConcurrentMixup(t *testing.T) {
    45  	bState, privs := testutil.DeterministicGenesisState(t, params.MinimalSpecConfig().MinGenesisActiveValidatorCount)
    46  	pbState, err := v1.ProtobufBeaconState(bState.CloneInnerState())
    47  	require.NoError(t, err)
    48  	originalState, err := v1.InitializeFromProto(pbState)
    49  	require.NoError(t, err)
    50  
    51  	blkCfg := testutil.DefaultBlockGenConfig()
    52  	blkCfg.NumAttestations = 1
    53  
    54  	state.SkipSlotCache.Disable()
    55  
    56  	// First transition will be with an empty cache, so the cache becomes populated
    57  	// with the state
    58  	blk, err := testutil.GenerateFullBlock(bState, privs, blkCfg, originalState.Slot()+10)
    59  	require.NoError(t, err)
    60  	executedState, err := state.ExecuteStateTransition(context.Background(), originalState, wrapper.WrappedPhase0SignedBeaconBlock(blk))
    61  	require.NoError(t, err, "Could not run state transition")
    62  	originalState, ok := executedState.(*v1.BeaconState)
    63  	require.Equal(t, true, ok)
    64  
    65  	// Create two shallow but different forks
    66  	var state1, state2 iface.BeaconState
    67  	{
    68  		blk, err := testutil.GenerateFullBlock(originalState.Copy(), privs, blkCfg, originalState.Slot()+10)
    69  		require.NoError(t, err)
    70  		copy(blk.Block.Body.Graffiti, "block 1")
    71  		signature, err := testutil.BlockSignature(originalState, blk.Block, privs)
    72  		require.NoError(t, err)
    73  		blk.Signature = signature.Marshal()
    74  		state1, err = state.ExecuteStateTransition(context.Background(), originalState.Copy(), wrapper.WrappedPhase0SignedBeaconBlock(blk))
    75  		require.NoError(t, err, "Could not run state transition")
    76  	}
    77  
    78  	{
    79  		blk, err := testutil.GenerateFullBlock(originalState.Copy(), privs, blkCfg, originalState.Slot()+10)
    80  		require.NoError(t, err)
    81  		copy(blk.Block.Body.Graffiti, "block 2")
    82  		signature, err := testutil.BlockSignature(originalState, blk.Block, privs)
    83  		require.NoError(t, err)
    84  		blk.Signature = signature.Marshal()
    85  		state2, err = state.ExecuteStateTransition(context.Background(), originalState.Copy(), wrapper.WrappedPhase0SignedBeaconBlock(blk))
    86  		require.NoError(t, err, "Could not run state transition")
    87  	}
    88  
    89  	r1, err := state1.HashTreeRoot(context.Background())
    90  	require.NoError(t, err)
    91  	r2, err := state2.HashTreeRoot(context.Background())
    92  	require.NoError(t, err)
    93  	if r1 == r2 {
    94  		t.Fatalf("need different starting states, got: %x", r1)
    95  	}
    96  
    97  	if state1.Slot() != state2.Slot() {
    98  		t.Fatalf("expecting different chains, but states at same slot")
    99  	}
   100  
   101  	// prepare copies for both states
   102  	var setups []iface.BeaconState
   103  	for i := uint64(0); i < 300; i++ {
   104  		var st iface.BeaconState
   105  		if i%2 == 0 {
   106  			st = state1
   107  		} else {
   108  			st = state2
   109  		}
   110  		setups = append(setups, st.Copy())
   111  	}
   112  
   113  	problemSlot := state1.Slot() + 2
   114  	expected1, err := state.ProcessSlots(context.Background(), state1.Copy(), problemSlot)
   115  	require.NoError(t, err)
   116  	expectedRoot1, err := expected1.HashTreeRoot(context.Background())
   117  	require.NoError(t, err)
   118  	t.Logf("chain 1 (even i) expected root %x at slot %d", expectedRoot1[:], problemSlot)
   119  
   120  	tmp1, err := state.ProcessSlots(context.Background(), expected1.Copy(), problemSlot+1)
   121  	require.NoError(t, err)
   122  	gotRoot := tmp1.StateRoots()[problemSlot]
   123  	require.DeepEqual(t, expectedRoot1[:], gotRoot, "State roots for chain 1 are bad, expected root doesn't match")
   124  
   125  	expected2, err := state.ProcessSlots(context.Background(), state2.Copy(), problemSlot)
   126  	require.NoError(t, err)
   127  	expectedRoot2, err := expected2.HashTreeRoot(context.Background())
   128  	require.NoError(t, err)
   129  	t.Logf("chain 2 (odd i) expected root %x at slot %d", expectedRoot2[:], problemSlot)
   130  
   131  	tmp2, err := state.ProcessSlots(context.Background(), expected2.Copy(), problemSlot+1)
   132  	require.NoError(t, err)
   133  	gotRoot = tmp2.StateRoots()[problemSlot]
   134  	require.DeepEqual(t, expectedRoot2[:], gotRoot, "State roots for chain 2 are bad, expected root doesn't match")
   135  
   136  	var wg sync.WaitGroup
   137  	wg.Add(len(setups))
   138  
   139  	step := func(i int, setup iface.BeaconState) {
   140  		// go at least 1 past problemSlot, to ensure problem slot state root is available
   141  		outState, err := state.ProcessSlots(context.Background(), setup, problemSlot.Add(1+uint64(i))) // keep increasing, to hit and extend the cache
   142  		require.NoError(t, err, "Could not process state transition")
   143  		roots := outState.StateRoots()
   144  		gotRoot := roots[problemSlot]
   145  		if i%2 == 0 {
   146  			assert.DeepEqual(t, expectedRoot1[:], gotRoot, "Unexpected root on chain 1")
   147  		} else {
   148  			assert.DeepEqual(t, expectedRoot2[:], gotRoot, "Unexpected root on chain 2")
   149  		}
   150  		wg.Done()
   151  	}
   152  
   153  	state.SkipSlotCache.Enable()
   154  	// now concurrently apply the blocks (alternating between states, and increasing skip slots)
   155  	for i, setup := range setups {
   156  		go step(i, setup)
   157  	}
   158  	// Wait for all transitions to finish
   159  	wg.Wait()
   160  }