github.com/koko1123/flow-go-1@v0.29.6/engine/execution/computation/computer/computer_test.go (about) 1 package computer_test 2 3 import ( 4 "context" 5 "fmt" 6 "math/rand" 7 "testing" 8 9 "github.com/onflow/cadence" 10 "github.com/onflow/cadence/runtime" 11 "github.com/onflow/cadence/runtime/common" 12 "github.com/onflow/cadence/runtime/interpreter" 13 "github.com/onflow/cadence/runtime/sema" 14 "github.com/onflow/cadence/runtime/stdlib" 15 16 "github.com/ipfs/go-datastore" 17 dssync "github.com/ipfs/go-datastore/sync" 18 blockstore "github.com/ipfs/go-ipfs-blockstore" 19 "github.com/rs/zerolog" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/mock" 22 "github.com/stretchr/testify/require" 23 24 "github.com/koko1123/flow-go-1/engine/execution" 25 "github.com/koko1123/flow-go-1/engine/execution/computation/committer" 26 "github.com/koko1123/flow-go-1/engine/execution/computation/computer" 27 computermock "github.com/koko1123/flow-go-1/engine/execution/computation/computer/mock" 28 "github.com/koko1123/flow-go-1/engine/execution/state/delta" 29 "github.com/koko1123/flow-go-1/engine/execution/testutil" 30 "github.com/koko1123/flow-go-1/fvm" 31 "github.com/koko1123/flow-go-1/fvm/derived" 32 "github.com/koko1123/flow-go-1/fvm/environment" 33 fvmErrors "github.com/koko1123/flow-go-1/fvm/errors" 34 reusableRuntime "github.com/koko1123/flow-go-1/fvm/runtime" 35 "github.com/koko1123/flow-go-1/fvm/state" 36 "github.com/koko1123/flow-go-1/fvm/systemcontracts" 37 "github.com/koko1123/flow-go-1/model/flow" 38 "github.com/koko1123/flow-go-1/module" 39 "github.com/koko1123/flow-go-1/module/epochs" 40 "github.com/koko1123/flow-go-1/module/executiondatasync/execution_data" 41 "github.com/koko1123/flow-go-1/module/executiondatasync/provider" 42 mocktracker "github.com/koko1123/flow-go-1/module/executiondatasync/tracker/mock" 43 "github.com/koko1123/flow-go-1/module/mempool/entity" 44 "github.com/koko1123/flow-go-1/module/metrics" 45 modulemock "github.com/koko1123/flow-go-1/module/mock" 46 requesterunit "github.com/koko1123/flow-go-1/module/state_synchronization/requester/unittest" 47 "github.com/koko1123/flow-go-1/module/trace" 48 "github.com/koko1123/flow-go-1/utils/unittest" 49 ) 50 51 func TestBlockExecutor_ExecuteBlock(t *testing.T) { 52 53 rag := &RandomAddressGenerator{} 54 55 me := new(modulemock.Local) 56 me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything). 57 Return(nil, nil) 58 59 t.Run("single collection", func(t *testing.T) { 60 61 execCtx := fvm.NewContext() 62 63 vm := new(computermock.VirtualMachine) 64 vm.On("Run", mock.Anything, mock.Anything, mock.Anything). 65 Return(nil). 66 Run(func(args mock.Arguments) { 67 // ctx := args[0].(fvm.Context) 68 tx := args[1].(*fvm.TransactionProcedure) 69 70 tx.Events = generateEvents(1, tx.TxIndex) 71 }). 72 Times(2 + 1) // 2 txs in collection + system chunk 73 74 committer := new(computermock.ViewCommitter) 75 committer.On("CommitView", mock.Anything, mock.Anything). 76 Return(nil, nil, nil, nil). 77 Times(2 + 1) // 2 txs in collection + system chunk 78 79 exemetrics := new(modulemock.ExecutionMetrics) 80 exemetrics.On("ExecutionCollectionExecuted", 81 mock.Anything, // duration 82 mock.Anything). // stats 83 Return(nil). 84 Times(2) // 1 collection + system collection 85 86 exemetrics.On("ExecutionTransactionExecuted", 87 mock.Anything, // duration 88 mock.Anything, // computation used 89 mock.Anything, // memory used 90 mock.Anything, // actual memory used 91 mock.Anything, // number of events 92 mock.Anything, // size of events 93 false). // no failure 94 Return(nil). 95 Times(2 + 1) // 2 txs in collection + system chunk tx 96 97 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 98 trackerStorage := mocktracker.NewMockStorage() 99 100 prov := provider.NewProvider( 101 zerolog.Nop(), 102 metrics.NewNoopCollector(), 103 execution_data.DefaultSerializer, 104 bservice, 105 trackerStorage, 106 ) 107 108 exe, err := computer.NewBlockComputer( 109 vm, 110 execCtx, 111 exemetrics, 112 trace.NewNoopTracer(), 113 zerolog.Nop(), 114 committer, 115 me, 116 prov) 117 require.NoError(t, err) 118 119 // create a block with 1 collection with 2 transactions 120 block := generateBlock(1, 2, rag) 121 122 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 123 return nil, nil 124 }) 125 126 result, err := exe.ExecuteBlock( 127 context.Background(), 128 block, 129 view, 130 derived.NewEmptyDerivedBlockData()) 131 assert.NoError(t, err) 132 assert.Len(t, result.StateSnapshots, 1+1) // +1 system chunk 133 assert.Len(t, result.TrieUpdates, 1+1) // +1 system chunk 134 135 assertEventHashesMatch(t, 1+1, result) 136 137 vm.AssertExpectations(t) 138 }) 139 140 t.Run("empty block still computes system chunk", func(t *testing.T) { 141 142 execCtx := fvm.NewContext() 143 144 vm := new(computermock.VirtualMachine) 145 committer := new(computermock.ViewCommitter) 146 147 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 148 trackerStorage := mocktracker.NewMockStorage() 149 150 prov := provider.NewProvider( 151 zerolog.Nop(), 152 metrics.NewNoopCollector(), 153 execution_data.DefaultSerializer, 154 bservice, 155 trackerStorage, 156 ) 157 158 exe, err := computer.NewBlockComputer( 159 vm, 160 execCtx, 161 metrics.NewNoopCollector(), 162 trace.NewNoopTracer(), 163 zerolog.Nop(), 164 committer, 165 me, 166 prov) 167 require.NoError(t, err) 168 169 // create an empty block 170 block := generateBlock(0, 0, rag) 171 derivedBlockData := derived.NewEmptyDerivedBlockData() 172 173 vm.On("Run", mock.Anything, mock.Anything, mock.Anything). 174 Return(nil). 175 Once() // just system chunk 176 177 committer.On("CommitView", mock.Anything, mock.Anything). 178 Return(nil, nil, nil, nil). 179 Once() // just system chunk 180 181 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 182 return nil, nil 183 }) 184 185 result, err := exe.ExecuteBlock(context.Background(), block, view, derivedBlockData) 186 assert.NoError(t, err) 187 assert.Len(t, result.StateSnapshots, 1) 188 assert.Len(t, result.TrieUpdates, 1) 189 assert.Len(t, result.TransactionResults, 1) 190 191 assertEventHashesMatch(t, 1, result) 192 193 vm.AssertExpectations(t) 194 }) 195 196 t.Run("system chunk transaction should not fail", func(t *testing.T) { 197 198 // include all fees. System chunk should ignore them 199 contextOptions := []fvm.Option{ 200 fvm.WithTransactionFeesEnabled(true), 201 fvm.WithAccountStorageLimit(true), 202 fvm.WithBlocks(&environment.NoopBlockFinder{}), 203 } 204 // set 0 clusters to pass n_collectors >= n_clusters check 205 epochConfig := epochs.DefaultEpochConfig() 206 epochConfig.NumCollectorClusters = 0 207 bootstrapOptions := []fvm.BootstrapProcedureOption{ 208 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 209 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 210 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 211 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 212 fvm.WithEpochConfig(epochConfig), 213 } 214 215 chain := flow.Localnet.Chain() 216 vm := fvm.NewVirtualMachine() 217 derivedBlockData := derived.NewEmptyDerivedBlockData() 218 baseOpts := []fvm.Option{ 219 fvm.WithChain(chain), 220 fvm.WithDerivedBlockData(derivedBlockData), 221 } 222 223 opts := append(baseOpts, contextOptions...) 224 ctx := fvm.NewContext(opts...) 225 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 226 return nil, nil 227 }) 228 229 baseBootstrapOpts := []fvm.BootstrapProcedureOption{ 230 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 231 } 232 bootstrapOpts := append(baseBootstrapOpts, bootstrapOptions...) 233 err := vm.Run(ctx, fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...), view) 234 require.NoError(t, err) 235 236 comm := new(computermock.ViewCommitter) 237 238 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 239 trackerStorage := mocktracker.NewMockStorage() 240 241 prov := provider.NewProvider( 242 zerolog.Nop(), 243 metrics.NewNoopCollector(), 244 execution_data.DefaultSerializer, 245 bservice, 246 trackerStorage, 247 ) 248 249 exe, err := computer.NewBlockComputer( 250 vm, 251 ctx, 252 metrics.NewNoopCollector(), 253 trace.NewNoopTracer(), 254 zerolog.Nop(), 255 comm, 256 me, 257 prov) 258 require.NoError(t, err) 259 260 // create an empty block 261 block := generateBlock(0, 0, rag) 262 263 comm.On("CommitView", mock.Anything, mock.Anything). 264 Return(nil, nil, nil, nil). 265 Once() // just system chunk 266 267 result, err := exe.ExecuteBlock(context.Background(), block, view, derivedBlockData) 268 assert.NoError(t, err) 269 assert.Len(t, result.StateSnapshots, 1) 270 assert.Len(t, result.TrieUpdates, 1) 271 assert.Len(t, result.TransactionResults, 1) 272 273 assert.Empty(t, result.TransactionResults[0].ErrorMessage) 274 }) 275 276 t.Run("multiple collections", func(t *testing.T) { 277 execCtx := fvm.NewContext() 278 279 vm := new(computermock.VirtualMachine) 280 committer := new(computermock.ViewCommitter) 281 282 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 283 trackerStorage := mocktracker.NewMockStorage() 284 285 prov := provider.NewProvider( 286 zerolog.Nop(), 287 metrics.NewNoopCollector(), 288 execution_data.DefaultSerializer, 289 bservice, 290 trackerStorage, 291 ) 292 293 exe, err := computer.NewBlockComputer( 294 vm, 295 execCtx, 296 metrics.NewNoopCollector(), 297 trace.NewNoopTracer(), 298 zerolog.Nop(), 299 committer, 300 me, 301 prov) 302 require.NoError(t, err) 303 304 collectionCount := 2 305 transactionsPerCollection := 2 306 eventsPerTransaction := 2 307 eventsPerCollection := eventsPerTransaction * transactionsPerCollection 308 totalTransactionCount := (collectionCount * transactionsPerCollection) + 1 // +1 for system chunk 309 // totalEventCount := eventsPerTransaction * totalTransactionCount 310 311 // create a block with 2 collections with 2 transactions each 312 block := generateBlock(collectionCount, transactionsPerCollection, rag) 313 derivedBlockData := derived.NewEmptyDerivedBlockData() 314 315 vm.On("Run", mock.Anything, mock.Anything, mock.Anything). 316 Run(func(args mock.Arguments) { 317 tx := args[1].(*fvm.TransactionProcedure) 318 319 tx.Err = fvmErrors.NewInvalidAddressErrorf(flow.Address{}, "no payer address provided") 320 // create dummy events 321 tx.Events = generateEvents(eventsPerTransaction, tx.TxIndex) 322 }). 323 Return(nil). 324 Times(totalTransactionCount) 325 326 committer.On("CommitView", mock.Anything, mock.Anything). 327 Return(nil, nil, nil, nil). 328 Times(collectionCount + 1) 329 330 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 331 return nil, nil 332 }) 333 334 result, err := exe.ExecuteBlock(context.Background(), block, view, derivedBlockData) 335 assert.NoError(t, err) 336 337 // chunk count should match collection count 338 assert.Len(t, result.StateSnapshots, collectionCount+1) // system chunk 339 340 // all events should have been collected 341 assert.Len(t, result.Events, collectionCount+1) 342 343 for i := 0; i < collectionCount; i++ { 344 assert.Len(t, result.Events[i], eventsPerCollection) 345 } 346 347 assert.Len(t, result.Events[len(result.Events)-1], eventsPerTransaction) 348 349 // events should have been indexed by transaction and event 350 k := 0 351 for expectedTxIndex := 0; expectedTxIndex < totalTransactionCount; expectedTxIndex++ { 352 for expectedEventIndex := 0; expectedEventIndex < eventsPerTransaction; expectedEventIndex++ { 353 354 chunkIndex := k / eventsPerCollection 355 eventIndex := k % eventsPerCollection 356 357 e := result.Events[chunkIndex][eventIndex] 358 assert.EqualValues(t, expectedEventIndex, int(e.EventIndex)) 359 assert.EqualValues(t, expectedTxIndex, e.TransactionIndex) 360 k++ 361 } 362 } 363 364 expectedResults := make([]flow.TransactionResult, 0) 365 for _, c := range block.CompleteCollections { 366 for _, t := range c.Transactions { 367 txResult := flow.TransactionResult{ 368 TransactionID: t.ID(), 369 ErrorMessage: fvmErrors.NewInvalidAddressErrorf(flow.Address{}, "no payer address provided").Error(), 370 } 371 expectedResults = append(expectedResults, txResult) 372 } 373 } 374 assert.ElementsMatch(t, expectedResults, result.TransactionResults[0:len(result.TransactionResults)-1]) // strip system chunk 375 376 assertEventHashesMatch(t, collectionCount+1, result) 377 378 vm.AssertExpectations(t) 379 }) 380 381 t.Run("service events are emitted", func(t *testing.T) { 382 execCtx := fvm.NewContext( 383 fvm.WithServiceEventCollectionEnabled(), 384 fvm.WithAuthorizationChecksEnabled(false), 385 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 386 ) 387 388 collectionCount := 2 389 transactionsPerCollection := 2 390 391 totalTransactionCount := (collectionCount * transactionsPerCollection) + 1 // +1 for system chunk 392 393 // create a block with 2 collections with 2 transactions each 394 block := generateBlock(collectionCount, transactionsPerCollection, rag) 395 396 ordinaryEvent := cadence.Event{ 397 EventType: &cadence.EventType{ 398 Location: stdlib.FlowLocation{}, 399 QualifiedIdentifier: "what.ever", 400 }, 401 } 402 403 serviceEvents, err := systemcontracts.ServiceEventsForChain(execCtx.Chain.ChainID()) 404 require.NoError(t, err) 405 406 serviceEventA := cadence.Event{ 407 EventType: &cadence.EventType{ 408 Location: common.AddressLocation{ 409 Address: common.Address(serviceEvents.EpochSetup.Address), 410 }, 411 QualifiedIdentifier: serviceEvents.EpochSetup.QualifiedIdentifier(), 412 }, 413 } 414 serviceEventB := cadence.Event{ 415 EventType: &cadence.EventType{ 416 Location: common.AddressLocation{ 417 Address: common.Address(serviceEvents.EpochCommit.Address), 418 }, 419 QualifiedIdentifier: serviceEvents.EpochCommit.QualifiedIdentifier(), 420 }, 421 } 422 423 // events to emit for each iteration/transaction 424 events := make([][]cadence.Event, totalTransactionCount) 425 events[0] = nil 426 events[1] = []cadence.Event{serviceEventA, ordinaryEvent} 427 events[2] = []cadence.Event{ordinaryEvent} 428 events[3] = nil 429 events[4] = []cadence.Event{serviceEventB} 430 431 emittingRuntime := &testRuntime{ 432 executeTransaction: func(script runtime.Script, context runtime.Context) error { 433 for _, e := range events[0] { 434 err := context.Interface.EmitEvent(e) 435 if err != nil { 436 return err 437 } 438 } 439 events = events[1:] 440 return nil 441 }, 442 readStored: func(address common.Address, path cadence.Path, r runtime.Context) (cadence.Value, error) { 443 return nil, nil 444 }, 445 } 446 447 execCtx = fvm.NewContextFromParent( 448 execCtx, 449 fvm.WithReusableCadenceRuntimePool( 450 reusableRuntime.NewCustomReusableCadenceRuntimePool( 451 0, 452 func(_ runtime.Config) runtime.Runtime { 453 return emittingRuntime 454 }))) 455 456 vm := fvm.NewVirtualMachine() 457 458 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 459 trackerStorage := mocktracker.NewMockStorage() 460 461 prov := provider.NewProvider( 462 zerolog.Nop(), 463 metrics.NewNoopCollector(), 464 execution_data.DefaultSerializer, 465 bservice, 466 trackerStorage, 467 ) 468 469 exe, err := computer.NewBlockComputer( 470 vm, 471 execCtx, 472 metrics.NewNoopCollector(), 473 trace.NewNoopTracer(), 474 zerolog.Nop(), 475 committer.NewNoopViewCommitter(), 476 me, 477 prov) 478 require.NoError(t, err) 479 480 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 481 return nil, nil 482 }) 483 484 result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData()) 485 require.NoError(t, err) 486 487 // make sure event index sequence are valid 488 for _, eventsList := range result.Events { 489 unittest.EnsureEventsIndexSeq(t, eventsList, execCtx.Chain.ChainID()) 490 } 491 492 // all events should have been collected 493 require.Len(t, result.ServiceEvents, 2) 494 495 // events are ordered 496 require.Equal(t, serviceEventA.EventType.ID(), string(result.ServiceEvents[0].Type)) 497 require.Equal(t, serviceEventB.EventType.ID(), string(result.ServiceEvents[1].Type)) 498 499 assertEventHashesMatch(t, collectionCount+1, result) 500 }) 501 502 t.Run("succeeding transactions store programs", func(t *testing.T) { 503 504 execCtx := fvm.NewContext() 505 506 address := common.Address{0x1} 507 contractLocation := common.AddressLocation{ 508 Address: address, 509 Name: "Test", 510 } 511 512 contractProgram := &interpreter.Program{} 513 514 rt := &testRuntime{ 515 executeTransaction: func(script runtime.Script, r runtime.Context) error { 516 517 program, err := r.Interface.GetProgram(contractLocation) //nolint:staticcheck 518 require.NoError(t, err) 519 require.Nil(t, program) 520 521 err = r.Interface.SetProgram( 522 contractLocation, 523 contractProgram, 524 ) 525 require.NoError(t, err) 526 527 return nil 528 }, 529 readStored: func(address common.Address, path cadence.Path, r runtime.Context) (cadence.Value, error) { 530 return nil, nil 531 }, 532 } 533 534 execCtx = fvm.NewContextFromParent( 535 execCtx, 536 fvm.WithReusableCadenceRuntimePool( 537 reusableRuntime.NewCustomReusableCadenceRuntimePool( 538 0, 539 func(_ runtime.Config) runtime.Runtime { 540 return rt 541 }))) 542 543 vm := fvm.NewVirtualMachine() 544 545 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 546 trackerStorage := mocktracker.NewMockStorage() 547 548 prov := provider.NewProvider( 549 zerolog.Nop(), 550 metrics.NewNoopCollector(), 551 execution_data.DefaultSerializer, 552 bservice, 553 trackerStorage, 554 ) 555 556 exe, err := computer.NewBlockComputer( 557 vm, 558 execCtx, 559 metrics.NewNoopCollector(), 560 trace.NewNoopTracer(), 561 zerolog.Nop(), 562 committer.NewNoopViewCommitter(), 563 me, 564 prov) 565 require.NoError(t, err) 566 567 const collectionCount = 2 568 const transactionCount = 2 569 block := generateBlock(collectionCount, transactionCount, rag) 570 571 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 572 return nil, nil 573 }) 574 575 err = view.Set(string(address.Bytes()), state.AccountStatusKey, environment.NewAccountStatus().ToBytes()) 576 require.NoError(t, err) 577 578 result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData()) 579 assert.NoError(t, err) 580 assert.Len(t, result.StateSnapshots, collectionCount+1) // +1 system chunk 581 }) 582 583 t.Run("failing transactions do not store programs", func(t *testing.T) { 584 execCtx := fvm.NewContext( 585 fvm.WithAuthorizationChecksEnabled(false), 586 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 587 ) 588 589 address := common.Address{0x1} 590 591 contractLocation := common.AddressLocation{ 592 Address: address, 593 Name: "Test", 594 } 595 596 contractProgram := &interpreter.Program{} 597 598 const collectionCount = 2 599 const transactionCount = 2 600 601 var executionCalls int 602 603 rt := &testRuntime{ 604 executeTransaction: func(script runtime.Script, r runtime.Context) error { 605 606 executionCalls++ 607 608 // NOTE: set a program and revert all transactions but the system chunk transaction 609 610 program, err := r.Interface.GetProgram(contractLocation) //nolint:staticcheck 611 require.NoError(t, err) 612 613 if executionCalls > collectionCount*transactionCount { 614 return nil 615 } 616 if program == nil { 617 618 err = r.Interface.SetProgram( 619 contractLocation, 620 contractProgram, 621 ) 622 require.NoError(t, err) 623 624 } 625 return runtime.Error{ 626 Err: fmt.Errorf("TX reverted"), 627 } 628 }, 629 readStored: func(address common.Address, path cadence.Path, r runtime.Context) (cadence.Value, error) { 630 return nil, nil 631 }, 632 } 633 634 execCtx = fvm.NewContextFromParent( 635 execCtx, 636 fvm.WithReusableCadenceRuntimePool( 637 reusableRuntime.NewCustomReusableCadenceRuntimePool( 638 0, 639 func(_ runtime.Config) runtime.Runtime { 640 return rt 641 }))) 642 643 vm := fvm.NewVirtualMachine() 644 645 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 646 trackerStorage := mocktracker.NewMockStorage() 647 648 prov := provider.NewProvider( 649 zerolog.Nop(), 650 metrics.NewNoopCollector(), 651 execution_data.DefaultSerializer, 652 bservice, 653 trackerStorage, 654 ) 655 656 exe, err := computer.NewBlockComputer( 657 vm, 658 execCtx, 659 metrics.NewNoopCollector(), 660 trace.NewNoopTracer(), 661 zerolog.Nop(), 662 committer.NewNoopViewCommitter(), 663 me, 664 prov) 665 require.NoError(t, err) 666 667 block := generateBlock(collectionCount, transactionCount, rag) 668 669 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 670 return nil, nil 671 }) 672 673 err = view.Set(string(address.Bytes()), state.AccountStatusKey, environment.NewAccountStatus().ToBytes()) 674 require.NoError(t, err) 675 676 result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData()) 677 require.NoError(t, err) 678 assert.Len(t, result.StateSnapshots, collectionCount+1) // +1 system chunk 679 }) 680 } 681 682 func assertEventHashesMatch(t *testing.T, expectedNoOfChunks int, result *execution.ComputationResult) { 683 684 require.Len(t, result.Events, expectedNoOfChunks) 685 require.Len(t, result.EventsHashes, expectedNoOfChunks) 686 687 for i := 0; i < expectedNoOfChunks; i++ { 688 calculatedHash, err := flow.EventsMerkleRootHash(result.Events[i]) 689 require.NoError(t, err) 690 691 require.Equal(t, calculatedHash, result.EventsHashes[i]) 692 } 693 } 694 695 type testTransactionExecutor struct { 696 executeTransaction func(runtime.Script, runtime.Context) error 697 698 script runtime.Script 699 context runtime.Context 700 } 701 702 func (executor *testTransactionExecutor) Preprocess() error { 703 // Do nothing. 704 return nil 705 } 706 707 func (executor *testTransactionExecutor) Execute() error { 708 return executor.executeTransaction(executor.script, executor.context) 709 } 710 711 func (executor *testTransactionExecutor) Result() (cadence.Value, error) { 712 panic("Result not expected") 713 } 714 715 type testRuntime struct { 716 executeScript func(runtime.Script, runtime.Context) (cadence.Value, error) 717 executeTransaction func(runtime.Script, runtime.Context) error 718 readStored func(common.Address, cadence.Path, runtime.Context) (cadence.Value, error) 719 } 720 721 func (e *testRuntime) NewScriptExecutor(script runtime.Script, c runtime.Context) runtime.Executor { 722 panic("NewScriptExecutor not expected") 723 } 724 725 func (e *testRuntime) NewTransactionExecutor(script runtime.Script, c runtime.Context) runtime.Executor { 726 return &testTransactionExecutor{ 727 executeTransaction: e.executeTransaction, 728 script: script, 729 context: c, 730 } 731 } 732 733 func (e *testRuntime) NewContractFunctionExecutor(contractLocation common.AddressLocation, functionName string, arguments []cadence.Value, argumentTypes []sema.Type, context runtime.Context) runtime.Executor { 734 panic("NewContractFunctionExecutor not expected") 735 } 736 737 var _ runtime.Runtime = &testRuntime{} 738 739 func (e *testRuntime) SetInvalidatedResourceValidationEnabled(_ bool) { 740 panic("SetInvalidatedResourceValidationEnabled not expected") 741 } 742 743 func (e *testRuntime) SetTracingEnabled(_ bool) { 744 panic("SetTracingEnabled not expected") 745 } 746 747 func (e *testRuntime) SetResourceOwnerChangeHandlerEnabled(_ bool) { 748 panic("SetResourceOwnerChangeHandlerEnabled not expected") 749 } 750 751 func (e *testRuntime) InvokeContractFunction(_ common.AddressLocation, _ string, _ []cadence.Value, _ []sema.Type, _ runtime.Context) (cadence.Value, error) { 752 panic("InvokeContractFunction not expected") 753 } 754 755 func (e *testRuntime) ExecuteScript(script runtime.Script, context runtime.Context) (cadence.Value, error) { 756 return e.executeScript(script, context) 757 } 758 759 func (e *testRuntime) ExecuteTransaction(script runtime.Script, context runtime.Context) error { 760 return e.executeTransaction(script, context) 761 } 762 763 func (*testRuntime) ParseAndCheckProgram(_ []byte, _ runtime.Context) (*interpreter.Program, error) { 764 panic("ParseAndCheckProgram not expected") 765 } 766 767 func (*testRuntime) SetCoverageReport(_ *runtime.CoverageReport) { 768 panic("SetCoverageReport not expected") 769 } 770 771 func (*testRuntime) SetContractUpdateValidationEnabled(_ bool) { 772 panic("SetContractUpdateValidationEnabled not expected") 773 } 774 775 func (*testRuntime) SetAtreeValidationEnabled(_ bool) { 776 panic("SetAtreeValidationEnabled not expected") 777 } 778 779 func (e *testRuntime) ReadStored(a common.Address, p cadence.Path, c runtime.Context) (cadence.Value, error) { 780 return e.readStored(a, p, c) 781 } 782 783 func (*testRuntime) ReadLinked(_ common.Address, _ cadence.Path, _ runtime.Context) (cadence.Value, error) { 784 panic("ReadLinked not expected") 785 } 786 787 func (*testRuntime) SetDebugger(_ *interpreter.Debugger) { 788 panic("SetDebugger not expected") 789 } 790 791 type RandomAddressGenerator struct{} 792 793 func (r *RandomAddressGenerator) NextAddress() (flow.Address, error) { 794 return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000))), nil 795 } 796 797 func (r *RandomAddressGenerator) CurrentAddress() flow.Address { 798 return flow.HexToAddress(fmt.Sprintf("0%d", rand.Intn(1000))) 799 } 800 801 func (r *RandomAddressGenerator) Bytes() []byte { 802 panic("not implemented") 803 } 804 805 func (r *RandomAddressGenerator) AddressCount() uint64 { 806 panic("not implemented") 807 } 808 809 func (testRuntime) Storage(runtime.Context) (*runtime.Storage, *interpreter.Interpreter, error) { 810 panic("Storage not expected") 811 } 812 813 type FixedAddressGenerator struct { 814 Address flow.Address 815 } 816 817 func (f *FixedAddressGenerator) NextAddress() (flow.Address, error) { 818 return f.Address, nil 819 } 820 821 func (f *FixedAddressGenerator) CurrentAddress() flow.Address { 822 return f.Address 823 } 824 825 func (f *FixedAddressGenerator) Bytes() []byte { 826 panic("not implemented") 827 } 828 829 func (f *FixedAddressGenerator) AddressCount() uint64 { 830 panic("not implemented") 831 } 832 833 func Test_AccountStatusRegistersAreIncluded(t *testing.T) { 834 835 address := flow.HexToAddress("1234") 836 fag := &FixedAddressGenerator{Address: address} 837 838 vm := fvm.NewVirtualMachine() 839 execCtx := fvm.NewContext() 840 841 ledger := testutil.RootBootstrappedLedger(vm, execCtx) 842 843 key, err := unittest.AccountKeyDefaultFixture() 844 require.NoError(t, err) 845 846 view := delta.NewView(func(owner, key string) (flow.RegisterValue, error) { 847 return ledger.Get(owner, key) 848 }) 849 txnState := state.NewTransactionState(view, state.DefaultParameters()) 850 accounts := environment.NewAccounts(txnState) 851 852 // account creation, signing of transaction and bootstrapping ledger should not be required for this test 853 // as freeze check should happen before a transaction signature is checked 854 // but we currently discard all the touches if it fails and any point 855 err = accounts.Create([]flow.AccountPublicKey{key.PublicKey(1000)}, address) 856 require.NoError(t, err) 857 858 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 859 trackerStorage := mocktracker.NewMockStorage() 860 861 prov := provider.NewProvider( 862 zerolog.Nop(), 863 metrics.NewNoopCollector(), 864 execution_data.DefaultSerializer, 865 bservice, 866 trackerStorage, 867 ) 868 869 me := new(modulemock.Local) 870 me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything). 871 Return(nil, nil) 872 873 exe, err := computer.NewBlockComputer( 874 vm, 875 execCtx, 876 metrics.NewNoopCollector(), 877 trace.NewNoopTracer(), 878 zerolog.Nop(), 879 committer.NewNoopViewCommitter(), 880 me, 881 prov) 882 require.NoError(t, err) 883 884 block := generateBlockWithVisitor(1, 1, fag, func(txBody *flow.TransactionBody) { 885 err := testutil.SignTransaction(txBody, txBody.Payer, *key, 0) 886 require.NoError(t, err) 887 }) 888 889 _, err = exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData()) 890 assert.NoError(t, err) 891 892 registerTouches := view.Interactions().RegisterTouches() 893 894 // make sure check for account status has been registered 895 id := flow.RegisterID{ 896 Owner: string(address.Bytes()), 897 Key: state.AccountStatusKey, 898 } 899 900 require.Contains(t, registerTouches, id) 901 } 902 903 func Test_ExecutingSystemCollection(t *testing.T) { 904 905 execCtx := fvm.NewContext( 906 fvm.WithChain(flow.Localnet.Chain()), 907 fvm.WithBlocks(&environment.NoopBlockFinder{}), 908 ) 909 910 vm := fvm.NewVirtualMachine() 911 912 rag := &RandomAddressGenerator{} 913 914 ledger := testutil.RootBootstrappedLedger(vm, execCtx) 915 916 committer := new(computermock.ViewCommitter) 917 committer.On("CommitView", mock.Anything, mock.Anything). 918 Return(nil, nil, nil, nil). 919 Times(1) // only system chunk 920 921 noopCollector := metrics.NewNoopCollector() 922 923 metrics := new(modulemock.ExecutionMetrics) 924 expectedNumberOfEvents := 2 925 expectedEventSize := 912 926 metrics.On("ExecutionCollectionExecuted", 927 mock.Anything, // duration 928 mock.Anything). // stats 929 Return(nil). 930 Times(1) // system collection 931 932 metrics.On("ExecutionTransactionExecuted", 933 mock.Anything, // duration 934 mock.Anything, // computation used 935 mock.Anything, // memory used 936 mock.Anything, // actual memory used 937 expectedNumberOfEvents, 938 expectedEventSize, 939 false). 940 Return(nil). 941 Times(1) // system chunk tx 942 943 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 944 trackerStorage := mocktracker.NewMockStorage() 945 946 prov := provider.NewProvider( 947 zerolog.Nop(), 948 noopCollector, 949 execution_data.DefaultSerializer, 950 bservice, 951 trackerStorage, 952 ) 953 954 me := new(modulemock.Local) 955 me.On("SignFunc", mock.Anything, mock.Anything, mock.Anything). 956 Return(nil, nil) 957 958 exe, err := computer.NewBlockComputer( 959 vm, 960 execCtx, 961 metrics, 962 trace.NewNoopTracer(), 963 zerolog.Nop(), 964 committer, 965 me, 966 prov) 967 require.NoError(t, err) 968 969 // create empty block, it will have system collection attached while executing 970 block := generateBlock(0, 0, rag) 971 972 view := delta.NewView(ledger.Get) 973 974 result, err := exe.ExecuteBlock(context.Background(), block, view, derived.NewEmptyDerivedBlockData()) 975 assert.NoError(t, err) 976 assert.Len(t, result.StateSnapshots, 1) // +1 system chunk 977 assert.Len(t, result.TransactionResults, 1) 978 979 assert.Empty(t, result.TransactionResults[0].ErrorMessage) 980 981 stats := result.CollectionStats(0) 982 // ignore computation and memory used 983 stats.ComputationUsed = 0 984 stats.MemoryUsed = 0 985 986 assert.Equal( 987 t, 988 module.ExecutionResultStats{ 989 EventCounts: expectedNumberOfEvents, 990 EventSize: expectedEventSize, 991 NumberOfRegistersTouched: 50, 992 NumberOfBytesWrittenToRegisters: 3404, 993 NumberOfCollections: 1, 994 NumberOfTransactions: 1, 995 }, 996 stats) 997 998 committer.AssertExpectations(t) 999 } 1000 1001 func generateBlock(collectionCount, transactionCount int, addressGenerator flow.AddressGenerator) *entity.ExecutableBlock { 1002 return generateBlockWithVisitor(collectionCount, transactionCount, addressGenerator, nil) 1003 } 1004 1005 func generateBlockWithVisitor(collectionCount, transactionCount int, addressGenerator flow.AddressGenerator, visitor func(body *flow.TransactionBody)) *entity.ExecutableBlock { 1006 collections := make([]*entity.CompleteCollection, collectionCount) 1007 guarantees := make([]*flow.CollectionGuarantee, collectionCount) 1008 completeCollections := make(map[flow.Identifier]*entity.CompleteCollection) 1009 1010 for i := 0; i < collectionCount; i++ { 1011 collection := generateCollection(transactionCount, addressGenerator, visitor) 1012 collections[i] = collection 1013 guarantees[i] = collection.Guarantee 1014 completeCollections[collection.Guarantee.ID()] = collection 1015 } 1016 1017 block := flow.Block{ 1018 Header: &flow.Header{ 1019 Timestamp: flow.GenesisTime, 1020 Height: 42, 1021 View: 42, 1022 }, 1023 Payload: &flow.Payload{ 1024 Guarantees: guarantees, 1025 }, 1026 } 1027 1028 return &entity.ExecutableBlock{ 1029 Block: &block, 1030 CompleteCollections: completeCollections, 1031 StartState: unittest.StateCommitmentPointerFixture(), 1032 } 1033 } 1034 1035 func generateCollection(transactionCount int, addressGenerator flow.AddressGenerator, visitor func(body *flow.TransactionBody)) *entity.CompleteCollection { 1036 transactions := make([]*flow.TransactionBody, transactionCount) 1037 1038 for i := 0; i < transactionCount; i++ { 1039 nextAddress, err := addressGenerator.NextAddress() 1040 if err != nil { 1041 panic(fmt.Errorf("cannot generate next address in test: %w", err)) 1042 } 1043 txBody := &flow.TransactionBody{ 1044 Payer: nextAddress, // a unique payer for each tx to generate a unique id 1045 Script: []byte("transaction { execute {} }"), 1046 } 1047 if visitor != nil { 1048 visitor(txBody) 1049 } 1050 transactions[i] = txBody 1051 } 1052 1053 collection := flow.Collection{Transactions: transactions} 1054 1055 guarantee := &flow.CollectionGuarantee{CollectionID: collection.ID()} 1056 1057 return &entity.CompleteCollection{ 1058 Guarantee: guarantee, 1059 Transactions: transactions, 1060 } 1061 } 1062 1063 func generateEvents(eventCount int, txIndex uint32) []flow.Event { 1064 events := make([]flow.Event, eventCount) 1065 for i := 0; i < eventCount; i++ { 1066 // creating some dummy event 1067 event := flow.Event{Type: "whatever", EventIndex: uint32(i), TransactionIndex: txIndex} 1068 events[i] = event 1069 } 1070 return events 1071 }