github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/computation/execution_verification_test.go (about) 1 package computation 2 3 import ( 4 "context" 5 "crypto/rand" 6 "fmt" 7 "testing" 8 9 "github.com/ipfs/boxo/blockstore" 10 "github.com/ipfs/go-datastore" 11 dssync "github.com/ipfs/go-datastore/sync" 12 "github.com/onflow/cadence" 13 jsoncdc "github.com/onflow/cadence/encoding/json" 14 "github.com/rs/zerolog" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 18 "github.com/onflow/crypto" 19 20 "github.com/onflow/flow-go/engine/execution" 21 "github.com/onflow/flow-go/engine/execution/computation/committer" 22 "github.com/onflow/flow-go/engine/execution/computation/computer" 23 "github.com/onflow/flow-go/engine/execution/state" 24 bootstrapexec "github.com/onflow/flow-go/engine/execution/state/bootstrap" 25 "github.com/onflow/flow-go/engine/execution/testutil" 26 "github.com/onflow/flow-go/engine/execution/utils" 27 "github.com/onflow/flow-go/engine/testutil/mocklocal" 28 "github.com/onflow/flow-go/engine/verification/fetcher" 29 "github.com/onflow/flow-go/fvm" 30 "github.com/onflow/flow-go/fvm/blueprints" 31 "github.com/onflow/flow-go/fvm/environment" 32 "github.com/onflow/flow-go/fvm/errors" 33 "github.com/onflow/flow-go/fvm/storage/derived" 34 "github.com/onflow/flow-go/fvm/systemcontracts" 35 completeLedger "github.com/onflow/flow-go/ledger/complete" 36 "github.com/onflow/flow-go/ledger/complete/wal/fixtures" 37 "github.com/onflow/flow-go/model/flow" 38 "github.com/onflow/flow-go/model/verification" 39 "github.com/onflow/flow-go/module/chunks" 40 "github.com/onflow/flow-go/module/executiondatasync/execution_data" 41 exedataprovider "github.com/onflow/flow-go/module/executiondatasync/provider" 42 mocktracker "github.com/onflow/flow-go/module/executiondatasync/tracker/mock" 43 "github.com/onflow/flow-go/module/metrics" 44 requesterunit "github.com/onflow/flow-go/module/state_synchronization/requester/unittest" 45 "github.com/onflow/flow-go/module/trace" 46 "github.com/onflow/flow-go/utils/unittest" 47 ) 48 49 const ( 50 testVerifyMaxConcurrency = 2 51 ) 52 53 var chain = flow.Emulator.Chain() 54 55 // In the following tests the system transaction is expected to fail, as the epoch related things are not set up properly. 56 // This is not relevant to the test, as only the non-system transactions are tested. 57 58 func Test_ExecutionMatchesVerification(t *testing.T) { 59 t.Run("empty block", func(t *testing.T) { 60 executeBlockAndVerify(t, 61 [][]*flow.TransactionBody{}, 62 fvm.DefaultTransactionFees, 63 fvm.DefaultMinimumStorageReservation) 64 }) 65 66 t.Run("single transaction event", func(t *testing.T) { 67 68 deployTx := blueprints.DeployContractTransaction(chain.ServiceAddress(), []byte(""+ 69 `access(all) contract Foo { 70 access(all) event FooEvent(x: Int, y: Int) 71 72 access(all) fun emitEvent() { 73 emit FooEvent(x: 2, y: 1) 74 } 75 }`), "Foo") 76 77 emitTx := &flow.TransactionBody{ 78 Script: []byte(fmt.Sprintf(` 79 import Foo from 0x%s 80 transaction { 81 prepare() {} 82 execute { 83 Foo.emitEvent() 84 } 85 }`, chain.ServiceAddress())), 86 } 87 88 err := testutil.SignTransactionAsServiceAccount(deployTx, 0, chain) 89 require.NoError(t, err) 90 91 err = testutil.SignTransactionAsServiceAccount(emitTx, 1, chain) 92 require.NoError(t, err) 93 94 cr := executeBlockAndVerify(t, [][]*flow.TransactionBody{ 95 { 96 deployTx, emitTx, 97 }, 98 }, fvm.BootstrapProcedureFeeParameters{}, fvm.DefaultMinimumStorageReservation) 99 100 colResult := cr.CollectionExecutionResultAt(0) 101 txResults := colResult.TransactionResults() 102 events := colResult.Events() 103 // ensure event is emitted 104 require.Empty(t, txResults[0].ErrorMessage) 105 require.Empty(t, txResults[1].ErrorMessage) 106 require.Len(t, events, 2) 107 require.Equal(t, flow.EventType(fmt.Sprintf("A.%s.Foo.FooEvent", chain.ServiceAddress())), events[1].Type) 108 }) 109 110 t.Run("multiple collections events", func(t *testing.T) { 111 112 deployTx := blueprints.DeployContractTransaction(chain.ServiceAddress(), []byte(""+ 113 `access(all) contract Foo { 114 access(all) event FooEvent(x: Int, y: Int) 115 116 access(all) fun emitEvent() { 117 emit FooEvent(x: 2, y: 1) 118 } 119 }`), "Foo") 120 121 emitTx1 := flow.TransactionBody{ 122 Script: []byte(fmt.Sprintf(` 123 import Foo from 0x%s 124 transaction { 125 prepare() {} 126 execute { 127 Foo.emitEvent() 128 } 129 }`, chain.ServiceAddress())), 130 } 131 132 // copy txs 133 emitTx2 := emitTx1 134 emitTx3 := emitTx1 135 136 err := testutil.SignTransactionAsServiceAccount(deployTx, 0, chain) 137 require.NoError(t, err) 138 139 err = testutil.SignTransactionAsServiceAccount(&emitTx1, 1, chain) 140 require.NoError(t, err) 141 err = testutil.SignTransactionAsServiceAccount(&emitTx2, 2, chain) 142 require.NoError(t, err) 143 err = testutil.SignTransactionAsServiceAccount(&emitTx3, 3, chain) 144 require.NoError(t, err) 145 146 cr := executeBlockAndVerify(t, [][]*flow.TransactionBody{ 147 { 148 deployTx, &emitTx1, 149 }, 150 { 151 &emitTx2, 152 }, 153 { 154 &emitTx3, 155 }, 156 }, fvm.BootstrapProcedureFeeParameters{}, fvm.DefaultMinimumStorageReservation) 157 158 verifyTxResults := func(t *testing.T, colIndex, expResCount int) { 159 colResult := cr.CollectionExecutionResultAt(colIndex) 160 txResults := colResult.TransactionResults() 161 require.Len(t, txResults, expResCount) 162 for i := 0; i < expResCount; i++ { 163 require.Empty(t, txResults[i].ErrorMessage) 164 } 165 } 166 167 verifyEvents := func(t *testing.T, colIndex int, eventTypes []flow.EventType) { 168 colResult := cr.CollectionExecutionResultAt(colIndex) 169 events := colResult.Events() 170 require.Len(t, events, len(eventTypes)) 171 for i, event := range events { 172 require.Equal(t, event.Type, eventTypes[i]) 173 } 174 } 175 176 expEventType1 := flow.EventType("flow.AccountContractAdded") 177 expEventType2 := flow.EventType(fmt.Sprintf("A.%s.Foo.FooEvent", chain.ServiceAddress())) 178 179 // first collection 180 verifyTxResults(t, 0, 2) 181 verifyEvents(t, 0, []flow.EventType{expEventType1, expEventType2}) 182 183 // second collection 184 verifyTxResults(t, 1, 1) 185 verifyEvents(t, 1, []flow.EventType{expEventType2}) 186 187 // 3rd collection 188 verifyTxResults(t, 2, 1) 189 verifyEvents(t, 2, []flow.EventType{expEventType2}) 190 }) 191 192 t.Run("with failed storage limit", func(t *testing.T) { 193 194 accountPrivKey, createAccountTx := testutil.CreateAccountCreationTransaction(t, chain) 195 196 // this should return the address of newly created account 197 accountAddress, err := chain.AddressAtIndex(systemcontracts.LastSystemAccountIndex + 1) 198 require.NoError(t, err) 199 200 err = testutil.SignTransactionAsServiceAccount(createAccountTx, 0, chain) 201 require.NoError(t, err) 202 203 addKeyTx := testutil.CreateAddAnAccountKeyMultipleTimesTransaction(t, &accountPrivKey, 100).AddAuthorizer(accountAddress) 204 err = testutil.SignTransaction(addKeyTx, accountAddress, accountPrivKey, 0) 205 require.NoError(t, err) 206 207 minimumStorage, err := cadence.NewUFix64("0.00011661") 208 require.NoError(t, err) 209 210 cr := executeBlockAndVerify(t, [][]*flow.TransactionBody{ 211 { 212 createAccountTx, 213 }, 214 { 215 addKeyTx, 216 }, 217 }, fvm.DefaultTransactionFees, minimumStorage) 218 219 colResult := cr.CollectionExecutionResultAt(0) 220 txResults := colResult.TransactionResults() 221 // storage limit error 222 assert.Len(t, txResults, 1) 223 assert.Equal(t, "", txResults[0].ErrorMessage) 224 // ensure events from the first transaction is emitted 225 require.Len(t, colResult.Events(), 14) 226 227 colResult = cr.CollectionExecutionResultAt(1) 228 txResults = colResult.TransactionResults() 229 assert.Len(t, txResults, 1) 230 // storage limit error 231 assert.Contains(t, txResults[0].ErrorMessage, errors.ErrCodeStorageCapacityExceeded.String()) 232 // ensure fee deduction events are emitted even though tx fails 233 require.Len(t, colResult.Events(), 5) 234 }) 235 236 t.Run("with failed transaction fee deduction", func(t *testing.T) { 237 accountPrivKey, createAccountTx := testutil.CreateAccountCreationTransaction(t, chain) 238 // this should return the address of newly created account 239 accountAddress, err := chain.AddressAtIndex(systemcontracts.LastSystemAccountIndex + 1) 240 require.NoError(t, err) 241 242 err = testutil.SignTransactionAsServiceAccount(createAccountTx, 0, chain) 243 require.NoError(t, err) 244 245 spamTx := &flow.TransactionBody{ 246 Script: []byte(` 247 transaction { 248 prepare() {} 249 execute { 250 var s: Int256 = 1024102410241024 251 var i = 0 252 var a = Int256(7) 253 var b = Int256(5) 254 var c = Int256(2) 255 while i < 150000 { 256 s = s * a 257 s = s / b 258 s = s / c 259 i = i + 1 260 } 261 log(i) 262 } 263 }`), 264 } 265 266 spamTx.SetComputeLimit(800000) 267 err = testutil.SignTransaction(spamTx, accountAddress, accountPrivKey, 0) 268 require.NoError(t, err) 269 270 require.NoError(t, err) 271 272 cr := executeBlockAndVerifyWithParameters(t, [][]*flow.TransactionBody{ 273 { 274 createAccountTx, 275 spamTx, 276 }, 277 }, 278 []fvm.Option{ 279 fvm.WithTransactionFeesEnabled(true), 280 fvm.WithAccountStorageLimit(true), 281 // make sure we don't run out of memory first. 282 fvm.WithMemoryLimit(20_000_000_000), 283 }, []fvm.BootstrapProcedureOption{ 284 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 285 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 286 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 287 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 288 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 289 }) 290 291 colResult := cr.CollectionExecutionResultAt(0) 292 txResults := colResult.TransactionResults() 293 events := colResult.Events() 294 295 // no error 296 assert.Equal(t, txResults[0].ErrorMessage, "") 297 298 // ensure events from the first transaction is emitted. Since transactions are in the same block, get all events from Events[0] 299 transactionEvents := 0 300 for _, event := range events { 301 if event.TransactionID == txResults[0].TransactionID { 302 transactionEvents += 1 303 } 304 } 305 require.Equal(t, 14, transactionEvents) 306 307 assert.Contains(t, txResults[1].ErrorMessage, errors.ErrCodeStorageCapacityExceeded.String()) 308 309 // ensure tx fee deduction events are emitted even though tx failed 310 transactionEvents = 0 311 for _, event := range events { 312 if event.TransactionID == txResults[1].TransactionID { 313 transactionEvents += 1 314 } 315 } 316 require.Equal(t, 5, transactionEvents) 317 }) 318 319 } 320 321 func TestTransactionFeeDeduction(t *testing.T) { 322 323 type testCase struct { 324 name string 325 fundWith uint64 326 tryToTransfer uint64 327 checkResult func(t *testing.T, cr *execution.ComputationResult) 328 } 329 330 txFees := uint64(1_000) 331 fundingAmount := uint64(100_000_000) 332 transferAmount := uint64(123_456) 333 334 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 335 336 depositedEvent := fmt.Sprintf("A.%s.FlowToken.TokensDeposited", sc.FlowToken.Address) 337 withdrawnEvent := fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", sc.FlowToken.Address) 338 339 testCases := []testCase{ 340 { 341 name: "Transaction fee deduction emits events", 342 fundWith: fundingAmount, 343 tryToTransfer: 0, 344 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 345 txResults := cr.AllTransactionResults() 346 347 require.Empty(t, txResults[0].ErrorMessage) 348 require.Empty(t, txResults[1].ErrorMessage) 349 require.Empty(t, txResults[2].ErrorMessage) 350 351 var deposits []flow.Event 352 var withdraws []flow.Event 353 354 // events of the first collection 355 events := cr.CollectionExecutionResultAt(2).Events() 356 for _, e := range events { 357 if string(e.Type) == depositedEvent { 358 deposits = append(deposits, e) 359 } 360 if string(e.Type) == withdrawnEvent { 361 withdraws = append(withdraws, e) 362 } 363 } 364 365 require.Len(t, deposits, 1) 366 require.Len(t, withdraws, 2) 367 }, 368 }, 369 { 370 name: "If just enough balance, fees are still deducted", 371 fundWith: txFees + transferAmount, 372 tryToTransfer: transferAmount, 373 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 374 txResults := cr.AllTransactionResults() 375 376 require.Empty(t, txResults[0].ErrorMessage) 377 require.Empty(t, txResults[1].ErrorMessage) 378 require.Empty(t, txResults[2].ErrorMessage) 379 380 var deposits []flow.Event 381 var withdraws []flow.Event 382 383 // events of the last collection 384 events := cr.CollectionExecutionResultAt(2).Events() 385 for _, e := range events { 386 if string(e.Type) == depositedEvent { 387 deposits = append(deposits, e) 388 } 389 if string(e.Type) == withdrawnEvent { 390 withdraws = append(withdraws, e) 391 } 392 } 393 394 require.Len(t, deposits, 1) 395 require.Len(t, withdraws, 2) 396 }, 397 }, 398 { 399 // this is an edge case that is not applicable to any network. 400 // If storage limits were on this would fail due to storage limits 401 name: "If not enough balance, transaction succeeds and fees are deducted to 0", 402 fundWith: txFees, 403 tryToTransfer: 1, 404 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 405 txResults := cr.AllTransactionResults() 406 407 require.Empty(t, txResults[0].ErrorMessage) 408 require.Empty(t, txResults[1].ErrorMessage) 409 require.Empty(t, txResults[2].ErrorMessage) 410 411 var deposits []flow.Event 412 var withdraws []flow.Event 413 414 // events of the last collection 415 events := cr.CollectionExecutionResultAt(2).Events() 416 for _, e := range events { 417 if string(e.Type) == depositedEvent { 418 deposits = append(deposits, e) 419 } 420 if string(e.Type) == withdrawnEvent { 421 withdraws = append(withdraws, e) 422 } 423 } 424 425 require.Len(t, deposits, 1) 426 require.Len(t, withdraws, 2) 427 }, 428 }, 429 { 430 name: "If tx fails, fees are deducted", 431 fundWith: fundingAmount, 432 tryToTransfer: 2 * fundingAmount, 433 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 434 txResults := cr.AllTransactionResults() 435 436 require.Empty(t, txResults[0].ErrorMessage) 437 require.Empty(t, txResults[1].ErrorMessage) 438 require.Contains(t, txResults[2].ErrorMessage, "Error Code: 1101") 439 440 var deposits []flow.Event 441 var withdraws []flow.Event 442 443 // events of the last collection 444 events := cr.CollectionExecutionResultAt(2).Events() 445 for _, e := range events { 446 if string(e.Type) == depositedEvent { 447 deposits = append(deposits, e) 448 } 449 if string(e.Type) == withdrawnEvent { 450 withdraws = append(withdraws, e) 451 } 452 } 453 454 require.Len(t, deposits, 1) 455 require.Len(t, withdraws, 1) 456 }, 457 }, 458 } 459 460 testCasesWithStorageEnabled := []testCase{ 461 { 462 name: "Transaction fee deduction emits events", 463 fundWith: fundingAmount, 464 tryToTransfer: 0, 465 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 466 txResults := cr.AllTransactionResults() 467 468 require.Empty(t, txResults[0].ErrorMessage) 469 require.Empty(t, txResults[1].ErrorMessage) 470 require.Empty(t, txResults[2].ErrorMessage) 471 472 var deposits []flow.Event 473 var withdraws []flow.Event 474 475 // events of the last collection 476 events := cr.CollectionExecutionResultAt(2).Events() 477 for _, e := range events { 478 if string(e.Type) == depositedEvent { 479 deposits = append(deposits, e) 480 } 481 if string(e.Type) == withdrawnEvent { 482 withdraws = append(withdraws, e) 483 } 484 } 485 486 require.Len(t, deposits, 1) 487 require.Len(t, withdraws, 2) 488 }, 489 }, 490 { 491 name: "If just enough balance, fees are deducted", 492 fundWith: txFees + transferAmount, 493 tryToTransfer: transferAmount, 494 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 495 txResults := cr.AllTransactionResults() 496 497 require.Empty(t, txResults[0].ErrorMessage) 498 require.Empty(t, txResults[1].ErrorMessage) 499 require.Empty(t, txResults[2].ErrorMessage) 500 501 var deposits []flow.Event 502 var withdraws []flow.Event 503 504 // events of the last collection 505 events := cr.CollectionExecutionResultAt(2).Events() 506 for _, e := range events { 507 if string(e.Type) == depositedEvent { 508 deposits = append(deposits, e) 509 } 510 if string(e.Type) == withdrawnEvent { 511 withdraws = append(withdraws, e) 512 } 513 } 514 515 require.Len(t, deposits, 1) 516 require.Len(t, withdraws, 2) 517 }, 518 }, 519 { 520 name: "If tx fails, fees are still deducted and fee deduction events are emitted", 521 fundWith: fundingAmount, 522 tryToTransfer: 2 * fundingAmount, 523 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 524 txResults := cr.AllTransactionResults() 525 526 require.Empty(t, txResults[0].ErrorMessage) 527 require.Empty(t, txResults[1].ErrorMessage) 528 require.Contains(t, txResults[2].ErrorMessage, "Error Code: 1101") 529 530 var deposits []flow.Event 531 var withdraws []flow.Event 532 533 // events of the last collection 534 events := cr.CollectionExecutionResultAt(2).Events() 535 for _, e := range events { 536 if string(e.Type) == depositedEvent { 537 deposits = append(deposits, e) 538 } 539 if string(e.Type) == withdrawnEvent { 540 withdraws = append(withdraws, e) 541 } 542 } 543 544 require.Len(t, deposits, 1) 545 require.Len(t, withdraws, 1) 546 }, 547 }, 548 { 549 name: "If balance at minimum, transaction fails, fees are deducted and fee deduction events are emitted", 550 fundWith: 0, 551 tryToTransfer: 0, 552 checkResult: func(t *testing.T, cr *execution.ComputationResult) { 553 txResults := cr.AllTransactionResults() 554 555 require.Empty(t, txResults[0].ErrorMessage) 556 require.Empty(t, txResults[1].ErrorMessage) 557 require.Contains(t, txResults[2].ErrorMessage, errors.ErrCodeStorageCapacityExceeded.String()) 558 559 var deposits []flow.Event 560 var withdraws []flow.Event 561 562 // events of the last collection 563 events := cr.CollectionExecutionResultAt(2).Events() 564 for _, e := range events { 565 if string(e.Type) == depositedEvent { 566 deposits = append(deposits, e) 567 } 568 if string(e.Type) == withdrawnEvent { 569 withdraws = append(withdraws, e) 570 } 571 } 572 573 require.Len(t, deposits, 1) 574 require.Len(t, withdraws, 1) 575 }, 576 }, 577 } 578 579 transferTokensTx := func(chain flow.Chain) *flow.TransactionBody { 580 return flow.NewTransactionBody(). 581 SetScript([]byte(fmt.Sprintf(` 582 // This transaction is a template for a transaction that 583 // could be used by anyone to send tokens to another account 584 // that has been set up to receive tokens. 585 // 586 // The withdraw amount and the account from getAccount 587 // would be the parameters to the transaction 588 589 import FungibleToken from 0x%s 590 import FlowToken from 0x%s 591 592 transaction(amount: UFix64, to: Address) { 593 594 // The Vault resource that holds the tokens that are being transferred 595 let sentVault: @{FungibleToken.Vault} 596 597 prepare(signer: auth(BorrowValue) &Account) { 598 599 // Get a reference to the signer's stored vault 600 let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) 601 ?? panic("Could not borrow reference to the owner's Vault!") 602 603 // Withdraw tokens from the signer's stored vault 604 self.sentVault <- vaultRef.withdraw(amount: amount) 605 } 606 607 execute { 608 609 // Get the recipient's public account object 610 let recipient = getAccount(to) 611 612 // Get a reference to the recipient's Receiver 613 let receiverRef = recipient.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 614 ?? panic("Could not borrow receiver reference to the recipient's Vault") 615 616 // Deposit the withdrawn tokens in the recipient's receiver 617 receiverRef.deposit(from: <-self.sentVault) 618 } 619 }`, sc.FungibleToken.Address, sc.FlowToken.Address)), 620 ) 621 } 622 623 runTx := func(tc testCase, 624 opts []fvm.Option, 625 bootstrapOpts []fvm.BootstrapProcedureOption) func(t *testing.T) { 626 return func(t *testing.T) { 627 // ==== Create an account ==== 628 privateKey, createAccountTx := testutil.CreateAccountCreationTransaction(t, chain) 629 630 // this should return the address of newly created account 631 address, err := chain.AddressAtIndex(systemcontracts.LastSystemAccountIndex + 1) 632 require.NoError(t, err) 633 634 err = testutil.SignTransactionAsServiceAccount(createAccountTx, 0, chain) 635 require.NoError(t, err) 636 637 // ==== Transfer tokens to new account ==== 638 transferTx := transferTokensTx(chain). 639 AddAuthorizer(chain.ServiceAddress()). 640 AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.fundWith))). 641 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address))) 642 643 transferTx.SetProposalKey(chain.ServiceAddress(), 0, 1) 644 transferTx.SetPayer(chain.ServiceAddress()) 645 646 err = testutil.SignEnvelope( 647 transferTx, 648 chain.ServiceAddress(), 649 unittest.ServiceAccountPrivateKey, 650 ) 651 require.NoError(t, err) 652 653 // ==== Transfer tokens from new account ==== 654 655 transferTx2 := transferTokensTx(chain). 656 AddAuthorizer(address). 657 AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.tryToTransfer))). 658 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress()))) 659 660 transferTx2.SetProposalKey(address, 0, 0) 661 transferTx2.SetPayer(address) 662 663 err = testutil.SignEnvelope( 664 transferTx2, 665 address, 666 privateKey, 667 ) 668 require.NoError(t, err) 669 670 cr := executeBlockAndVerifyWithParameters(t, [][]*flow.TransactionBody{ 671 { 672 createAccountTx, 673 }, 674 { 675 transferTx, 676 }, 677 { 678 transferTx2, 679 }, 680 }, opts, bootstrapOpts) 681 682 tc.checkResult(t, cr) 683 } 684 } 685 686 for i, tc := range testCases { 687 t.Run(fmt.Sprintf("Transaction Fees without storage %d: %s", i, tc.name), runTx(tc, []fvm.Option{ 688 fvm.WithTransactionFeesEnabled(true), 689 fvm.WithAccountStorageLimit(false), 690 }, []fvm.BootstrapProcedureOption{ 691 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 692 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 693 })) 694 } 695 696 for i, tc := range testCasesWithStorageEnabled { 697 t.Run(fmt.Sprintf("Transaction Fees with storage %d: %s", i, tc.name), runTx(tc, []fvm.Option{ 698 fvm.WithTransactionFeesEnabled(true), 699 fvm.WithAccountStorageLimit(true), 700 }, []fvm.BootstrapProcedureOption{ 701 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 702 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 703 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 704 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 705 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 706 })) 707 } 708 } 709 710 func executeBlockAndVerifyWithParameters(t *testing.T, 711 txs [][]*flow.TransactionBody, 712 opts []fvm.Option, 713 bootstrapOpts []fvm.BootstrapProcedureOption) *execution.ComputationResult { 714 vm := fvm.NewVirtualMachine() 715 716 logger := zerolog.Nop() 717 718 opts = append(opts, fvm.WithChain(chain)) 719 opts = append(opts, fvm.WithLogger(logger)) 720 opts = append(opts, fvm.WithBlocks(&environment.NoopBlockFinder{})) 721 722 fvmContext := 723 fvm.NewContext( 724 opts..., 725 ) 726 727 collector := metrics.NewNoopCollector() 728 tracer := trace.NewNoopTracer() 729 730 wal := &fixtures.NoopWAL{} 731 732 ledger, err := completeLedger.NewLedger(wal, 100, collector, logger, completeLedger.DefaultPathFinderVersion) 733 require.NoError(t, err) 734 735 compactor := fixtures.NewNoopCompactor(ledger) 736 <-compactor.Ready() 737 defer func() { 738 <-ledger.Done() 739 <-compactor.Done() 740 }() 741 742 bootstrapper := bootstrapexec.NewBootstrapper(logger) 743 744 initialCommit, err := bootstrapper.BootstrapLedger( 745 ledger, 746 unittest.ServiceAccountPublicKey, 747 chain, 748 bootstrapOpts..., 749 ) 750 751 require.NoError(t, err) 752 753 ledgerCommiter := committer.NewLedgerViewCommitter(ledger, tracer) 754 755 bservice := requesterunit.MockBlobService(blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore()))) 756 trackerStorage := mocktracker.NewMockStorage() 757 758 prov := exedataprovider.NewProvider( 759 zerolog.Nop(), 760 metrics.NewNoopCollector(), 761 execution_data.DefaultSerializer, 762 bservice, 763 trackerStorage, 764 ) 765 766 // generates signing identity including staking key for signing 767 myIdentity := unittest.IdentityFixture() 768 seed := make([]byte, crypto.KeyGenSeedMinLen) 769 n, err := rand.Read(seed) 770 require.Equal(t, n, crypto.KeyGenSeedMinLen) 771 require.NoError(t, err) 772 sk, err := crypto.GeneratePrivateKey(crypto.BLSBLS12381, seed) 773 require.NoError(t, err) 774 myIdentity.StakingPubKey = sk.PublicKey() 775 me := mocklocal.NewMockLocal(sk, myIdentity.ID(), t) 776 777 // used by computer to generate the prng used in the service tx 778 stateForRandomSource := testutil.ProtocolStateWithSourceFixture(nil) 779 780 blockComputer, err := computer.NewBlockComputer( 781 vm, 782 fvmContext, 783 collector, 784 tracer, 785 logger, 786 ledgerCommiter, 787 me, 788 prov, 789 nil, 790 stateForRandomSource, 791 testVerifyMaxConcurrency) 792 require.NoError(t, err) 793 794 executableBlock := unittest.ExecutableBlockFromTransactions(chain.ChainID(), txs) 795 executableBlock.StartState = &initialCommit 796 797 prevResultId := unittest.IdentifierFixture() 798 computationResult, err := blockComputer.ExecuteBlock( 799 context.Background(), 800 prevResultId, 801 executableBlock, 802 state.NewLedgerStorageSnapshot( 803 ledger, 804 initialCommit), 805 derived.NewEmptyDerivedBlockData(0)) 806 require.NoError(t, err) 807 808 spockHasher := utils.NewSPOCKHasher() 809 810 for i := 0; i < computationResult.BlockExecutionResult.Size(); i++ { 811 res := computationResult.CollectionExecutionResultAt(i) 812 snapshot := res.ExecutionSnapshot() 813 valid, err := crypto.SPOCKVerifyAgainstData( 814 myIdentity.StakingPubKey, 815 computationResult.Spocks[i], 816 snapshot.SpockSecret, 817 spockHasher) 818 require.NoError(t, err) 819 require.True(t, valid) 820 } 821 822 receipt := computationResult.ExecutionReceipt 823 receiptID := receipt.ID() 824 valid, err := myIdentity.StakingPubKey.Verify( 825 receipt.ExecutorSignature, 826 receiptID[:], 827 utils.NewExecutionReceiptHasher()) 828 829 require.NoError(t, err) 830 require.True(t, valid) 831 832 chdps := computationResult.AllChunkDataPacks() 833 require.Equal(t, len(chdps), len(receipt.Spocks)) 834 835 er := &computationResult.ExecutionResult 836 837 verifier := chunks.NewChunkVerifier(vm, fvmContext, logger) 838 839 vcds := make([]*verification.VerifiableChunkData, er.Chunks.Len()) 840 841 for i, chunk := range er.Chunks { 842 isSystemChunk := i == er.Chunks.Len()-1 843 offsetForChunk, err := fetcher.TransactionOffsetForChunk(er.Chunks, chunk.Index) 844 require.NoError(t, err) 845 846 vcds[i] = &verification.VerifiableChunkData{ 847 IsSystemChunk: isSystemChunk, 848 Chunk: chunk, 849 Header: executableBlock.Block.Header, 850 Result: er, 851 ChunkDataPack: chdps[i], 852 EndState: chunk.EndState, 853 TransactionOffset: offsetForChunk, 854 // returns the same RandomSource used by the computer 855 Snapshot: stateForRandomSource.AtBlockID(chunk.BlockID), 856 } 857 } 858 859 require.Len(t, vcds, len(txs)+1) // +1 for system chunk 860 861 for _, vcd := range vcds { 862 spockSecret, err := verifier.Verify(vcd) 863 assert.NoError(t, err) 864 assert.NotNil(t, spockSecret) 865 } 866 867 return computationResult 868 } 869 870 func executeBlockAndVerify(t *testing.T, 871 txs [][]*flow.TransactionBody, 872 txFees fvm.BootstrapProcedureFeeParameters, 873 minStorageBalance cadence.UFix64) *execution.ComputationResult { 874 return executeBlockAndVerifyWithParameters(t, 875 txs, 876 []fvm.Option{ 877 fvm.WithTransactionFeesEnabled(true), 878 fvm.WithAccountStorageLimit(true), 879 }, []fvm.BootstrapProcedureOption{ 880 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 881 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 882 fvm.WithMinimumStorageReservation(minStorageBalance), 883 fvm.WithTransactionFee(txFees), 884 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 885 }) 886 }