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 }