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