github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/checker/core_test.go (about) 1 package checker_test 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/mock" 7 "github.com/stretchr/testify/require" 8 9 "github.com/onflow/flow-go/engine/execution/checker" 10 stateMock "github.com/onflow/flow-go/engine/execution/state/mock" 11 "github.com/onflow/flow-go/model/flow" 12 protocol "github.com/onflow/flow-go/state/protocol/mock" 13 "github.com/onflow/flow-go/storage" 14 "github.com/onflow/flow-go/utils/unittest" 15 ) 16 17 func makeCore(t *testing.T) (*checker.Core, *protocol.State, *stateMock.ExecutionState) { 18 logger := unittest.Logger() 19 state := protocol.NewState(t) 20 execState := stateMock.NewExecutionState(t) 21 core := checker.NewCore(logger, state, execState) 22 return core, state, execState 23 } 24 25 func mockFinalizedBlock(t *testing.T, state *protocol.State, finalized *flow.Header) *protocol.Snapshot { 26 finalizedSnapshot := protocol.NewSnapshot(t) 27 finalizedSnapshot.On("Head").Return(finalized, nil) 28 state.On("Final").Return(finalizedSnapshot) 29 return finalizedSnapshot 30 } 31 32 func mockAtBlockID(t *testing.T, state *protocol.State, header *flow.Header) *protocol.Snapshot { 33 snapshot := protocol.NewSnapshot(t) 34 snapshot.On("Head").Return(header, nil) 35 state.On("AtBlockID", header.ID()).Return(snapshot) 36 return snapshot 37 } 38 39 func mockSealedBlock(t *testing.T, state *protocol.State, finalized *protocol.Snapshot, sealed *flow.Header) (*flow.ExecutionResult, *flow.Seal) { 40 lastSealResult := unittest.ExecutionResultFixture(func(r *flow.ExecutionResult) { 41 r.BlockID = sealed.ID() 42 }) 43 lastSeal := unittest.Seal.Fixture(unittest.Seal.WithResult(lastSealResult)) 44 finalized.On("SealedResult").Return(lastSealResult, lastSeal, nil) 45 return lastSealResult, lastSeal 46 } 47 48 func mockFinalizedSealedBlock(t *testing.T, state *protocol.State, finalized *flow.Header, sealed *flow.Header) (*flow.ExecutionResult, *flow.Seal) { 49 finalizedSnapshot := mockFinalizedBlock(t, state, finalized) 50 return mockSealedBlock(t, state, finalizedSnapshot, sealed) 51 } 52 53 func mockSealedBlockAtHeight(t *testing.T, state *protocol.State, height uint64, lastSealed *flow.Header) (*flow.ExecutionResult, *flow.Seal) { 54 snapshotAtHeight := protocol.NewSnapshot(t) 55 lastSealedResultAtHeight := unittest.ExecutionResultFixture(func(r *flow.ExecutionResult) { 56 r.BlockID = lastSealed.ID() 57 }) 58 lastSealAtHeight := unittest.Seal.Fixture(unittest.Seal.WithResult(lastSealedResultAtHeight)) 59 snapshotAtHeight.On("SealedResult").Return(lastSealedResultAtHeight, lastSealAtHeight, nil) 60 state.On("AtHeight", height).Return(snapshotAtHeight, nil) 61 return lastSealedResultAtHeight, lastSealAtHeight 62 } 63 64 func mockExecutedBlock(t *testing.T, es *stateMock.ExecutionState, executed *flow.Header, result *flow.ExecutionResult) { 65 commit, err := result.FinalStateCommitment() 66 require.NoError(t, err) 67 es.On("StateCommitmentByBlockID", executed.ID()).Return(commit, nil) 68 } 69 70 func mockUnexecutedBlock(t *testing.T, es *stateMock.ExecutionState, unexecuted *flow.Header) { 71 es.On("StateCommitmentByBlockID", unexecuted.ID()).Return(nil, storage.ErrNotFound) 72 } 73 74 func TestCheckPassIfLastSealedIsExecutedAndMatch(t *testing.T) { 75 // ..<- LastSealed(executed) <- .. <- LastFinalized <- .. <- LastExecuted <- ... 76 chain, _, _ := unittest.ChainFixture(10) 77 lastFinal := chain[7].Header 78 lastSealed := chain[5].Header 79 80 core, state, es := makeCore(t) 81 lastSealedResult, _ := mockFinalizedSealedBlock(t, state, lastFinal, lastSealed) 82 mockAtBlockID(t, state, lastSealed) 83 mockExecutedBlock(t, es, lastSealed, lastSealedResult) 84 85 require.NoError(t, core.RunCheck()) 86 } 87 88 func TestCheckFailIfLastSealedIsExecutedButMismatch(t *testing.T) { 89 // ..<- LastSealed(executed) <- .. <- LastFinalized <- .. <- LastExecuted <- ... 90 chain, _, _ := unittest.ChainFixture(10) 91 lastFinal := chain[7].Header 92 lastSealed := chain[5].Header 93 94 core, state, es := makeCore(t) 95 _, _ = mockFinalizedSealedBlock(t, state, lastFinal, lastSealed) 96 mockAtBlockID(t, state, lastSealed) 97 98 mismatchingResult := unittest.ExecutionResultFixture() 99 100 mockExecutedBlock(t, es, lastSealed, mismatchingResult) 101 102 require.Error(t, core.RunCheck()) 103 require.Contains(t, core.RunCheck().Error(), "execution result is different from the sealed result") 104 } 105 106 func TestCheckPassIfLastSealedIsNotExecutedAndLastExecutedMatch(t *testing.T) { 107 // LastSealedExecuted (sealed) <..<- LastExecuted(finalized) <..<- LastSealed(not executed) <..<- LastFinalized 108 chain, _, _ := unittest.ChainFixture(10) 109 lastFinal := chain[7].Header 110 lastSealed := chain[5].Header 111 lastExecuted := chain[3].Header 112 lastSealedExecuted := chain[1].Header 113 114 core, state, es := makeCore(t) 115 // mock that last sealed is not executed 116 mockFinalizedSealedBlock(t, state, lastFinal, lastSealed) 117 mockAtBlockID(t, state, lastSealed) 118 mockUnexecutedBlock(t, es, lastSealed) 119 120 // mock the last sealed and is also executed 121 es.On("GetHighestExecutedBlockID", mock.Anything).Return(lastExecuted.Height, lastExecuted.ID(), nil) 122 lastSealedResultAtExecutedHeight, _ := mockSealedBlockAtHeight(t, state, lastExecuted.Height, lastSealedExecuted) 123 mockAtBlockID(t, state, lastSealedExecuted) 124 125 // mock with matching result 126 mockExecutedBlock(t, es, lastSealedExecuted, lastSealedResultAtExecutedHeight) 127 128 require.NoError(t, core.RunCheck()) 129 } 130 131 func TestCheckFailIfLastSealedIsNotExecutedAndLastExecutedMismatch(t *testing.T) { 132 // LastSealedExecuted (sealed) <..<- LastExecuted(finalized) <..<- LastSealed(not executed) <..<- LastFinalized 133 chain, _, _ := unittest.ChainFixture(10) 134 lastFinal := chain[7].Header 135 lastSealed := chain[5].Header 136 lastExecuted := chain[3].Header 137 lastSealedExecuted := chain[1].Header 138 139 core, state, es := makeCore(t) 140 // mock that last sealed is not executed 141 mockFinalizedSealedBlock(t, state, lastFinal, lastSealed) 142 mockAtBlockID(t, state, lastSealed) 143 mockUnexecutedBlock(t, es, lastSealed) 144 145 // mock the last sealed and is also executed 146 es.On("GetHighestExecutedBlockID", mock.Anything).Return(lastExecuted.Height, lastExecuted.ID(), nil) 147 mockSealedBlockAtHeight(t, state, lastExecuted.Height, lastSealedExecuted) 148 mockAtBlockID(t, state, lastSealedExecuted) 149 150 // mock with mismatching result 151 mismatchingResult := unittest.ExecutionResultFixture() 152 mockExecutedBlock(t, es, lastSealedExecuted, mismatchingResult) 153 154 require.Error(t, core.RunCheck()) 155 require.Contains(t, core.RunCheck().Error(), "execution result is different from the sealed result") 156 }