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