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

     1  package execution
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/onflow/cadence/runtime/stdlib"
    10  
    11  	"github.com/onflow/flow-go/fvm/errors"
    12  	"github.com/onflow/flow-go/fvm/storage/derived"
    13  
    14  	"github.com/onflow/cadence"
    15  	"github.com/onflow/cadence/encoding/ccf"
    16  	jsoncdc "github.com/onflow/cadence/encoding/json"
    17  	"github.com/rs/zerolog"
    18  	mocks "github.com/stretchr/testify/mock"
    19  	"github.com/stretchr/testify/suite"
    20  
    21  	"github.com/onflow/flow-go/engine/execution/computation/query"
    22  	"github.com/onflow/flow-go/engine/execution/computation/query/mock"
    23  	"github.com/onflow/flow-go/engine/execution/testutil"
    24  	"github.com/onflow/flow-go/fvm"
    25  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    26  	"github.com/onflow/flow-go/model/flow"
    27  	"github.com/onflow/flow-go/module/metrics"
    28  	"github.com/onflow/flow-go/module/state_synchronization/indexer"
    29  	synctest "github.com/onflow/flow-go/module/state_synchronization/requester/unittest"
    30  	"github.com/onflow/flow-go/storage"
    31  	pebbleStorage "github.com/onflow/flow-go/storage/pebble"
    32  	"github.com/onflow/flow-go/utils/unittest"
    33  )
    34  
    35  func TestScripts(t *testing.T) {
    36  	suite.Run(t, new(scriptTestSuite))
    37  }
    38  
    39  type scriptTestSuite struct {
    40  	suite.Suite
    41  	scripts       *Scripts
    42  	registerIndex storage.RegisterIndex
    43  	vm            *fvm.VirtualMachine
    44  	vmCtx         fvm.Context
    45  	chain         flow.Chain
    46  	height        uint64
    47  	snapshot      snapshot.SnapshotTree
    48  	dbDir         string
    49  }
    50  
    51  func (s *scriptTestSuite) TestScriptExecution() {
    52  	s.Run("Simple Script Execution", func() {
    53  		number := int64(42)
    54  		code := []byte(fmt.Sprintf("access(all) fun main(): Int { return %d; }", number))
    55  
    56  		result, err := s.scripts.ExecuteAtBlockHeight(context.Background(), code, nil, s.height)
    57  		s.Require().NoError(err)
    58  		val, err := jsoncdc.Decode(nil, result)
    59  		s.Require().NoError(err)
    60  		s.Assert().Equal(number, val.(cadence.Int).Value.Int64())
    61  	})
    62  
    63  	s.Run("Get Block", func() {
    64  		code := []byte(fmt.Sprintf(`access(all) fun main(): UInt64 {
    65  			getBlock(at: %d)!
    66  			return getCurrentBlock().height 
    67  		}`, s.height))
    68  
    69  		result, err := s.scripts.ExecuteAtBlockHeight(context.Background(), code, nil, s.height)
    70  		s.Require().NoError(err)
    71  		val, err := jsoncdc.Decode(nil, result)
    72  		s.Require().NoError(err)
    73  		// make sure that the returned block height matches the current one set
    74  		s.Assert().Equal(s.height, uint64(val.(cadence.UInt64)))
    75  	})
    76  
    77  	s.Run("Handle not found Register", func() {
    78  		// use a non-existing address to trigger register get function
    79  		code := []byte("import Foo from 0x01; access(all) fun main() { }")
    80  
    81  		result, err := s.scripts.ExecuteAtBlockHeight(context.Background(), code, nil, s.height)
    82  		s.Assert().Error(err)
    83  		s.Assert().Nil(result)
    84  	})
    85  
    86  	s.Run("Valid Argument", func() {
    87  		code := []byte("access(all) fun main(foo: Int): Int { return foo }")
    88  		arg := cadence.NewInt(2)
    89  		encoded, err := jsoncdc.Encode(arg)
    90  		s.Require().NoError(err)
    91  
    92  		result, err := s.scripts.ExecuteAtBlockHeight(
    93  			context.Background(),
    94  			code,
    95  			[][]byte{encoded},
    96  			s.height,
    97  		)
    98  		s.Require().NoError(err)
    99  		s.Assert().Equal(encoded, result)
   100  	})
   101  
   102  	s.Run("Invalid Argument", func() {
   103  		code := []byte("access(all) fun main(foo: Int): Int { return foo }")
   104  		invalid := [][]byte{[]byte("i")}
   105  
   106  		result, err := s.scripts.ExecuteAtBlockHeight(context.Background(), code, invalid, s.height)
   107  		s.Assert().Nil(result)
   108  		var coded errors.CodedError
   109  		s.Require().True(errors.As(err, &coded))
   110  		fmt.Println(coded.Code(), coded.Error())
   111  		s.Assert().Equal(errors.ErrCodeInvalidArgumentError, coded.Code())
   112  	})
   113  }
   114  
   115  func (s *scriptTestSuite) TestGetAccount() {
   116  	s.Run("Get Service Account", func() {
   117  		address := s.chain.ServiceAddress()
   118  		account, err := s.scripts.GetAccountAtBlockHeight(context.Background(), address, s.height)
   119  		s.Require().NoError(err)
   120  		s.Assert().Equal(address, account.Address)
   121  		s.Assert().NotZero(account.Balance)
   122  		s.Assert().NotZero(len(account.Contracts))
   123  	})
   124  
   125  	s.Run("Get New Account", func() {
   126  		address := s.createAccount()
   127  		account, err := s.scripts.GetAccountAtBlockHeight(context.Background(), address, s.height)
   128  		s.Require().NoError(err)
   129  		s.Require().Equal(address, account.Address)
   130  		s.Assert().Zero(account.Balance)
   131  	})
   132  }
   133  
   134  func (s *scriptTestSuite) SetupTest() {
   135  	logger := unittest.LoggerForTest(s.Suite.T(), zerolog.InfoLevel)
   136  	entropyProvider := testutil.EntropyProviderFixture(nil)
   137  	blockchain := unittest.BlockchainFixture(10)
   138  	headers := newBlockHeadersStorage(blockchain)
   139  
   140  	s.chain = flow.Emulator.Chain()
   141  	s.snapshot = snapshot.NewSnapshotTree(nil)
   142  	s.vm = fvm.NewVirtualMachine()
   143  	s.vmCtx = fvm.NewContext(
   144  		fvm.WithChain(s.chain),
   145  		fvm.WithAuthorizationChecksEnabled(false),
   146  		fvm.WithSequenceNumberCheckAndIncrementEnabled(false),
   147  	)
   148  	s.height = blockchain[0].Header.Height
   149  
   150  	entropyBlock := mock.NewEntropyProviderPerBlock(s.T())
   151  	entropyBlock.
   152  		On("AtBlockID", mocks.AnythingOfType("flow.Identifier")).
   153  		Return(entropyProvider).
   154  		Maybe()
   155  
   156  	s.dbDir = unittest.TempDir(s.T())
   157  	db := pebbleStorage.NewBootstrappedRegistersWithPathForTest(s.T(), s.dbDir, s.height, s.height)
   158  	pebbleRegisters, err := pebbleStorage.NewRegisters(db)
   159  	s.Require().NoError(err)
   160  	s.registerIndex = pebbleRegisters
   161  
   162  	derivedChainData, err := derived.NewDerivedChainData(derived.DefaultDerivedDataCacheSize)
   163  	s.Require().NoError(err)
   164  
   165  	index, err := indexer.New(
   166  		logger,
   167  		metrics.NewNoopCollector(),
   168  		nil,
   169  		s.registerIndex,
   170  		headers,
   171  		nil,
   172  		nil,
   173  		nil,
   174  		nil,
   175  		flow.Testnet.Chain(),
   176  		derivedChainData,
   177  		nil,
   178  	)
   179  	s.Require().NoError(err)
   180  
   181  	s.scripts = NewScripts(
   182  		logger,
   183  		metrics.NewNoopCollector(),
   184  		s.chain.ChainID(),
   185  		entropyBlock,
   186  		headers,
   187  		index.RegisterValue,
   188  		query.NewDefaultConfig(),
   189  		derivedChainData,
   190  		true,
   191  	)
   192  
   193  	s.bootstrap()
   194  }
   195  
   196  func (s *scriptTestSuite) TearDownTest() {
   197  	s.Require().NoError(os.RemoveAll(s.dbDir))
   198  }
   199  
   200  func (s *scriptTestSuite) bootstrap() {
   201  	bootstrapOpts := []fvm.BootstrapProcedureOption{
   202  		fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply),
   203  	}
   204  
   205  	executionSnapshot, out, err := s.vm.Run(
   206  		s.vmCtx,
   207  		fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...),
   208  		s.snapshot)
   209  
   210  	s.Require().NoError(err)
   211  	s.Require().NoError(out.Err)
   212  
   213  	s.height++
   214  	err = s.registerIndex.Store(executionSnapshot.UpdatedRegisters(), s.height)
   215  	s.Require().NoError(err)
   216  
   217  	s.snapshot = s.snapshot.Append(executionSnapshot)
   218  }
   219  
   220  func (s *scriptTestSuite) createAccount() flow.Address {
   221  	const createAccountTransaction = `
   222  		transaction {
   223  		  prepare(signer: auth(Storage, Capabilities) &Account) {
   224  			let account = Account(payer: signer)
   225  		  }
   226  		}`
   227  
   228  	txBody := flow.NewTransactionBody().
   229  		SetScript([]byte(createAccountTransaction)).
   230  		AddAuthorizer(s.chain.ServiceAddress())
   231  
   232  	executionSnapshot, output, err := s.vm.Run(
   233  		s.vmCtx,
   234  		fvm.Transaction(txBody, 0),
   235  		s.snapshot,
   236  	)
   237  	s.Require().NoError(err)
   238  	s.Require().NoError(output.Err)
   239  
   240  	s.height++
   241  	err = s.registerIndex.Store(executionSnapshot.UpdatedRegisters(), s.height)
   242  	s.Require().NoError(err)
   243  
   244  	s.snapshot = s.snapshot.Append(executionSnapshot)
   245  
   246  	var accountCreatedEvents []flow.Event
   247  	for _, event := range output.Events {
   248  		if event.Type != flow.EventAccountCreated {
   249  			continue
   250  		}
   251  		accountCreatedEvents = append(accountCreatedEvents, event)
   252  		break
   253  	}
   254  	s.Require().Len(accountCreatedEvents, 1)
   255  
   256  	data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload)
   257  	s.Require().NoError(err)
   258  
   259  	return flow.ConvertAddress(
   260  		cadence.SearchFieldByName(
   261  			data.(cadence.Event),
   262  			stdlib.AccountEventAddressParameter.Identifier,
   263  		).(cadence.Address),
   264  	)
   265  }
   266  
   267  func newBlockHeadersStorage(blocks []*flow.Block) storage.Headers {
   268  	blocksByHeight := make(map[uint64]*flow.Block)
   269  	for _, b := range blocks {
   270  		blocksByHeight[b.Header.Height] = b
   271  	}
   272  
   273  	return synctest.MockBlockHeaderStorage(synctest.WithByHeight(blocksByHeight))
   274  }