github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/chunks/chunkVerifier_test.go (about)

     1  package chunks_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/ipfs/go-cid"
     7  	"github.com/onflow/cadence/runtime"
     8  	"github.com/rs/zerolog"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/mock"
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/stretchr/testify/suite"
    13  
    14  	executionState "github.com/onflow/flow-go/engine/execution/state"
    15  	"github.com/onflow/flow-go/fvm"
    16  	"github.com/onflow/flow-go/fvm/blueprints"
    17  	fvmErrors "github.com/onflow/flow-go/fvm/errors"
    18  	fvmmock "github.com/onflow/flow-go/fvm/mock"
    19  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    20  	"github.com/onflow/flow-go/ledger"
    21  	completeLedger "github.com/onflow/flow-go/ledger/complete"
    22  	"github.com/onflow/flow-go/ledger/complete/wal/fixtures"
    23  	chunksmodels "github.com/onflow/flow-go/model/chunks"
    24  	"github.com/onflow/flow-go/model/convert"
    25  	"github.com/onflow/flow-go/model/flow"
    26  	"github.com/onflow/flow-go/model/verification"
    27  	"github.com/onflow/flow-go/module/chunks"
    28  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    29  	"github.com/onflow/flow-go/module/executiondatasync/provider"
    30  	"github.com/onflow/flow-go/module/metrics"
    31  	"github.com/onflow/flow-go/utils/unittest"
    32  )
    33  
    34  var eventsList = flow.EventsList{
    35  	{
    36  		Type:             "event.someType",
    37  		TransactionID:    flow.Identifier{2, 3, 2, 3},
    38  		TransactionIndex: 1,
    39  		EventIndex:       2,
    40  		Payload:          []byte{7, 3, 1, 2},
    41  	},
    42  	{
    43  		Type:             "event.otherType",
    44  		TransactionID:    flow.Identifier{3, 3, 3},
    45  		TransactionIndex: 4,
    46  		EventIndex:       4,
    47  		Payload:          []byte{7, 3, 1, 2},
    48  	},
    49  }
    50  
    51  const computationUsed = uint64(100)
    52  
    53  var id0 = flow.NewRegisterID(unittest.RandomAddressFixture(), "")
    54  var id5 = flow.NewRegisterID(unittest.RandomAddressFixture(), "")
    55  
    56  // the chain we use for this test suite
    57  var testChain = flow.Emulator
    58  var epochSetupEvent, _ = unittest.EpochSetupFixtureByChainID(testChain)
    59  var epochCommitEvent, _ = unittest.EpochCommitFixtureByChainID(testChain)
    60  
    61  var systemEventsList = []flow.Event{
    62  	epochSetupEvent,
    63  }
    64  
    65  var executionDataCIDProvider = provider.NewExecutionDataCIDProvider(execution_data.DefaultSerializer)
    66  
    67  var serviceTxBody *flow.TransactionBody
    68  
    69  type ChunkVerifierTestSuite struct {
    70  	suite.Suite
    71  
    72  	verifier *chunks.ChunkVerifier
    73  	ledger   *completeLedger.Ledger
    74  
    75  	snapshots map[string]*snapshot.ExecutionSnapshot
    76  	outputs   map[string]fvm.ProcedureOutput
    77  }
    78  
    79  // Make sure variables are set properly
    80  // SetupTest is executed prior to each individual test in this test suite
    81  func (s *ChunkVerifierTestSuite) SetupSuite() {
    82  	vmCtx := fvm.NewContext(fvm.WithChain(testChain.Chain()))
    83  	vmMock := fvmmock.NewVM(s.T())
    84  
    85  	vmMock.
    86  		On("Run",
    87  			mock.AnythingOfType("fvm.Context"),
    88  			mock.AnythingOfType("*fvm.TransactionProcedure"),
    89  			mock.AnythingOfType("snapshot.SnapshotTree")).
    90  		Return(
    91  			func(ctx fvm.Context, proc fvm.Procedure, storage snapshot.StorageSnapshot) *snapshot.ExecutionSnapshot {
    92  				tx, ok := proc.(*fvm.TransactionProcedure)
    93  				if !ok {
    94  					s.Fail("unexpected procedure type")
    95  					return nil
    96  				}
    97  
    98  				if snapshot, ok := s.snapshots[string(tx.Transaction.Script)]; ok {
    99  					return snapshot
   100  				}
   101  				return generateDefaultSnapshot()
   102  			},
   103  			func(ctx fvm.Context, proc fvm.Procedure, storage snapshot.StorageSnapshot) fvm.ProcedureOutput {
   104  				tx, ok := proc.(*fvm.TransactionProcedure)
   105  				if !ok {
   106  					s.Fail("unexpected procedure type")
   107  					return fvm.ProcedureOutput{}
   108  				}
   109  
   110  				if output, ok := s.outputs[string(tx.Transaction.Script)]; ok {
   111  					return output
   112  				}
   113  				return generateDefaultOutput()
   114  			},
   115  			func(ctx fvm.Context, proc fvm.Procedure, storage snapshot.StorageSnapshot) error {
   116  				return nil
   117  			},
   118  		).
   119  		Maybe() // don't require for all tests since some never call FVM
   120  
   121  	s.verifier = chunks.NewChunkVerifier(vmMock, vmCtx, zerolog.Nop())
   122  
   123  	txBody, err := blueprints.SystemChunkTransaction(testChain.Chain())
   124  	require.NoError(s.T(), err)
   125  	serviceTxBody = txBody
   126  }
   127  
   128  func (s *ChunkVerifierTestSuite) SetupTest() {
   129  	s.ledger = newLedger(s.T())
   130  
   131  	s.snapshots = make(map[string]*snapshot.ExecutionSnapshot)
   132  	s.outputs = make(map[string]fvm.ProcedureOutput)
   133  }
   134  
   135  // TestChunkVerifier invokes all the tests in this test suite
   136  func TestChunkVerifier(t *testing.T) {
   137  	suite.Run(t, new(ChunkVerifierTestSuite))
   138  }
   139  
   140  // TestHappyPath tests verification of the baseline verifiable chunk
   141  func (s *ChunkVerifierTestSuite) TestHappyPath() {
   142  	meta := s.GetTestSetup(s.T(), "", false)
   143  	vch := meta.RefreshChunkData(s.T())
   144  
   145  	spockSecret, err := s.verifier.Verify(vch)
   146  	assert.NoError(s.T(), err)
   147  	assert.NotNil(s.T(), spockSecret)
   148  }
   149  
   150  // TestMissingRegisterTouchForUpdate tests verification given a chunkdatapack missing a register touch (update)
   151  func (s *ChunkVerifierTestSuite) TestMissingRegisterTouchForUpdate() {
   152  	unittest.SkipUnless(s.T(), unittest.TEST_DEPRECATED, "Check new partial ledger for missing keys")
   153  
   154  	meta := s.GetTestSetup(s.T(), "", false)
   155  	vch := meta.RefreshChunkData(s.T())
   156  
   157  	// remove the second register touch
   158  	// vch.ChunkDataPack.RegisterTouches = vch.ChunkDataPack.RegisterTouches[:1]
   159  	spockSecret, err := s.verifier.Verify(vch)
   160  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   161  	assert.IsType(s.T(), &chunksmodels.CFMissingRegisterTouch{}, err)
   162  	assert.Nil(s.T(), spockSecret)
   163  }
   164  
   165  // TestMissingRegisterTouchForRead tests verification given a chunkdatapack missing a register touch (read)
   166  func (s *ChunkVerifierTestSuite) TestMissingRegisterTouchForRead() {
   167  	unittest.SkipUnless(s.T(), unittest.TEST_DEPRECATED, "Check new partial ledger for missing keys")
   168  
   169  	meta := s.GetTestSetup(s.T(), "", false)
   170  	vch := meta.RefreshChunkData(s.T())
   171  
   172  	// remove the second register touch
   173  	// vch.ChunkDataPack.RegisterTouches = vch.ChunkDataPack.RegisterTouches[1:]
   174  	spockSecret, err := s.verifier.Verify(vch)
   175  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   176  	assert.IsType(s.T(), &chunksmodels.CFMissingRegisterTouch{}, err)
   177  	assert.Nil(s.T(), spockSecret)
   178  }
   179  
   180  // TestWrongEndState tests verification covering the case
   181  // the state commitment computed after updating the partial trie
   182  // doesn't match the one provided by the chunks
   183  func (s *ChunkVerifierTestSuite) TestWrongEndState() {
   184  	meta := s.GetTestSetup(s.T(), "wrongEndState", false)
   185  	vch := meta.RefreshChunkData(s.T())
   186  
   187  	// modify calculated end state, which is different from the one provided by the vch
   188  	s.snapshots["wrongEndState"] = &snapshot.ExecutionSnapshot{
   189  		WriteSet: map[flow.RegisterID]flow.RegisterValue{
   190  			id0: []byte{'F'},
   191  		},
   192  	}
   193  
   194  	spockSecret, err := s.verifier.Verify(vch)
   195  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   196  	assert.IsType(s.T(), &chunksmodels.CFNonMatchingFinalState{}, err)
   197  	assert.Nil(s.T(), spockSecret)
   198  }
   199  
   200  // TestFailedTx tests verification behavior in case
   201  // of failed transaction. if a transaction fails, it should
   202  // still change the state commitment.
   203  func (s *ChunkVerifierTestSuite) TestFailedTx() {
   204  	meta := s.GetTestSetup(s.T(), "failedTx", false)
   205  	vch := meta.RefreshChunkData(s.T())
   206  
   207  	// modify the FVM output to include a failing tx. the input already has a failing tx, but we need to
   208  	s.snapshots["failedTx"] = &snapshot.ExecutionSnapshot{
   209  		WriteSet: map[flow.RegisterID]flow.RegisterValue{
   210  			id5: []byte{'B'},
   211  		},
   212  	}
   213  	s.outputs["failedTx"] = fvm.ProcedureOutput{
   214  		ComputationUsed: computationUsed,
   215  		Err:             fvmErrors.NewCadenceRuntimeError(runtime.Error{}), // inside the runtime (e.g. div by zero, access account)
   216  	}
   217  
   218  	spockSecret, err := s.verifier.Verify(vch)
   219  	assert.NoError(s.T(), err)
   220  	assert.NotNil(s.T(), spockSecret)
   221  }
   222  
   223  // TestEventsMismatch tests verification behavior in case
   224  // of emitted events not matching chunks
   225  func (s *ChunkVerifierTestSuite) TestEventsMismatch() {
   226  	meta := s.GetTestSetup(s.T(), "eventsMismatch", false)
   227  	vch := meta.RefreshChunkData(s.T())
   228  
   229  	// add an additional event to the list of events produced by FVM
   230  	output := generateDefaultOutput()
   231  	output.Events = append(eventsList, flow.Event{
   232  		Type:             "event.Extra",
   233  		TransactionID:    flow.Identifier{2, 3},
   234  		TransactionIndex: 0,
   235  		EventIndex:       0,
   236  		Payload:          []byte{88},
   237  	})
   238  	s.outputs["eventsMismatch"] = output
   239  
   240  	_, err := s.verifier.Verify(vch)
   241  	assert.Error(s.T(), err)
   242  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   243  	assert.IsType(s.T(), &chunksmodels.CFInvalidEventsCollection{}, err)
   244  }
   245  
   246  // TestServiceEventsMismatch tests verification behavior in case
   247  // of emitted service events not matching chunks'
   248  func (s *ChunkVerifierTestSuite) TestServiceEventsMismatch() {
   249  	meta := s.GetTestSetup(s.T(), "doesn't matter", true)
   250  	vch := meta.RefreshChunkData(s.T())
   251  
   252  	// modify the list of service events produced by FVM
   253  	// EpochSetup event is expected, but we emit EpochCommit here resulting in a chunk fault
   254  	epochCommitServiceEvent, err := convert.ServiceEvent(testChain, epochCommitEvent)
   255  	require.NoError(s.T(), err)
   256  
   257  	s.snapshots[string(serviceTxBody.Script)] = &snapshot.ExecutionSnapshot{}
   258  	s.outputs[string(serviceTxBody.Script)] = fvm.ProcedureOutput{
   259  		ComputationUsed:        computationUsed,
   260  		ConvertedServiceEvents: flow.ServiceEventList{*epochCommitServiceEvent},
   261  		Events:                 meta.ChunkEvents,
   262  	}
   263  
   264  	_, err = s.verifier.Verify(vch)
   265  	assert.Error(s.T(), err)
   266  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   267  	assert.IsType(s.T(), &chunksmodels.CFInvalidServiceEventsEmitted{}, err)
   268  }
   269  
   270  // TestServiceEventsAreChecked ensures that service events are in fact checked
   271  func (s *ChunkVerifierTestSuite) TestServiceEventsAreChecked() {
   272  	meta := s.GetTestSetup(s.T(), "doesn't matter", true)
   273  	vch := meta.RefreshChunkData(s.T())
   274  
   275  	// setup the verifier output to include the correct data for the service events
   276  	output := generateDefaultOutput()
   277  	output.ConvertedServiceEvents = meta.ServiceEvents
   278  	output.Events = meta.ChunkEvents
   279  	s.outputs[string(serviceTxBody.Script)] = output
   280  
   281  	_, err := s.verifier.Verify(vch)
   282  	assert.NoError(s.T(), err)
   283  }
   284  
   285  // TestSystemChunkWithCollectionFails ensures verification fails for system chunks with collections
   286  func (s *ChunkVerifierTestSuite) TestSystemChunkWithCollectionFails() {
   287  	meta := s.GetTestSetup(s.T(), "doesn't matter", true)
   288  
   289  	// add a collection to the system chunk
   290  	col := unittest.CollectionFixture(1)
   291  	meta.Collection = &col
   292  
   293  	vch := meta.RefreshChunkData(s.T())
   294  
   295  	_, err := s.verifier.Verify(vch)
   296  	assert.Error(s.T(), err)
   297  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   298  	assert.IsType(s.T(), &chunksmodels.CFSystemChunkIncludedCollection{}, err)
   299  }
   300  
   301  // TestEmptyCollection tests verification behaviour if a
   302  // collection doesn't have any transaction.
   303  func (s *ChunkVerifierTestSuite) TestEmptyCollection() {
   304  	meta := s.GetTestSetup(s.T(), "", false)
   305  
   306  	// reset test to use an empty collection
   307  	collection := unittest.CollectionFixture(0)
   308  	meta.Collection = &collection
   309  	meta.ChunkEvents = nil
   310  	meta.TxResults = nil
   311  
   312  	// update the Update to not change the state
   313  	update, err := ledger.NewEmptyUpdate(meta.StartState)
   314  	require.NoError(s.T(), err)
   315  
   316  	meta.Update = update
   317  
   318  	vch := meta.RefreshChunkData(s.T())
   319  
   320  	spockSecret, err := s.verifier.Verify(vch)
   321  	assert.NoError(s.T(), err)
   322  	assert.NotNil(s.T(), spockSecret)
   323  }
   324  
   325  func (s *ChunkVerifierTestSuite) TestExecutionDataBlockMismatch() {
   326  	meta := s.GetTestSetup(s.T(), "", false)
   327  
   328  	// modify Block in the ExecutionDataRoot
   329  	meta.ExecDataBlockID = unittest.IdentifierFixture()
   330  
   331  	vch := meta.RefreshChunkData(s.T())
   332  
   333  	_, err := s.verifier.Verify(vch)
   334  	assert.Error(s.T(), err)
   335  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   336  	assert.IsType(s.T(), &chunksmodels.CFExecutionDataBlockIDMismatch{}, err)
   337  }
   338  
   339  func (s *ChunkVerifierTestSuite) TestExecutionDataChunkIdsLengthDiffers() {
   340  	meta := s.GetTestSetup(s.T(), "", false)
   341  	vch := meta.RefreshChunkData(s.T())
   342  
   343  	// add an additional ChunkExecutionDataID into the ExecutionDataRoot passed into Verify
   344  	vch.ChunkDataPack.ExecutionDataRoot.ChunkExecutionDataIDs = append(vch.ChunkDataPack.ExecutionDataRoot.ChunkExecutionDataIDs, cid.Undef)
   345  
   346  	_, err := s.verifier.Verify(vch)
   347  	assert.Error(s.T(), err)
   348  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   349  	assert.IsType(s.T(), &chunksmodels.CFExecutionDataChunksLengthMismatch{}, err)
   350  }
   351  
   352  func (s *ChunkVerifierTestSuite) TestExecutionDataChunkIdMismatch() {
   353  	meta := s.GetTestSetup(s.T(), "", false)
   354  	vch := meta.RefreshChunkData(s.T())
   355  
   356  	// modify one of the ChunkExecutionDataIDs passed into Verify
   357  	vch.ChunkDataPack.ExecutionDataRoot.ChunkExecutionDataIDs[0] = cid.Undef // substitute invalid CID
   358  
   359  	_, err := s.verifier.Verify(vch)
   360  	assert.Error(s.T(), err)
   361  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   362  	assert.IsType(s.T(), &chunksmodels.CFExecutionDataInvalidChunkCID{}, err)
   363  }
   364  
   365  func (s *ChunkVerifierTestSuite) TestExecutionDataIdMismatch() {
   366  	meta := s.GetTestSetup(s.T(), "", false)
   367  	vch := meta.RefreshChunkData(s.T())
   368  
   369  	// modify ExecutionDataID passed into Verify
   370  	vch.Result.ExecutionDataID[5]++
   371  
   372  	_, err := s.verifier.Verify(vch)
   373  	assert.Error(s.T(), err)
   374  	assert.True(s.T(), chunksmodels.IsChunkFaultError(err))
   375  	assert.IsType(s.T(), &chunksmodels.CFInvalidExecutionDataID{}, err)
   376  }
   377  
   378  func newLedger(t *testing.T) *completeLedger.Ledger {
   379  	f, err := completeLedger.NewLedger(&fixtures.NoopWAL{}, 1000, metrics.NewNoopCollector(), zerolog.Nop(), completeLedger.DefaultPathFinderVersion)
   380  	require.NoError(t, err)
   381  
   382  	compactor := fixtures.NewNoopCompactor(f)
   383  	<-compactor.Ready()
   384  
   385  	t.Cleanup(func() {
   386  		<-f.Done()
   387  		<-compactor.Done()
   388  	})
   389  
   390  	return f
   391  }
   392  
   393  func blockFixture(collection *flow.Collection) *flow.Block {
   394  	guarantee := collection.Guarantee()
   395  	block := &flow.Block{
   396  		Header: unittest.BlockHeaderFixture(),
   397  		Payload: &flow.Payload{
   398  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
   399  		},
   400  	}
   401  	block.Header.PayloadHash = block.Payload.Hash()
   402  	return block
   403  }
   404  
   405  func generateStateUpdates(t *testing.T, f *completeLedger.Ledger) (ledger.State, ledger.Proof, *ledger.Update) {
   406  	entries := flow.RegisterEntries{
   407  		{
   408  			Key:   id0,
   409  			Value: []byte{'a'},
   410  		},
   411  		{
   412  			Key:   id5,
   413  			Value: []byte{'b'},
   414  		},
   415  	}
   416  
   417  	keys, values := executionState.RegisterEntriesToKeysValues(entries)
   418  	update, err := ledger.NewUpdate(f.InitialState(), keys, values)
   419  	require.NoError(t, err)
   420  
   421  	startState, _, err := f.Set(update)
   422  	require.NoError(t, err)
   423  
   424  	query, err := ledger.NewQuery(startState, keys)
   425  	require.NoError(t, err)
   426  
   427  	proof, err := f.Prove(query)
   428  	require.NoError(t, err)
   429  
   430  	entries = flow.RegisterEntries{
   431  		{
   432  			Key:   id5,
   433  			Value: []byte{'B'},
   434  		},
   435  	}
   436  
   437  	keys, values = executionState.RegisterEntriesToKeysValues(entries)
   438  	update, err = ledger.NewUpdate(startState, keys, values)
   439  	require.NoError(t, err)
   440  
   441  	return startState, proof, update
   442  }
   443  
   444  func generateExecutionData(t *testing.T, blockID flow.Identifier, ced *execution_data.ChunkExecutionData) (flow.Identifier, flow.BlockExecutionDataRoot) {
   445  	chunkCid, err := executionDataCIDProvider.CalculateChunkExecutionDataID(*ced)
   446  	require.NoError(t, err)
   447  
   448  	executionDataRoot := flow.BlockExecutionDataRoot{
   449  		BlockID:               blockID,
   450  		ChunkExecutionDataIDs: []cid.Cid{chunkCid},
   451  	}
   452  
   453  	executionDataID, err := executionDataCIDProvider.CalculateExecutionDataRootID(executionDataRoot)
   454  	require.NoError(t, err)
   455  
   456  	return executionDataID, executionDataRoot
   457  }
   458  
   459  func generateEvents(t *testing.T, isSystemChunk bool, collection *flow.Collection) (flow.EventsList, []flow.ServiceEvent) {
   460  	var chunkEvents flow.EventsList
   461  	serviceEvents := make([]flow.ServiceEvent, 0)
   462  
   463  	// service events are also included as regular events
   464  	if isSystemChunk {
   465  		for _, e := range systemEventsList {
   466  			e := e
   467  			event, err := convert.ServiceEvent(testChain, e)
   468  			require.NoError(t, err)
   469  
   470  			serviceEvents = append(serviceEvents, *event)
   471  			chunkEvents = append(chunkEvents, e)
   472  		}
   473  	}
   474  
   475  	for _, coll := range collection.Transactions {
   476  		switch string(coll.Script) {
   477  		case "failedTx":
   478  			continue
   479  		}
   480  		chunkEvents = append(chunkEvents, eventsList...)
   481  	}
   482  
   483  	return chunkEvents, serviceEvents
   484  }
   485  
   486  func generateTransactionResults(t *testing.T, collection *flow.Collection) []flow.LightTransactionResult {
   487  	txResults := make([]flow.LightTransactionResult, len(collection.Transactions))
   488  	for i, tx := range collection.Transactions {
   489  		txResults[i] = flow.LightTransactionResult{
   490  			TransactionID:   tx.ID(),
   491  			ComputationUsed: computationUsed,
   492  			Failed:          false,
   493  		}
   494  
   495  		if string(tx.Script) == "failedTx" {
   496  			txResults[i].Failed = true
   497  		}
   498  	}
   499  
   500  	return txResults
   501  }
   502  
   503  func generateCollection(t *testing.T, isSystemChunk bool, script string) *flow.Collection {
   504  	if isSystemChunk {
   505  		// the system chunk's data pack does not include the collection, but the execution data does.
   506  		// we must include the correct collection in the execution data, otherwise verification will fail.
   507  		return &flow.Collection{
   508  			Transactions: []*flow.TransactionBody{serviceTxBody},
   509  		}
   510  	}
   511  
   512  	collectionSize := 5
   513  	magicTxIndex := 3
   514  
   515  	coll := unittest.CollectionFixture(collectionSize)
   516  	if script != "" {
   517  		coll.Transactions[magicTxIndex] = &flow.TransactionBody{Script: []byte(script)}
   518  	}
   519  
   520  	return &coll
   521  }
   522  
   523  func generateDefaultSnapshot() *snapshot.ExecutionSnapshot {
   524  	return &snapshot.ExecutionSnapshot{
   525  		ReadSet: map[flow.RegisterID]struct{}{
   526  			id0: {},
   527  			id5: {},
   528  		},
   529  		WriteSet: map[flow.RegisterID]flow.RegisterValue{
   530  			id5: []byte{'B'},
   531  		},
   532  	}
   533  }
   534  
   535  func generateDefaultOutput() fvm.ProcedureOutput {
   536  	return fvm.ProcedureOutput{
   537  		ComputationUsed: computationUsed,
   538  		Logs:            []string{"log1", "log2"},
   539  		Events:          eventsList,
   540  	}
   541  }
   542  
   543  func (s *ChunkVerifierTestSuite) GetTestSetup(t *testing.T, script string, system bool) *testMetadata {
   544  	collection := generateCollection(t, system, script)
   545  	block := blockFixture(collection)
   546  
   547  	// transaction results
   548  	txResults := generateTransactionResults(t, collection)
   549  	// make sure this includes results even for the service tx
   550  	if system {
   551  		require.Len(t, txResults, 1)
   552  	} else {
   553  		require.Len(t, txResults, len(collection.Transactions))
   554  	}
   555  
   556  	// events
   557  	chunkEvents, serviceEvents := generateEvents(t, system, collection)
   558  	// make sure this includes events even for the service tx
   559  	require.NotEmpty(t, chunkEvents)
   560  	if system {
   561  		require.Len(t, serviceEvents, 1)
   562  	} else {
   563  		require.Empty(t, serviceEvents)
   564  	}
   565  
   566  	// registerTouch and State setup
   567  	startState, proof, update := generateStateUpdates(t, s.ledger)
   568  
   569  	if system {
   570  		collection = nil
   571  	}
   572  
   573  	meta := &testMetadata{
   574  		IsSystemChunk: system,
   575  		Header:        block.Header,
   576  		Collection:    collection,
   577  		TxResults:     txResults,
   578  		ChunkEvents:   chunkEvents,
   579  		ServiceEvents: serviceEvents,
   580  		StartState:    startState,
   581  		Update:        update,
   582  		Proof:         proof,
   583  
   584  		ExecDataBlockID: block.Header.ID(),
   585  
   586  		ledger: s.ledger,
   587  	}
   588  
   589  	return meta
   590  }
   591  
   592  type testMetadata struct {
   593  	IsSystemChunk bool
   594  	Header        *flow.Header
   595  	Collection    *flow.Collection
   596  	TxResults     []flow.LightTransactionResult
   597  	ChunkEvents   flow.EventsList
   598  	ServiceEvents []flow.ServiceEvent
   599  	StartState    ledger.State
   600  	Update        *ledger.Update
   601  	Proof         ledger.Proof
   602  
   603  	// separated to allow overriding
   604  	ExecDataBlockID flow.Identifier
   605  
   606  	ledger *completeLedger.Ledger
   607  }
   608  
   609  func (m *testMetadata) RefreshChunkData(t *testing.T) *verification.VerifiableChunkData {
   610  	cedCollection := m.Collection
   611  
   612  	if m.IsSystemChunk {
   613  		// the system chunk's data pack does not include the collection, but the execution data does.
   614  		// we must include the correct collection in the execution data, otherwise verification will fail.
   615  		cedCollection = &flow.Collection{
   616  			Transactions: []*flow.TransactionBody{serviceTxBody},
   617  		}
   618  	}
   619  
   620  	endState, trieUpdate, err := m.ledger.Set(m.Update)
   621  	require.NoError(t, err)
   622  
   623  	eventsMerkleRootHash, err := flow.EventsMerkleRootHash(m.ChunkEvents)
   624  	require.NoError(t, err)
   625  
   626  	chunkExecutionData := &execution_data.ChunkExecutionData{
   627  		Collection:         cedCollection,
   628  		Events:             m.ChunkEvents,
   629  		TrieUpdate:         trieUpdate,
   630  		TransactionResults: m.TxResults,
   631  	}
   632  
   633  	executionDataID, executionDataRoot := generateExecutionData(t, m.ExecDataBlockID, chunkExecutionData)
   634  
   635  	// Chunk setup
   636  	chunk := &flow.Chunk{
   637  		ChunkBody: flow.ChunkBody{
   638  			CollectionIndex: 0,
   639  			StartState:      flow.StateCommitment(m.StartState),
   640  			BlockID:         m.Header.ID(),
   641  			EventCollection: eventsMerkleRootHash,
   642  		},
   643  		Index: 0,
   644  	}
   645  
   646  	chunkDataPack := &flow.ChunkDataPack{
   647  		ChunkID:           chunk.ID(),
   648  		StartState:        flow.StateCommitment(m.StartState),
   649  		Proof:             m.Proof,
   650  		Collection:        m.Collection,
   651  		ExecutionDataRoot: executionDataRoot,
   652  	}
   653  
   654  	// ExecutionResult setup
   655  	result := &flow.ExecutionResult{
   656  		BlockID:         m.Header.ID(),
   657  		Chunks:          flow.ChunkList{chunk},
   658  		ServiceEvents:   m.ServiceEvents,
   659  		ExecutionDataID: executionDataID,
   660  	}
   661  
   662  	return &verification.VerifiableChunkData{
   663  		IsSystemChunk: m.IsSystemChunk,
   664  		Header:        m.Header,
   665  		Chunk:         chunk,
   666  		Result:        result,
   667  		ChunkDataPack: chunkDataPack,
   668  		EndState:      flow.StateCommitment(endState),
   669  	}
   670  }