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 }