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 }