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  }