github.com/koko1123/flow-go-1@v0.29.6/engine/execution/computation/programs_test.go (about)

     1  package computation
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ipfs/go-datastore"
    10  	dssync "github.com/ipfs/go-datastore/sync"
    11  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    12  	"github.com/onflow/cadence"
    13  	jsoncdc "github.com/onflow/cadence/encoding/json"
    14  	"github.com/rs/zerolog"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/mock"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/koko1123/flow-go-1/engine/execution"
    20  	"github.com/koko1123/flow-go-1/engine/execution/computation/committer"
    21  	"github.com/koko1123/flow-go-1/engine/execution/computation/computer"
    22  	"github.com/koko1123/flow-go-1/engine/execution/state/delta"
    23  	"github.com/koko1123/flow-go-1/engine/execution/testutil"
    24  	"github.com/koko1123/flow-go-1/fvm"
    25  	"github.com/koko1123/flow-go-1/fvm/derived"
    26  	"github.com/koko1123/flow-go-1/fvm/state"
    27  	"github.com/koko1123/flow-go-1/model/flow"
    28  	"github.com/koko1123/flow-go-1/module/executiondatasync/execution_data"
    29  	"github.com/koko1123/flow-go-1/module/executiondatasync/provider"
    30  	mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock"
    31  	"github.com/koko1123/flow-go-1/module/mempool/entity"
    32  	"github.com/koko1123/flow-go-1/module/metrics"
    33  	module "github.com/koko1123/flow-go-1/module/mock"
    34  	requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest"
    35  	"github.com/koko1123/flow-go-1/module/trace"
    36  	"github.com/koko1123/flow-go-1/utils/unittest"
    37  )
    38  
    39  func TestPrograms_TestContractUpdates(t *testing.T) {
    40  	chain := flow.Mainnet.Chain()
    41  	vm := fvm.NewVirtualMachine()
    42  	execCtx := fvm.NewContext(fvm.WithChain(chain))
    43  
    44  	privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
    45  	require.NoError(t, err)
    46  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
    47  	accounts, err := testutil.CreateAccounts(
    48  		vm,
    49  		ledger,
    50  		derived.NewEmptyDerivedBlockData(),
    51  		privateKeys,
    52  		chain)
    53  	require.NoError(t, err)
    54  
    55  	// setup transactions
    56  	account := accounts[0]
    57  	privKey := privateKeys[0]
    58  	// tx1 deploys contract version 1
    59  	tx1 := testutil.DeployEventContractTransaction(account, chain, 1)
    60  	prepareTx(t, tx1, account, privKey, 0, chain)
    61  
    62  	// tx2 calls the method of the contract (version 1)
    63  	tx2 := testutil.CreateEmitEventTransaction(account, account)
    64  	prepareTx(t, tx2, account, privKey, 1, chain)
    65  
    66  	// tx3 updates the contract to version 2
    67  	tx3 := testutil.UpdateEventContractTransaction(account, chain, 2)
    68  	prepareTx(t, tx3, account, privKey, 2, chain)
    69  
    70  	// tx4 calls the method of the contract (version 2)
    71  	tx4 := testutil.CreateEmitEventTransaction(account, account)
    72  	prepareTx(t, tx4, account, privKey, 3, chain)
    73  
    74  	// tx5 updates the contract to version 3 but fails (no env signature of service account)
    75  	tx5 := testutil.UnauthorizedDeployEventContractTransaction(account, chain, 3)
    76  	tx5.SetProposalKey(account, 0, 4).SetPayer(account)
    77  	err = testutil.SignEnvelope(tx5, account, privKey)
    78  	require.NoError(t, err)
    79  
    80  	// tx6 calls the method of the contract (version 2 expected)
    81  	tx6 := testutil.CreateEmitEventTransaction(account, account)
    82  	prepareTx(t, tx6, account, privKey, 5, chain)
    83  
    84  	transactions := []*flow.TransactionBody{tx1, tx2, tx3, tx4, tx5, tx6}
    85  
    86  	col := flow.Collection{Transactions: transactions}
    87  
    88  	guarantee := flow.CollectionGuarantee{
    89  		CollectionID: col.ID(),
    90  		Signature:    nil,
    91  	}
    92  
    93  	block := flow.Block{
    94  		Header: &flow.Header{
    95  			View: 26,
    96  		},
    97  		Payload: &flow.Payload{
    98  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
    99  		},
   100  	}
   101  
   102  	executableBlock := &entity.ExecutableBlock{
   103  		Block: &block,
   104  		CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{
   105  			guarantee.ID(): {
   106  				Guarantee:    &guarantee,
   107  				Transactions: transactions,
   108  			},
   109  		},
   110  		StartState: unittest.StateCommitmentPointerFixture(),
   111  	}
   112  
   113  	me := new(module.Local)
   114  	me.On("NodeID").Return(flow.ZeroID)
   115  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   116  		Return(nil, nil)
   117  
   118  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   119  	trackerStorage := mocktracker.NewMockStorage()
   120  
   121  	prov := provider.NewProvider(
   122  		zerolog.Nop(),
   123  		metrics.NewNoopCollector(),
   124  		execution_data.DefaultSerializer,
   125  		bservice,
   126  		trackerStorage,
   127  	)
   128  
   129  	blockComputer, err := computer.NewBlockComputer(
   130  		vm,
   131  		execCtx,
   132  		metrics.NewNoopCollector(),
   133  		trace.NewNoopTracer(),
   134  		zerolog.Nop(),
   135  		committer.NewNoopViewCommitter(),
   136  		me,
   137  		prov)
   138  	require.NoError(t, err)
   139  
   140  	derivedChainData, err := derived.NewDerivedChainData(10)
   141  	require.NoError(t, err)
   142  
   143  	engine := &Manager{
   144  		blockComputer:    blockComputer,
   145  		tracer:           trace.NewNoopTracer(),
   146  		me:               me,
   147  		derivedChainData: derivedChainData,
   148  	}
   149  
   150  	view := delta.NewView(ledger.Get)
   151  	blockView := view.NewChild()
   152  
   153  	returnedComputationResult, err := engine.ComputeBlock(context.Background(), executableBlock, blockView)
   154  	require.NoError(t, err)
   155  
   156  	require.Len(t, returnedComputationResult.Events, 2) // 1 collection + 1 system chunk
   157  
   158  	// first event should be contract deployed
   159  	assert.EqualValues(t, "flow.AccountContractAdded", returnedComputationResult.Events[0][0].Type)
   160  
   161  	// second event should have a value of 1 (since is calling version 1 of contract)
   162  	hasValidEventValue(t, returnedComputationResult.Events[0][1], 1)
   163  
   164  	// third event should be contract updated
   165  	assert.EqualValues(t, "flow.AccountContractUpdated", returnedComputationResult.Events[0][2].Type)
   166  
   167  	// 4th event should have a value of 2 (since is calling version 2 of contract)
   168  	hasValidEventValue(t, returnedComputationResult.Events[0][3], 2)
   169  
   170  	// 5th event should have a value of 2 (since is calling version 2 of contract)
   171  	hasValidEventValue(t, returnedComputationResult.Events[0][4], 2)
   172  }
   173  
   174  type blockProvider struct {
   175  	blocks map[uint64]*flow.Block
   176  }
   177  
   178  func (b blockProvider) ByHeightFrom(height uint64, _ *flow.Header) (*flow.Header, error) {
   179  	block, has := b.blocks[height]
   180  	if has {
   181  		return block.Header, nil
   182  	}
   183  	return nil, fmt.Errorf("block for height (%d) is not available", height)
   184  }
   185  
   186  // TestPrograms_TestBlockForks tests the functionality of
   187  // derivedChainData under contract deployment and contract updates on
   188  // different block forks
   189  //
   190  // block structure and operations
   191  // Block1 (empty block)
   192  //
   193  //	    -> Block11 (deploy contract v1)
   194  //	        -> Block111  (emit event - version should be 1) and (update contract to v3)
   195  //	            -> Block1111   (emit event - version should be 3)
   196  //		       -> Block112 (emit event - version should be 1) and (update contract to v4)
   197  //	            -> Block1121  (emit event - version should be 4)
   198  //	    -> Block12 (deploy contract v2)
   199  //	        -> Block121 (emit event - version should be 2)
   200  //	            -> Block1211 (emit event - version should be 2)
   201  func TestPrograms_TestBlockForks(t *testing.T) {
   202  	block := unittest.BlockFixture()
   203  	chain := flow.Emulator.Chain()
   204  	vm := fvm.NewVirtualMachine()
   205  	execCtx := fvm.NewContext(
   206  		fvm.WithBlockHeader(block.Header),
   207  		fvm.WithBlocks(blockProvider{map[uint64]*flow.Block{0: &block}}),
   208  		fvm.WithChain(chain))
   209  
   210  	privateKeys, err := testutil.GenerateAccountPrivateKeys(1)
   211  	require.NoError(t, err)
   212  	ledger := testutil.RootBootstrappedLedger(vm, execCtx)
   213  	accounts, err := testutil.CreateAccounts(
   214  		vm,
   215  		ledger,
   216  		derived.NewEmptyDerivedBlockData(),
   217  		privateKeys,
   218  		chain)
   219  	require.NoError(t, err)
   220  
   221  	account := accounts[0]
   222  	privKey := privateKeys[0]
   223  
   224  	me := new(module.Local)
   225  	me.On("NodeID").Return(flow.ZeroID)
   226  	me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything).
   227  		Return(nil, nil)
   228  
   229  	bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())))
   230  	trackerStorage := mocktracker.NewMockStorage()
   231  
   232  	prov := provider.NewProvider(
   233  		zerolog.Nop(),
   234  		metrics.NewNoopCollector(),
   235  		execution_data.DefaultSerializer,
   236  		bservice,
   237  		trackerStorage,
   238  	)
   239  
   240  	blockComputer, err := computer.NewBlockComputer(
   241  		vm,
   242  		execCtx,
   243  		metrics.NewNoopCollector(),
   244  		trace.NewNoopTracer(),
   245  		zerolog.Nop(),
   246  		committer.NewNoopViewCommitter(),
   247  		me,
   248  		prov)
   249  	require.NoError(t, err)
   250  
   251  	derivedChainData, err := derived.NewDerivedChainData(10)
   252  	require.NoError(t, err)
   253  
   254  	engine := &Manager{
   255  		blockComputer:    blockComputer,
   256  		tracer:           trace.NewNoopTracer(),
   257  		me:               me,
   258  		derivedChainData: derivedChainData,
   259  	}
   260  
   261  	view := delta.NewView(ledger.Get)
   262  
   263  	var (
   264  		res *execution.ComputationResult
   265  
   266  		block1, block11, block111, block112, block1121,
   267  		block1111, block12, block121, block1211 *flow.Block
   268  
   269  		block1View, block11View, block111View, block112View, block1121View,
   270  		block1111View, block12View, block121View, block1211View state.View
   271  	)
   272  
   273  	t.Run("executing block1 (no collection)", func(t *testing.T) {
   274  		block1 = &flow.Block{
   275  			Header: &flow.Header{
   276  				View: 1,
   277  			},
   278  			Payload: &flow.Payload{
   279  				Guarantees: []*flow.CollectionGuarantee{},
   280  			},
   281  		}
   282  		block1View = view.NewChild()
   283  		executableBlock := &entity.ExecutableBlock{
   284  			Block:      block1,
   285  			StartState: unittest.StateCommitmentPointerFixture(),
   286  		}
   287  		_, err := engine.ComputeBlock(context.Background(), executableBlock, block1View)
   288  		require.NoError(t, err)
   289  	})
   290  
   291  	t.Run("executing block11 (deploys contract version 1)", func(t *testing.T) {
   292  		block11tx1 := testutil.DeployEventContractTransaction(account, chain, 1)
   293  		prepareTx(t, block11tx1, account, privKey, 0, chain)
   294  
   295  		txs11 := []*flow.TransactionBody{block11tx1}
   296  		col11 := flow.Collection{Transactions: txs11}
   297  		block11View = block1View.NewChild()
   298  		block11, res = createTestBlockAndRun(t, engine, block1, col11, block11View)
   299  		// cache should include value for this block
   300  		require.NotNil(t, derivedChainData.Get(block11.ID()))
   301  		// 1st event should be contract deployed
   302  		assert.EqualValues(t, "flow.AccountContractAdded", res.Events[0][0].Type)
   303  	})
   304  
   305  	t.Run("executing block111 (emit event (expected v1), update contract to v3)", func(t *testing.T) {
   306  		block111ExpectedValue := 1
   307  		// emit event
   308  		block111tx1 := testutil.CreateEmitEventTransaction(account, account)
   309  		prepareTx(t, block111tx1, account, privKey, 1, chain)
   310  
   311  		// update contract version 3
   312  		block111tx2 := testutil.UpdateEventContractTransaction(account, chain, 3)
   313  		prepareTx(t, block111tx2, account, privKey, 2, chain)
   314  
   315  		col111 := flow.Collection{Transactions: []*flow.TransactionBody{block111tx1, block111tx2}}
   316  		block111View = block11View.NewChild()
   317  		block111, res = createTestBlockAndRun(t, engine, block11, col111, block111View)
   318  		// cache should include a program for this block
   319  		require.NotNil(t, derivedChainData.Get(block111.ID()))
   320  
   321  		require.Len(t, res.Events, 2)
   322  
   323  		// 1st event
   324  		hasValidEventValue(t, res.Events[0][0], block111ExpectedValue)
   325  		// second event should be contract deployed
   326  		assert.EqualValues(t, "flow.AccountContractUpdated", res.Events[0][1].Type)
   327  	})
   328  
   329  	t.Run("executing block1111 (emit event (expected v3))", func(t *testing.T) {
   330  		block1111ExpectedValue := 3
   331  		block1111tx1 := testutil.CreateEmitEventTransaction(account, account)
   332  		prepareTx(t, block1111tx1, account, privKey, 3, chain)
   333  
   334  		col1111 := flow.Collection{Transactions: []*flow.TransactionBody{block1111tx1}}
   335  		block1111View = block111View.NewChild()
   336  		block1111, res = createTestBlockAndRun(t, engine, block111, col1111, block1111View)
   337  		// cache should include a program for this block
   338  		require.NotNil(t, derivedChainData.Get(block1111.ID()))
   339  
   340  		require.Len(t, res.Events, 2)
   341  
   342  		// 1st event
   343  		hasValidEventValue(t, res.Events[0][0], block1111ExpectedValue)
   344  	})
   345  
   346  	t.Run("executing block112 (emit event (expected v1))", func(t *testing.T) {
   347  		block112ExpectedValue := 1
   348  		block112tx1 := testutil.CreateEmitEventTransaction(account, account)
   349  		prepareTx(t, block112tx1, account, privKey, 1, chain)
   350  
   351  		// update contract version 4
   352  		block112tx2 := testutil.UpdateEventContractTransaction(account, chain, 4)
   353  		prepareTx(t, block112tx2, account, privKey, 2, chain)
   354  
   355  		col112 := flow.Collection{Transactions: []*flow.TransactionBody{block112tx1, block112tx2}}
   356  		block112View = block11View.NewChild()
   357  		block112, res = createTestBlockAndRun(t, engine, block11, col112, block112View)
   358  		// cache should include a program for this block
   359  		require.NotNil(t, derivedChainData.Get(block112.ID()))
   360  
   361  		require.Len(t, res.Events, 2)
   362  
   363  		// 1st event
   364  		hasValidEventValue(t, res.Events[0][0], block112ExpectedValue)
   365  		// second event should be contract deployed
   366  		assert.EqualValues(t, "flow.AccountContractUpdated", res.Events[0][1].Type)
   367  
   368  	})
   369  	t.Run("executing block1121 (emit event (expected v4))", func(t *testing.T) {
   370  		block1121ExpectedValue := 4
   371  		block1121tx1 := testutil.CreateEmitEventTransaction(account, account)
   372  		prepareTx(t, block1121tx1, account, privKey, 3, chain)
   373  
   374  		col1121 := flow.Collection{Transactions: []*flow.TransactionBody{block1121tx1}}
   375  		block1121View = block112View.NewChild()
   376  		block1121, res = createTestBlockAndRun(t, engine, block112, col1121, block1121View)
   377  		// cache should include a program for this block
   378  		require.NotNil(t, derivedChainData.Get(block1121.ID()))
   379  
   380  		require.Len(t, res.Events, 2)
   381  
   382  		// 1st event
   383  		hasValidEventValue(t, res.Events[0][0], block1121ExpectedValue)
   384  
   385  	})
   386  	t.Run("executing block12 (deploys contract V2)", func(t *testing.T) {
   387  
   388  		block12tx1 := testutil.DeployEventContractTransaction(account, chain, 2)
   389  		prepareTx(t, block12tx1, account, privKey, 0, chain)
   390  
   391  		col12 := flow.Collection{Transactions: []*flow.TransactionBody{block12tx1}}
   392  		block12View = block1View.NewChild()
   393  		block12, res = createTestBlockAndRun(t, engine, block1, col12, block12View)
   394  		// cache should include a program for this block
   395  		require.NotNil(t, derivedChainData.Get(block12.ID()))
   396  
   397  		require.Len(t, res.Events, 2)
   398  
   399  		assert.EqualValues(t, "flow.AccountContractAdded", res.Events[0][0].Type)
   400  	})
   401  	t.Run("executing block121 (emit event (expected V2)", func(t *testing.T) {
   402  		block121ExpectedValue := 2
   403  		block121tx1 := testutil.CreateEmitEventTransaction(account, account)
   404  		prepareTx(t, block121tx1, account, privKey, 1, chain)
   405  
   406  		col121 := flow.Collection{Transactions: []*flow.TransactionBody{block121tx1}}
   407  		block121View = block12View.NewChild()
   408  		block121, res = createTestBlockAndRun(t, engine, block12, col121, block121View)
   409  		// cache should include a program for this block
   410  		require.NotNil(t, derivedChainData.Get(block121.ID()))
   411  
   412  		require.Len(t, res.Events, 2)
   413  
   414  		// 1st event
   415  		hasValidEventValue(t, res.Events[0][0], block121ExpectedValue)
   416  	})
   417  	t.Run("executing Block1211 (emit event (expected V2)", func(t *testing.T) {
   418  		block1211ExpectedValue := 2
   419  		block1211tx1 := testutil.CreateEmitEventTransaction(account, account)
   420  		prepareTx(t, block1211tx1, account, privKey, 2, chain)
   421  
   422  		col1211 := flow.Collection{Transactions: []*flow.TransactionBody{block1211tx1}}
   423  		block1211View = block121View.NewChild()
   424  		block1211, res = createTestBlockAndRun(t, engine, block121, col1211, block1211View)
   425  		// cache should include a program for this block
   426  		require.NotNil(t, derivedChainData.Get(block1211.ID()))
   427  		// had no change so cache should be equal to parent
   428  		require.Equal(t, derivedChainData.Get(block121.ID()), derivedChainData.Get(block1211.ID()))
   429  
   430  		require.Len(t, res.Events, 2)
   431  
   432  		// 1st event
   433  		hasValidEventValue(t, res.Events[0][0], block1211ExpectedValue)
   434  	})
   435  
   436  }
   437  
   438  func createTestBlockAndRun(t *testing.T, engine *Manager, parentBlock *flow.Block, col flow.Collection, view state.View) (*flow.Block, *execution.ComputationResult) {
   439  	guarantee := flow.CollectionGuarantee{
   440  		CollectionID: col.ID(),
   441  		Signature:    nil,
   442  	}
   443  
   444  	block := &flow.Block{
   445  		Header: &flow.Header{
   446  			ParentID:  parentBlock.ID(),
   447  			View:      parentBlock.Header.Height + 1,
   448  			Timestamp: time.Now(),
   449  		},
   450  		Payload: &flow.Payload{
   451  			Guarantees: []*flow.CollectionGuarantee{&guarantee},
   452  		},
   453  	}
   454  
   455  	executableBlock := &entity.ExecutableBlock{
   456  		Block: block,
   457  		CompleteCollections: map[flow.Identifier]*entity.CompleteCollection{
   458  			guarantee.ID(): {
   459  				Guarantee:    &guarantee,
   460  				Transactions: col.Transactions,
   461  			},
   462  		},
   463  		StartState: unittest.StateCommitmentPointerFixture(),
   464  	}
   465  	returnedComputationResult, err := engine.ComputeBlock(context.Background(), executableBlock, view)
   466  	require.NoError(t, err)
   467  
   468  	for _, txResult := range returnedComputationResult.TransactionResults {
   469  		require.Empty(t, txResult.ErrorMessage)
   470  	}
   471  
   472  	return block, returnedComputationResult
   473  }
   474  
   475  func prepareTx(t *testing.T,
   476  	tx *flow.TransactionBody,
   477  	account flow.Address,
   478  	privKey flow.AccountPrivateKey,
   479  	seqNumber uint64,
   480  	chain flow.Chain) {
   481  	tx.SetProposalKey(account, 0, seqNumber).
   482  		SetPayer(chain.ServiceAddress())
   483  	err := testutil.SignPayload(tx, account, privKey)
   484  	require.NoError(t, err)
   485  	err = testutil.SignEnvelope(tx, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey)
   486  	require.NoError(t, err)
   487  }
   488  
   489  func hasValidEventValue(t *testing.T, event flow.Event, value int) {
   490  	data, err := jsoncdc.Decode(nil, event.Payload)
   491  	require.NoError(t, err)
   492  	assert.Equal(t, int16(value), data.(cadence.Event).Fields[0].ToGoValue())
   493  }