github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/evm_test.go (about) 1 package evm_test 2 3 import ( 4 "encoding/binary" 5 "encoding/hex" 6 "fmt" 7 "math" 8 "math/big" 9 "testing" 10 11 "github.com/onflow/cadence/runtime/common" 12 13 "github.com/onflow/cadence/encoding/ccf" 14 gethTypes "github.com/onflow/go-ethereum/core/types" 15 "github.com/onflow/go-ethereum/rlp" 16 "github.com/stretchr/testify/assert" 17 18 "github.com/onflow/cadence" 19 "github.com/onflow/cadence/encoding/json" 20 "github.com/stretchr/testify/require" 21 22 "github.com/onflow/flow-go/engine/execution/testutil" 23 "github.com/onflow/flow-go/fvm" 24 "github.com/onflow/flow-go/fvm/crypto" 25 envMock "github.com/onflow/flow-go/fvm/environment/mock" 26 "github.com/onflow/flow-go/fvm/evm" 27 "github.com/onflow/flow-go/fvm/evm/stdlib" 28 "github.com/onflow/flow-go/fvm/evm/testutils" 29 . "github.com/onflow/flow-go/fvm/evm/testutils" 30 "github.com/onflow/flow-go/fvm/evm/types" 31 "github.com/onflow/flow-go/fvm/storage/snapshot" 32 "github.com/onflow/flow-go/fvm/systemcontracts" 33 "github.com/onflow/flow-go/model/flow" 34 "github.com/onflow/flow-go/utils/unittest" 35 ) 36 37 func TestEVMRun(t *testing.T) { 38 t.Parallel() 39 40 chain := flow.Emulator.Chain() 41 t.Run("testing EVM.run (happy case)", func(t *testing.T) { 42 43 t.Parallel() 44 RunWithNewEnvironment(t, 45 chain, func( 46 ctx fvm.Context, 47 vm fvm.VM, 48 snapshot snapshot.SnapshotTree, 49 testContract *TestContract, 50 testAccount *EOATestAccount, 51 ) { 52 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 53 code := []byte(fmt.Sprintf( 54 ` 55 import EVM from %s 56 57 transaction(tx: [UInt8], coinbaseBytes: [UInt8; 20]){ 58 prepare(account: &Account) { 59 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 60 let res = EVM.run(tx: tx, coinbase: coinbase) 61 62 assert(res.status == EVM.Status.successful, message: "unexpected status") 63 assert(res.errorCode == 0, message: "unexpected error code") 64 assert(res.deployedContract == nil, message: "unexpected deployed contract") 65 } 66 } 67 `, 68 sc.EVMContract.Address.HexWithPrefix(), 69 )) 70 71 num := int64(12) 72 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 73 testContract.DeployedAt.ToCommon(), 74 testContract.MakeCallData(t, "store", big.NewInt(num)), 75 big.NewInt(0), 76 uint64(100_000), 77 big.NewInt(0), 78 ) 79 80 innerTx := cadence.NewArray( 81 ConvertToCadence(innerTxBytes), 82 ).WithType(stdlib.EVMTransactionBytesCadenceType) 83 84 coinbase := cadence.NewArray( 85 ConvertToCadence(testAccount.Address().Bytes()), 86 ).WithType(stdlib.EVMAddressBytesCadenceType) 87 88 tx := fvm.Transaction( 89 flow.NewTransactionBody(). 90 SetScript(code). 91 AddAuthorizer(sc.FlowServiceAccount.Address). 92 AddArgument(json.MustEncode(innerTx)). 93 AddArgument(json.MustEncode(coinbase)), 94 0) 95 96 state, output, err := vm.Run( 97 ctx, 98 tx, 99 snapshot) 100 require.NoError(t, err) 101 require.NoError(t, output.Err) 102 require.NotEmpty(t, state.WriteSet) 103 104 // assert event fiedls are correct 105 require.Len(t, output.Events, 2) 106 107 blockEvent := output.Events[1] 108 109 assert.Equal( 110 t, 111 common.NewAddressLocation( 112 nil, 113 common.Address(sc.EVMContract.Address), 114 string(types.EventTypeBlockExecuted), 115 ).ID(), 116 string(blockEvent.Type), 117 ) 118 119 ev, err := ccf.Decode(nil, blockEvent.Payload) 120 require.NoError(t, err) 121 cadenceEvent, ok := ev.(cadence.Event) 122 require.True(t, ok) 123 124 blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) 125 require.NoError(t, err) 126 require.NotEmpty(t, blockEventPayload.Hash) 127 128 txEvent := output.Events[0] 129 130 assert.Equal( 131 t, 132 common.NewAddressLocation( 133 nil, 134 common.Address(sc.EVMContract.Address), 135 string(types.EventTypeTransactionExecuted), 136 ).ID(), 137 string(txEvent.Type), 138 ) 139 140 ev, err = ccf.Decode(nil, txEvent.Payload) 141 require.NoError(t, err) 142 cadenceEvent, ok = ev.(cadence.Event) 143 require.True(t, ok) 144 145 txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent) 146 require.NoError(t, err) 147 require.NotEmpty(t, txEventPayload.Hash) 148 require.Equal(t, hex.EncodeToString(innerTxBytes), txEventPayload.Payload) 149 require.Equal(t, uint16(types.ErrCodeNoError), txEventPayload.ErrorCode) 150 require.Equal(t, uint16(0), txEventPayload.Index) 151 require.Equal(t, blockEventPayload.Hash, txEventPayload.BlockHash) 152 require.Equal(t, blockEventPayload.Height, txEventPayload.BlockHeight) 153 require.Equal(t, blockEventPayload.TotalGasUsed, txEventPayload.GasConsumed) 154 require.Empty(t, txEventPayload.ContractAddress) 155 156 // append the state 157 snapshot = snapshot.Append(state) 158 159 // query the value 160 code = []byte(fmt.Sprintf( 161 ` 162 import EVM from %s 163 access(all) 164 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 165 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 166 let res = EVM.run(tx: tx, coinbase: coinbase) 167 168 assert(res.status == EVM.Status.successful, message: "unexpected status") 169 assert(res.errorCode == 0, message: "unexpected error code") 170 171 return res 172 } 173 `, 174 sc.EVMContract.Address.HexWithPrefix(), 175 )) 176 177 innerTxBytes = testAccount.PrepareSignAndEncodeTx(t, 178 testContract.DeployedAt.ToCommon(), 179 testContract.MakeCallData(t, "retrieve"), 180 big.NewInt(0), 181 uint64(100_000), 182 big.NewInt(0), 183 ) 184 185 innerTx = cadence.NewArray( 186 ConvertToCadence(innerTxBytes), 187 ).WithType(stdlib.EVMTransactionBytesCadenceType) 188 189 script := fvm.Script(code).WithArguments( 190 json.MustEncode(innerTx), 191 json.MustEncode(coinbase), 192 ) 193 194 _, output, err = vm.Run( 195 ctx, 196 script, 197 snapshot) 198 require.NoError(t, err) 199 require.NoError(t, output.Err) 200 201 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 202 require.NoError(t, err) 203 require.Equal(t, types.StatusSuccessful, res.Status) 204 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 205 require.Nil(t, res.DeployedContractAddress) 206 require.Equal(t, num, new(big.Int).SetBytes(res.ReturnedValue).Int64()) 207 }) 208 }) 209 210 t.Run("testing EVM.run (failed)", func(t *testing.T) { 211 t.Parallel() 212 RunWithNewEnvironment(t, 213 chain, func( 214 ctx fvm.Context, 215 vm fvm.VM, 216 snapshot snapshot.SnapshotTree, 217 testContract *TestContract, 218 testAccount *EOATestAccount, 219 ) { 220 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 221 code := []byte(fmt.Sprintf( 222 ` 223 import EVM from %s 224 225 transaction(tx: [UInt8], coinbaseBytes: [UInt8; 20]){ 226 prepare(account: &Account) { 227 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 228 let res = EVM.run(tx: tx, coinbase: coinbase) 229 230 assert(res.status == EVM.Status.failed, message: "unexpected status") 231 // ExecutionErrCodeExecutionReverted 232 assert(res.errorCode == %d, message: "unexpected error code") 233 } 234 } 235 `, 236 sc.EVMContract.Address.HexWithPrefix(), 237 types.ExecutionErrCodeExecutionReverted, 238 )) 239 240 num := int64(12) 241 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 242 testContract.DeployedAt.ToCommon(), 243 testContract.MakeCallData(t, "storeButRevert", big.NewInt(num)), 244 big.NewInt(0), 245 uint64(100_000), 246 big.NewInt(0), 247 ) 248 249 innerTx := cadence.NewArray( 250 ConvertToCadence(innerTxBytes), 251 ).WithType(stdlib.EVMTransactionBytesCadenceType) 252 253 coinbase := cadence.NewArray( 254 ConvertToCadence(testAccount.Address().Bytes()), 255 ).WithType(stdlib.EVMAddressBytesCadenceType) 256 257 tx := fvm.Transaction( 258 flow.NewTransactionBody(). 259 SetScript(code). 260 AddAuthorizer(sc.FlowServiceAccount.Address). 261 AddArgument(json.MustEncode(innerTx)). 262 AddArgument(json.MustEncode(coinbase)), 263 0) 264 265 state, output, err := vm.Run( 266 ctx, 267 tx, 268 snapshot) 269 require.NoError(t, err) 270 require.NoError(t, output.Err) 271 require.NotEmpty(t, state.WriteSet) 272 273 snapshot = snapshot.Append(state) 274 275 // query the value 276 code = []byte(fmt.Sprintf( 277 ` 278 import EVM from %s 279 access(all) 280 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 281 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 282 return EVM.run(tx: tx, coinbase: coinbase) 283 } 284 `, 285 sc.EVMContract.Address.HexWithPrefix(), 286 )) 287 288 innerTxBytes = testAccount.PrepareSignAndEncodeTx(t, 289 testContract.DeployedAt.ToCommon(), 290 testContract.MakeCallData(t, "retrieve"), 291 big.NewInt(0), 292 uint64(100_000), 293 big.NewInt(0), 294 ) 295 296 innerTx = cadence.NewArray( 297 ConvertToCadence(innerTxBytes), 298 ).WithType(stdlib.EVMTransactionBytesCadenceType) 299 300 script := fvm.Script(code).WithArguments( 301 json.MustEncode(innerTx), 302 json.MustEncode(coinbase), 303 ) 304 305 _, output, err = vm.Run( 306 ctx, 307 script, 308 snapshot) 309 require.NoError(t, err) 310 require.NoError(t, output.Err) 311 312 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 313 require.NoError(t, err) 314 require.Equal(t, types.StatusSuccessful, res.Status) 315 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 316 require.Equal(t, int64(0), new(big.Int).SetBytes(res.ReturnedValue).Int64()) 317 }) 318 }) 319 320 t.Run("testing EVM.run (with event emitted)", func(t *testing.T) { 321 t.Parallel() 322 RunWithNewEnvironment(t, 323 chain, func( 324 ctx fvm.Context, 325 vm fvm.VM, 326 snapshot snapshot.SnapshotTree, 327 testContract *TestContract, 328 testAccount *EOATestAccount, 329 ) { 330 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 331 code := []byte(fmt.Sprintf( 332 ` 333 import EVM from %s 334 335 transaction(tx: [UInt8], coinbaseBytes: [UInt8; 20]){ 336 prepare(account: &Account) { 337 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 338 let res = EVM.run(tx: tx, coinbase: coinbase) 339 340 assert(res.status == EVM.Status.successful, message: "unexpected status") 341 assert(res.errorCode == 0, message: "unexpected error code") 342 } 343 } 344 `, 345 sc.EVMContract.Address.HexWithPrefix(), 346 )) 347 348 num := int64(12) 349 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 350 testContract.DeployedAt.ToCommon(), 351 testContract.MakeCallData(t, "storeWithLog", big.NewInt(num)), 352 big.NewInt(0), 353 uint64(100_000), 354 big.NewInt(0), 355 ) 356 357 innerTx := cadence.NewArray( 358 ConvertToCadence(innerTxBytes), 359 ).WithType(stdlib.EVMTransactionBytesCadenceType) 360 361 coinbase := cadence.NewArray( 362 ConvertToCadence(testAccount.Address().Bytes()), 363 ).WithType(stdlib.EVMAddressBytesCadenceType) 364 365 tx := fvm.Transaction( 366 flow.NewTransactionBody(). 367 SetScript(code). 368 AddAuthorizer(sc.FlowServiceAccount.Address). 369 AddArgument(json.MustEncode(innerTx)). 370 AddArgument(json.MustEncode(coinbase)), 371 0) 372 373 state, output, err := vm.Run( 374 ctx, 375 tx, 376 snapshot) 377 require.NoError(t, err) 378 require.NoError(t, output.Err) 379 require.NotEmpty(t, state.WriteSet) 380 381 txEvent := output.Events[0] 382 ev, err := ccf.Decode(nil, txEvent.Payload) 383 require.NoError(t, err) 384 cadenceEvent, ok := ev.(cadence.Event) 385 require.True(t, ok) 386 387 event, err := types.DecodeTransactionEventPayload(cadenceEvent) 388 require.NoError(t, err) 389 require.NotEmpty(t, event.Hash) 390 391 encodedLogs, err := hex.DecodeString(event.Logs) 392 require.NoError(t, err) 393 394 var logs []*gethTypes.Log 395 err = rlp.DecodeBytes(encodedLogs, &logs) 396 require.NoError(t, err) 397 require.Len(t, logs, 1) 398 log := logs[0] 399 last := log.Topics[len(log.Topics)-1] // last topic is the value set in the store method 400 assert.Equal(t, num, last.Big().Int64()) 401 }) 402 }) 403 } 404 405 func TestEVMBatchRun(t *testing.T) { 406 chain := flow.Emulator.Chain() 407 408 // run a batch of valid transactions which update a value on the contract 409 // after the batch is run check that the value updated on the contract matches 410 // the last transaction update in the batch. 411 t.Run("Batch run multiple valid transactions", func(t *testing.T) { 412 t.Parallel() 413 RunWithNewEnvironment(t, 414 chain, func( 415 ctx fvm.Context, 416 vm fvm.VM, 417 snapshot snapshot.SnapshotTree, 418 testContract *TestContract, 419 testAccount *EOATestAccount, 420 ) { 421 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 422 batchRunCode := []byte(fmt.Sprintf( 423 ` 424 import EVM from %s 425 426 transaction(txs: [[UInt8]], coinbaseBytes: [UInt8; 20]) { 427 prepare(account: &Account) { 428 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 429 let batchResults = EVM.batchRun(txs: txs, coinbase: coinbase) 430 431 assert(batchResults.length == txs.length, message: "invalid result length") 432 for res in batchResults { 433 assert(res.status == EVM.Status.successful, message: "unexpected status") 434 assert(res.errorCode == 0, message: "unexpected error code") 435 } 436 } 437 } 438 `, 439 sc.EVMContract.Address.HexWithPrefix(), 440 )) 441 442 batchCount := 5 443 var storedValues []int64 444 txBytes := make([]cadence.Value, batchCount) 445 for i := 0; i < batchCount; i++ { 446 num := int64(i) 447 storedValues = append(storedValues, num) 448 // prepare batch of transaction payloads 449 tx := testAccount.PrepareSignAndEncodeTx(t, 450 testContract.DeployedAt.ToCommon(), 451 testContract.MakeCallData(t, "storeWithLog", big.NewInt(num)), 452 big.NewInt(0), 453 uint64(100_000), 454 big.NewInt(0), 455 ) 456 457 // build txs argument 458 txBytes[i] = cadence.NewArray( 459 ConvertToCadence(tx), 460 ).WithType(stdlib.EVMTransactionBytesCadenceType) 461 } 462 463 coinbase := cadence.NewArray( 464 ConvertToCadence(testAccount.Address().Bytes()), 465 ).WithType(stdlib.EVMAddressBytesCadenceType) 466 467 txs := cadence.NewArray(txBytes). 468 WithType(cadence.NewVariableSizedArrayType( 469 stdlib.EVMTransactionBytesCadenceType, 470 )) 471 472 tx := fvm.Transaction( 473 flow.NewTransactionBody(). 474 SetScript(batchRunCode). 475 AddAuthorizer(sc.FlowServiceAccount.Address). 476 AddArgument(json.MustEncode(txs)). 477 AddArgument(json.MustEncode(coinbase)), 478 0) 479 480 state, output, err := vm.Run(ctx, tx, snapshot) 481 require.NoError(t, err) 482 require.NoError(t, output.Err) 483 require.NotEmpty(t, state.WriteSet) 484 485 require.Len(t, output.Events, batchCount+1) // +1 block executed 486 for i, event := range output.Events { 487 if i == batchCount { // last one is block executed 488 continue 489 } 490 491 ev, err := ccf.Decode(nil, event.Payload) 492 require.NoError(t, err) 493 cadenceEvent, ok := ev.(cadence.Event) 494 require.True(t, ok) 495 496 event, err := types.DecodeTransactionEventPayload(cadenceEvent) 497 require.NoError(t, err) 498 499 encodedLogs, err := hex.DecodeString(event.Logs) 500 require.NoError(t, err) 501 502 var logs []*gethTypes.Log 503 err = rlp.DecodeBytes(encodedLogs, &logs) 504 require.NoError(t, err) 505 506 require.Len(t, logs, 1) 507 508 log := logs[0] 509 last := log.Topics[len(log.Topics)-1] // last topic is the value set in the store method 510 assert.Equal(t, storedValues[i], last.Big().Int64()) 511 } 512 513 // append the state 514 snapshot = snapshot.Append(state) 515 516 // retrieve the values 517 retrieveCode := []byte(fmt.Sprintf( 518 ` 519 import EVM from %s 520 access(all) 521 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 522 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 523 return EVM.run(tx: tx, coinbase: coinbase) 524 } 525 `, 526 sc.EVMContract.Address.HexWithPrefix(), 527 )) 528 529 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 530 testContract.DeployedAt.ToCommon(), 531 testContract.MakeCallData(t, "retrieve"), 532 big.NewInt(0), 533 uint64(100_000), 534 big.NewInt(0), 535 ) 536 537 innerTx := cadence.NewArray( 538 ConvertToCadence(innerTxBytes), 539 ).WithType(stdlib.EVMTransactionBytesCadenceType) 540 541 script := fvm.Script(retrieveCode).WithArguments( 542 json.MustEncode(innerTx), 543 json.MustEncode(coinbase), 544 ) 545 546 _, output, err = vm.Run( 547 ctx, 548 script, 549 snapshot) 550 require.NoError(t, err) 551 require.NoError(t, output.Err) 552 553 // make sure the retrieved value is the same as the last value 554 // that was stored by transaction batch 555 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 556 require.NoError(t, err) 557 require.Equal(t, types.StatusSuccessful, res.Status) 558 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 559 require.Equal(t, storedValues[len(storedValues)-1], new(big.Int).SetBytes(res.ReturnedValue).Int64()) 560 }) 561 }) 562 563 // run batch with one invalid transaction that has an invalid nonce 564 // this should produce invalid result on that specific transaction 565 // but other transaction should successfuly update the value on the contract 566 t.Run("Batch run with one invalid transaction", func(t *testing.T) { 567 t.Parallel() 568 RunWithNewEnvironment(t, 569 chain, func( 570 ctx fvm.Context, 571 vm fvm.VM, 572 snapshot snapshot.SnapshotTree, 573 testContract *TestContract, 574 testAccount *EOATestAccount, 575 ) { 576 // we make transaction at specific index invalid to fail 577 const failedTxIndex = 3 578 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 579 batchRunCode := []byte(fmt.Sprintf( 580 ` 581 import EVM from %s 582 583 transaction(txs: [[UInt8]], coinbaseBytes: [UInt8; 20]) { 584 prepare(account: &Account) { 585 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 586 let batchResults = EVM.batchRun(txs: txs, coinbase: coinbase) 587 588 assert(batchResults.length == txs.length, message: "invalid result length") 589 for i, res in batchResults { 590 if i != %d { 591 assert(res.status == EVM.Status.successful, message: "unexpected status") 592 assert(res.errorCode == 0, message: "unexpected error code") 593 } else { 594 assert(res.status == EVM.Status.invalid, message: "unexpected status") 595 assert(res.errorCode == 201, message: "unexpected error code") 596 } 597 } 598 } 599 } 600 `, 601 sc.EVMContract.Address.HexWithPrefix(), 602 failedTxIndex, 603 )) 604 605 batchCount := 5 606 var num int64 607 txBytes := make([]cadence.Value, batchCount) 608 for i := 0; i < batchCount; i++ { 609 num = int64(i) 610 611 if i == failedTxIndex { 612 // make one transaction in the batch have an invalid nonce 613 testAccount.SetNonce(testAccount.Nonce() - 1) 614 } 615 // prepare batch of transaction payloads 616 tx := testAccount.PrepareSignAndEncodeTx(t, 617 testContract.DeployedAt.ToCommon(), 618 testContract.MakeCallData(t, "store", big.NewInt(num)), 619 big.NewInt(0), 620 uint64(100_000), 621 big.NewInt(0), 622 ) 623 624 // build txs argument 625 txBytes[i] = cadence.NewArray( 626 ConvertToCadence(tx), 627 ).WithType(stdlib.EVMTransactionBytesCadenceType) 628 } 629 630 coinbase := cadence.NewArray( 631 ConvertToCadence(testAccount.Address().Bytes()), 632 ).WithType(stdlib.EVMAddressBytesCadenceType) 633 634 txs := cadence.NewArray(txBytes). 635 WithType(cadence.NewVariableSizedArrayType( 636 stdlib.EVMTransactionBytesCadenceType, 637 )) 638 639 tx := fvm.Transaction( 640 flow.NewTransactionBody(). 641 SetScript(batchRunCode). 642 AddAuthorizer(sc.FlowServiceAccount.Address). 643 AddArgument(json.MustEncode(txs)). 644 AddArgument(json.MustEncode(coinbase)), 645 0) 646 647 state, output, err := vm.Run(ctx, tx, snapshot) 648 require.NoError(t, err) 649 require.NoError(t, output.Err) 650 require.NotEmpty(t, state.WriteSet) 651 652 // append the state 653 snapshot = snapshot.Append(state) 654 655 // retrieve the values 656 retrieveCode := []byte(fmt.Sprintf( 657 ` 658 import EVM from %s 659 access(all) 660 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 661 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 662 return EVM.run(tx: tx, coinbase: coinbase) 663 } 664 `, 665 sc.EVMContract.Address.HexWithPrefix(), 666 )) 667 668 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 669 testContract.DeployedAt.ToCommon(), 670 testContract.MakeCallData(t, "retrieve"), 671 big.NewInt(0), 672 uint64(100_000), 673 big.NewInt(0), 674 ) 675 676 innerTx := cadence.NewArray( 677 ConvertToCadence(innerTxBytes), 678 ).WithType(stdlib.EVMTransactionBytesCadenceType) 679 680 script := fvm.Script(retrieveCode).WithArguments( 681 json.MustEncode(innerTx), 682 json.MustEncode(coinbase), 683 ) 684 685 _, output, err = vm.Run( 686 ctx, 687 script, 688 snapshot) 689 require.NoError(t, err) 690 require.NoError(t, output.Err) 691 692 // make sure the retrieved value is the same as the last value 693 // that was stored by transaction batch 694 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 695 require.NoError(t, err) 696 require.Equal(t, types.StatusSuccessful, res.Status) 697 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 698 require.Equal(t, num, new(big.Int).SetBytes(res.ReturnedValue).Int64()) 699 }) 700 }) 701 702 // fail every other transaction with gas set too low for execution to succeed 703 // but high enough to pass intristic gas check, then check the updated values on the 704 // contract to match the last successful transaction execution 705 t.Run("Batch run with with failed transactions", func(t *testing.T) { 706 t.Parallel() 707 RunWithNewEnvironment(t, 708 chain, func( 709 ctx fvm.Context, 710 vm fvm.VM, 711 snapshot snapshot.SnapshotTree, 712 testContract *TestContract, 713 testAccount *EOATestAccount, 714 ) { 715 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 716 batchRunCode := []byte(fmt.Sprintf( 717 ` 718 import EVM from %s 719 720 transaction(txs: [[UInt8]], coinbaseBytes: [UInt8; 20]) { 721 execute { 722 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 723 let batchResults = EVM.batchRun(txs: txs, coinbase: coinbase) 724 725 log("results") 726 log(batchResults) 727 assert(batchResults.length == txs.length, message: "invalid result length") 728 729 for i, res in batchResults { 730 if i %% 2 != 0 { 731 assert(res.status == EVM.Status.successful, message: "unexpected success status") 732 assert(res.errorCode == 0, message: "unexpected error code") 733 } else { 734 assert(res.status == EVM.Status.failed, message: "unexpected failed status") 735 assert(res.errorCode == 301, message: "unexpected error code") 736 } 737 } 738 } 739 } 740 `, 741 sc.EVMContract.Address.HexWithPrefix(), 742 )) 743 744 batchCount := 6 745 var num int64 746 txBytes := make([]cadence.Value, batchCount) 747 for i := 0; i < batchCount; i++ { 748 gas := uint64(100_000) 749 if i%2 == 0 { 750 // fail with too low gas limit 751 gas = 22_000 752 } else { 753 // update number with only valid transactions 754 num = int64(i) 755 } 756 757 // prepare batch of transaction payloads 758 tx := testAccount.PrepareSignAndEncodeTx(t, 759 testContract.DeployedAt.ToCommon(), 760 testContract.MakeCallData(t, "store", big.NewInt(num)), 761 big.NewInt(0), 762 gas, 763 big.NewInt(0), 764 ) 765 766 // build txs argument 767 txBytes[i] = cadence.NewArray( 768 ConvertToCadence(tx), 769 ).WithType(stdlib.EVMTransactionBytesCadenceType) 770 } 771 772 coinbase := cadence.NewArray( 773 ConvertToCadence(testAccount.Address().Bytes()), 774 ).WithType(stdlib.EVMAddressBytesCadenceType) 775 776 txs := cadence.NewArray(txBytes). 777 WithType(cadence.NewVariableSizedArrayType( 778 stdlib.EVMTransactionBytesCadenceType, 779 )) 780 781 tx := fvm.Transaction( 782 flow.NewTransactionBody(). 783 SetScript(batchRunCode). 784 AddArgument(json.MustEncode(txs)). 785 AddArgument(json.MustEncode(coinbase)), 786 0) 787 788 state, output, err := vm.Run(ctx, tx, snapshot) 789 790 require.NoError(t, err) 791 require.NoError(t, output.Err) 792 //require.NotEmpty(t, state.WriteSet) 793 794 // append the state 795 snapshot = snapshot.Append(state) 796 797 // retrieve the values 798 retrieveCode := []byte(fmt.Sprintf( 799 ` 800 import EVM from %s 801 access(all) 802 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 803 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 804 return EVM.run(tx: tx, coinbase: coinbase) 805 } 806 `, 807 sc.EVMContract.Address.HexWithPrefix(), 808 )) 809 810 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 811 testContract.DeployedAt.ToCommon(), 812 testContract.MakeCallData(t, "retrieve"), 813 big.NewInt(0), 814 uint64(100_000), 815 big.NewInt(0), 816 ) 817 818 innerTx := cadence.NewArray( 819 ConvertToCadence(innerTxBytes), 820 ).WithType(stdlib.EVMTransactionBytesCadenceType) 821 822 script := fvm.Script(retrieveCode).WithArguments( 823 json.MustEncode(innerTx), 824 json.MustEncode(coinbase), 825 ) 826 827 _, output, err = vm.Run( 828 ctx, 829 script, 830 snapshot) 831 require.NoError(t, err) 832 require.NoError(t, output.Err) 833 834 // make sure the retrieved value is the same as the last value 835 // that was stored by transaction batch 836 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 837 require.NoError(t, err) 838 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 839 require.Equal(t, types.StatusSuccessful, res.Status) 840 require.Equal(t, num, new(big.Int).SetBytes(res.ReturnedValue).Int64()) 841 }) 842 }) 843 } 844 845 func TestEVMBlockData(t *testing.T) { 846 t.Parallel() 847 chain := flow.Emulator.Chain() 848 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 849 RunWithNewEnvironment(t, 850 chain, func( 851 ctx fvm.Context, 852 vm fvm.VM, 853 snapshot snapshot.SnapshotTree, 854 testContract *TestContract, 855 testAccount *EOATestAccount, 856 ) { 857 858 // query the block timestamp 859 code := []byte(fmt.Sprintf( 860 ` 861 import EVM from %s 862 access(all) 863 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 864 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 865 return EVM.run(tx: tx, coinbase: coinbase) 866 } 867 `, 868 sc.EVMContract.Address.HexWithPrefix(), 869 )) 870 871 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 872 testContract.DeployedAt.ToCommon(), 873 testContract.MakeCallData(t, "blockTime"), 874 big.NewInt(0), 875 uint64(100_000), 876 big.NewInt(0), 877 ) 878 879 coinbase := cadence.NewArray( 880 ConvertToCadence(testAccount.Address().Bytes()), 881 ).WithType(stdlib.EVMAddressBytesCadenceType) 882 883 innerTx := cadence.NewArray( 884 ConvertToCadence(innerTxBytes), 885 ).WithType(stdlib.EVMTransactionBytesCadenceType) 886 887 script := fvm.Script(code).WithArguments( 888 json.MustEncode(innerTx), 889 json.MustEncode(coinbase), 890 ) 891 892 _, output, err := vm.Run( 893 ctx, 894 script, 895 snapshot) 896 require.NoError(t, err) 897 require.NoError(t, output.Err) 898 899 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 900 require.NoError(t, err) 901 require.Equal(t, types.StatusSuccessful, res.Status) 902 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 903 require.Equal(t, ctx.BlockHeader.Timestamp.Unix(), new(big.Int).SetBytes(res.ReturnedValue).Int64()) 904 905 }) 906 } 907 908 func TestEVMAddressDeposit(t *testing.T) { 909 910 t.Parallel() 911 chain := flow.Emulator.Chain() 912 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 913 RunWithNewEnvironment(t, 914 chain, func( 915 ctx fvm.Context, 916 vm fvm.VM, 917 snapshot snapshot.SnapshotTree, 918 testContract *TestContract, 919 testAccount *EOATestAccount, 920 ) { 921 922 code := []byte(fmt.Sprintf( 923 ` 924 import EVM from %s 925 import FlowToken from %s 926 927 transaction(addr: [UInt8; 20]) { 928 prepare(account: auth(BorrowValue) &Account) { 929 let admin = account.storage 930 .borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 931 932 let minter <- admin.createNewMinter(allowedAmount: 1.0) 933 let vault <- minter.mintTokens(amount: 1.0) 934 destroy minter 935 936 let address = EVM.EVMAddress(bytes: addr) 937 address.deposit(from: <-vault) 938 } 939 } 940 `, 941 sc.EVMContract.Address.HexWithPrefix(), 942 sc.FlowToken.Address.HexWithPrefix(), 943 )) 944 945 addr := RandomAddress(t) 946 947 tx := fvm.Transaction( 948 flow.NewTransactionBody(). 949 SetScript(code). 950 AddAuthorizer(sc.FlowServiceAccount.Address). 951 AddArgument(json.MustEncode(cadence.NewArray( 952 ConvertToCadence(addr.Bytes()), 953 ).WithType(stdlib.EVMAddressBytesCadenceType))), 954 0) 955 956 execSnap, output, err := vm.Run( 957 ctx, 958 tx, 959 snapshot) 960 require.NoError(t, err) 961 require.NoError(t, output.Err) 962 963 snapshot = snapshot.Append(execSnap) 964 965 expectedBalance := types.OneFlowBalance 966 bal := getEVMAccountBalance(t, ctx, vm, snapshot, addr) 967 require.Equal(t, expectedBalance, bal) 968 }) 969 } 970 971 func TestCOAAddressDeposit(t *testing.T) { 972 t.Parallel() 973 974 chain := flow.Emulator.Chain() 975 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 976 RunWithNewEnvironment(t, 977 chain, func( 978 ctx fvm.Context, 979 vm fvm.VM, 980 snapshot snapshot.SnapshotTree, 981 testContract *TestContract, 982 testAccount *EOATestAccount, 983 ) { 984 code := []byte(fmt.Sprintf( 985 ` 986 import EVM from %s 987 import FlowToken from %s 988 989 access(all) 990 fun main() { 991 let admin = getAuthAccount<auth(BorrowValue) &Account>(%s) 992 .storage.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 993 let minter <- admin.createNewMinter(allowedAmount: 1.23) 994 let vault <- minter.mintTokens(amount: 1.23) 995 destroy minter 996 997 let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount() 998 cadenceOwnedAccount.deposit(from: <-vault) 999 destroy cadenceOwnedAccount 1000 } 1001 `, 1002 sc.EVMContract.Address.HexWithPrefix(), 1003 sc.FlowToken.Address.HexWithPrefix(), 1004 sc.FlowServiceAccount.Address.HexWithPrefix(), 1005 )) 1006 1007 script := fvm.Script(code) 1008 1009 _, output, err := vm.Run( 1010 ctx, 1011 script, 1012 snapshot) 1013 require.NoError(t, err) 1014 require.NoError(t, output.Err) 1015 1016 }) 1017 } 1018 1019 func TestCadenceOwnedAccountFunctionalities(t *testing.T) { 1020 t.Parallel() 1021 chain := flow.Emulator.Chain() 1022 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1023 1024 t.Run("test coa setup", func(t *testing.T) { 1025 t.Parallel() 1026 1027 RunWithNewEnvironment(t, 1028 chain, func( 1029 ctx fvm.Context, 1030 vm fvm.VM, 1031 snapshot snapshot.SnapshotTree, 1032 testContract *TestContract, 1033 testAccount *EOATestAccount, 1034 ) { 1035 // create a flow account 1036 flowAccount, _, snapshot := createAndFundFlowAccount( 1037 t, 1038 ctx, 1039 vm, 1040 snapshot, 1041 ) 1042 1043 var coaAddress types.Address 1044 1045 initNonce := uint64(1) 1046 // 10 Flow in UFix64 1047 initBalanceInUFix64 := uint64(1_000_000_000) 1048 initBalance := types.NewBalanceFromUFix64(cadence.UFix64(initBalanceInUFix64)) 1049 1050 coaAddress, snapshot = setupCOA( 1051 t, 1052 ctx, 1053 vm, 1054 snapshot, 1055 flowAccount, 1056 initBalanceInUFix64) 1057 1058 bal := getEVMAccountBalance( 1059 t, 1060 ctx, 1061 vm, 1062 snapshot, 1063 coaAddress) 1064 require.Equal(t, initBalance, bal) 1065 1066 nonce := getEVMAccountNonce( 1067 t, 1068 ctx, 1069 vm, 1070 snapshot, 1071 coaAddress) 1072 require.Equal(t, initNonce, nonce) 1073 }) 1074 }) 1075 1076 t.Run("test coa withdraw", func(t *testing.T) { 1077 t.Parallel() 1078 1079 RunWithNewEnvironment(t, 1080 chain, func( 1081 ctx fvm.Context, 1082 vm fvm.VM, 1083 snapshot snapshot.SnapshotTree, 1084 testContract *TestContract, 1085 testAccount *EOATestAccount, 1086 ) { 1087 code := []byte(fmt.Sprintf( 1088 ` 1089 import EVM from %s 1090 import FlowToken from %s 1091 1092 access(all) 1093 fun main(): UFix64 { 1094 let admin = getAuthAccount<auth(BorrowValue) &Account>(%s) 1095 .storage.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 1096 let minter <- admin.createNewMinter(allowedAmount: 2.34) 1097 let vault <- minter.mintTokens(amount: 2.34) 1098 destroy minter 1099 1100 let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount() 1101 cadenceOwnedAccount.deposit(from: <-vault) 1102 1103 let bal = EVM.Balance(attoflow: 0) 1104 bal.setFLOW(flow: 1.23) 1105 let vault2 <- cadenceOwnedAccount.withdraw(balance: bal) 1106 let balance = vault2.balance 1107 destroy cadenceOwnedAccount 1108 destroy vault2 1109 1110 return balance 1111 } 1112 `, 1113 sc.EVMContract.Address.HexWithPrefix(), 1114 sc.FlowToken.Address.HexWithPrefix(), 1115 sc.FlowServiceAccount.Address.HexWithPrefix(), 1116 )) 1117 1118 script := fvm.Script(code) 1119 1120 _, output, err := vm.Run( 1121 ctx, 1122 script, 1123 snapshot) 1124 require.NoError(t, err) 1125 require.NoError(t, output.Err) 1126 }) 1127 }) 1128 1129 t.Run("test coa transfer", func(t *testing.T) { 1130 t.Parallel() 1131 1132 RunWithNewEnvironment(t, 1133 chain, func( 1134 ctx fvm.Context, 1135 vm fvm.VM, 1136 snapshot snapshot.SnapshotTree, 1137 testContract *TestContract, 1138 testAccount *EOATestAccount, 1139 ) { 1140 code := []byte(fmt.Sprintf( 1141 ` 1142 import EVM from %s 1143 import FlowToken from %s 1144 1145 access(all) 1146 fun main(address: [UInt8; 20]): UFix64 { 1147 let admin = getAuthAccount<auth(BorrowValue) &Account>(%s) 1148 .storage.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 1149 1150 let minter <- admin.createNewMinter(allowedAmount: 2.34) 1151 let vault <- minter.mintTokens(amount: 2.34) 1152 destroy minter 1153 1154 let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount() 1155 cadenceOwnedAccount.deposit(from: <-vault) 1156 1157 let bal = EVM.Balance(attoflow: 0) 1158 bal.setFLOW(flow: 1.23) 1159 1160 let recipientEVMAddress = EVM.EVMAddress(bytes: address) 1161 1162 let res = cadenceOwnedAccount.call( 1163 to: recipientEVMAddress, 1164 data: [], 1165 gasLimit: 100_000, 1166 value: bal, 1167 ) 1168 1169 assert(res.status == EVM.Status.successful, message: "transfer call was not successful") 1170 1171 destroy cadenceOwnedAccount 1172 return recipientEVMAddress.balance().inFLOW() 1173 } 1174 `, 1175 sc.EVMContract.Address.HexWithPrefix(), 1176 sc.FlowToken.Address.HexWithPrefix(), 1177 sc.FlowServiceAccount.Address.HexWithPrefix(), 1178 )) 1179 1180 addr := cadence.NewArray( 1181 ConvertToCadence(testutils.RandomAddress(t).Bytes()), 1182 ).WithType(stdlib.EVMAddressBytesCadenceType) 1183 1184 script := fvm.Script(code).WithArguments( 1185 json.MustEncode(addr), 1186 ) 1187 1188 _, output, err := vm.Run( 1189 ctx, 1190 script, 1191 snapshot) 1192 require.NoError(t, err) 1193 require.NoError(t, output.Err) 1194 1195 require.Equal(t, uint64(123000000), uint64(output.Value.(cadence.UFix64))) 1196 }) 1197 }) 1198 1199 t.Run("test coa deposit and withdraw in a single transaction", func(t *testing.T) { 1200 RunWithNewEnvironment(t, 1201 chain, func( 1202 ctx fvm.Context, 1203 vm fvm.VM, 1204 snapshot snapshot.SnapshotTree, 1205 testContract *TestContract, 1206 testAccount *EOATestAccount, 1207 ) { 1208 code := []byte(fmt.Sprintf( 1209 ` 1210 import EVM from %s 1211 import FlowToken from %s 1212 1213 access(all) 1214 fun main(): UFix64 { 1215 let admin = getAuthAccount<auth(Storage) &Account>(%s) 1216 .storage.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 1217 1218 let minter <- admin.createNewMinter(allowedAmount: 2.34) 1219 let vault <- minter.mintTokens(amount: 2.34) 1220 destroy minter 1221 1222 let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount() 1223 cadenceOwnedAccount.deposit(from: <-vault) 1224 1225 let bal = EVM.Balance(attoflow: 0) 1226 bal.setFLOW(flow: 1.23) 1227 let vault2 <- cadenceOwnedAccount.withdraw(balance: bal) 1228 let balance = vault2.balance 1229 destroy cadenceOwnedAccount 1230 destroy vault2 1231 1232 return balance 1233 } 1234 `, 1235 sc.EVMContract.Address.HexWithPrefix(), 1236 sc.FlowToken.Address.HexWithPrefix(), 1237 sc.FlowServiceAccount.Address.HexWithPrefix(), 1238 )) 1239 1240 script := fvm.Script(code) 1241 1242 _, output, err := vm.Run( 1243 ctx, 1244 script, 1245 snapshot) 1246 require.NoError(t, err) 1247 require.NoError(t, output.Err) 1248 }) 1249 }) 1250 1251 t.Run("test coa deploy", func(t *testing.T) { 1252 RunWithNewEnvironment(t, 1253 chain, func( 1254 ctx fvm.Context, 1255 vm fvm.VM, 1256 snapshot snapshot.SnapshotTree, 1257 testContract *TestContract, 1258 testAccount *EOATestAccount, 1259 ) { 1260 code := []byte(fmt.Sprintf( 1261 ` 1262 import EVM from %s 1263 import FlowToken from %s 1264 1265 access(all) 1266 fun main(code: [UInt8]): EVM.Result { 1267 let admin = getAuthAccount<auth(Storage) &Account>(%s) 1268 .storage.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 1269 let minter <- admin.createNewMinter(allowedAmount: 2.34) 1270 let vault <- minter.mintTokens(amount: 2.34) 1271 destroy minter 1272 1273 let cadenceOwnedAccount <- EVM.createCadenceOwnedAccount() 1274 cadenceOwnedAccount.deposit(from: <-vault) 1275 1276 let res = cadenceOwnedAccount.deploy( 1277 code: code, 1278 gasLimit: 1000000, 1279 value: EVM.Balance(attoflow: 1230000000000000000) 1280 ) 1281 destroy cadenceOwnedAccount 1282 return res 1283 } 1284 `, 1285 sc.EVMContract.Address.HexWithPrefix(), 1286 sc.FlowToken.Address.HexWithPrefix(), 1287 sc.FlowServiceAccount.Address.HexWithPrefix(), 1288 )) 1289 1290 script := fvm.Script(code). 1291 WithArguments(json.MustEncode( 1292 cadence.NewArray( 1293 ConvertToCadence(testContract.ByteCode), 1294 ).WithType(cadence.NewVariableSizedArrayType(cadence.UInt8Type)), 1295 )) 1296 1297 _, output, err := vm.Run( 1298 ctx, 1299 script, 1300 snapshot) 1301 require.NoError(t, err) 1302 require.NoError(t, output.Err) 1303 1304 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 1305 require.NoError(t, err) 1306 require.Equal(t, types.StatusSuccessful, res.Status) 1307 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 1308 require.NotNil(t, res.DeployedContractAddress) 1309 // we strip away first few bytes because they contain deploy code 1310 require.Equal(t, testContract.ByteCode[17:], []byte(res.ReturnedValue)) 1311 }) 1312 }) 1313 } 1314 1315 func TestDryRun(t *testing.T) { 1316 t.Parallel() 1317 chain := flow.Emulator.Chain() 1318 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1319 evmAddress := sc.EVMContract.Address.HexWithPrefix() 1320 1321 dryRunTx := func( 1322 t *testing.T, 1323 tx *gethTypes.Transaction, 1324 ctx fvm.Context, 1325 vm fvm.VM, 1326 snapshot snapshot.SnapshotTree, 1327 testContract *TestContract, 1328 ) *types.ResultSummary { 1329 code := []byte(fmt.Sprintf(` 1330 import EVM from %s 1331 1332 access(all) 1333 fun main(tx: [UInt8]): EVM.Result { 1334 return EVM.dryRun( 1335 tx: tx, 1336 from: EVM.EVMAddress(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) 1337 ) 1338 }`, 1339 evmAddress, 1340 )) 1341 1342 innerTxBytes, err := tx.MarshalBinary() 1343 require.NoError(t, err) 1344 1345 script := fvm.Script(code).WithArguments( 1346 json.MustEncode( 1347 cadence.NewArray( 1348 ConvertToCadence(innerTxBytes), 1349 ).WithType(stdlib.EVMTransactionBytesCadenceType), 1350 ), 1351 ) 1352 _, output, err := vm.Run( 1353 ctx, 1354 script, 1355 snapshot) 1356 require.NoError(t, err) 1357 require.NoError(t, output.Err) 1358 1359 result, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 1360 require.NoError(t, err) 1361 return result 1362 } 1363 1364 // this test checks that gas limit is correctly used and gas usage correctly reported 1365 t.Run("test dry run storing a value with different gas limits", func(t *testing.T) { 1366 RunWithNewEnvironment(t, 1367 chain, func( 1368 ctx fvm.Context, 1369 vm fvm.VM, 1370 snapshot snapshot.SnapshotTree, 1371 testContract *TestContract, 1372 testAccount *EOATestAccount, 1373 ) { 1374 data := testContract.MakeCallData(t, "store", big.NewInt(1337)) 1375 1376 limit := uint64(math.MaxUint64 - 1) 1377 tx := gethTypes.NewTransaction( 1378 0, 1379 testContract.DeployedAt.ToCommon(), 1380 big.NewInt(0), 1381 limit, 1382 big.NewInt(0), 1383 data, 1384 ) 1385 result := dryRunTx(t, tx, ctx, vm, snapshot, testContract) 1386 require.Equal(t, types.ErrCodeNoError, result.ErrorCode) 1387 require.Equal(t, types.StatusSuccessful, result.Status) 1388 require.Greater(t, result.GasConsumed, uint64(0)) 1389 require.Less(t, result.GasConsumed, limit) 1390 1391 // gas limit too low, but still bigger than intrinsic gas value 1392 limit = uint64(21216) 1393 tx = gethTypes.NewTransaction( 1394 0, 1395 testContract.DeployedAt.ToCommon(), 1396 big.NewInt(0), 1397 limit, 1398 big.NewInt(0), 1399 data, 1400 ) 1401 result = dryRunTx(t, tx, ctx, vm, snapshot, testContract) 1402 require.Equal(t, types.ExecutionErrCodeOutOfGas, result.ErrorCode) 1403 require.Equal(t, types.StatusFailed, result.Status) 1404 require.Equal(t, result.GasConsumed, limit) // burn it all!!! 1405 }) 1406 }) 1407 1408 // this test makes sure the dry-run that updates the value on the contract 1409 // doesn't persist the change, and after when the value is read it isn't updated. 1410 t.Run("test dry run for any side-effects", func(t *testing.T) { 1411 RunWithNewEnvironment(t, 1412 chain, func( 1413 ctx fvm.Context, 1414 vm fvm.VM, 1415 snapshot snapshot.SnapshotTree, 1416 testContract *TestContract, 1417 testAccount *EOATestAccount, 1418 ) { 1419 updatedValue := int64(1337) 1420 data := testContract.MakeCallData(t, "store", big.NewInt(updatedValue)) 1421 tx := gethTypes.NewTransaction( 1422 0, 1423 testContract.DeployedAt.ToCommon(), 1424 big.NewInt(0), 1425 uint64(1000000), 1426 big.NewInt(0), 1427 data, 1428 ) 1429 1430 result := dryRunTx(t, tx, ctx, vm, snapshot, testContract) 1431 require.Equal(t, types.ErrCodeNoError, result.ErrorCode) 1432 require.Equal(t, types.StatusSuccessful, result.Status) 1433 require.Greater(t, result.GasConsumed, uint64(0)) 1434 1435 // query the value make sure it's not updated 1436 code := []byte(fmt.Sprintf( 1437 ` 1438 import EVM from %s 1439 access(all) 1440 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): EVM.Result { 1441 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 1442 return EVM.run(tx: tx, coinbase: coinbase) 1443 } 1444 `, 1445 evmAddress, 1446 )) 1447 1448 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 1449 testContract.DeployedAt.ToCommon(), 1450 testContract.MakeCallData(t, "retrieve"), 1451 big.NewInt(0), 1452 uint64(100_000), 1453 big.NewInt(0), 1454 ) 1455 1456 innerTx := cadence.NewArray( 1457 ConvertToCadence(innerTxBytes), 1458 ).WithType(stdlib.EVMTransactionBytesCadenceType) 1459 1460 coinbase := cadence.NewArray( 1461 ConvertToCadence(testAccount.Address().Bytes()), 1462 ).WithType(stdlib.EVMAddressBytesCadenceType) 1463 1464 script := fvm.Script(code).WithArguments( 1465 json.MustEncode(innerTx), 1466 json.MustEncode(coinbase), 1467 ) 1468 1469 _, output, err := vm.Run( 1470 ctx, 1471 script, 1472 snapshot) 1473 require.NoError(t, err) 1474 require.NoError(t, output.Err) 1475 1476 res, err := stdlib.ResultSummaryFromEVMResultValue(output.Value) 1477 require.NoError(t, err) 1478 require.Equal(t, types.StatusSuccessful, res.Status) 1479 require.Equal(t, types.ErrCodeNoError, res.ErrorCode) 1480 // make sure the value we used in the dry-run is not the same as the value stored in contract 1481 require.NotEqual(t, updatedValue, new(big.Int).SetBytes(res.ReturnedValue).Int64()) 1482 }) 1483 }) 1484 1485 t.Run("test dry run contract deployment", func(t *testing.T) { 1486 RunWithNewEnvironment(t, 1487 chain, func( 1488 ctx fvm.Context, 1489 vm fvm.VM, 1490 snapshot snapshot.SnapshotTree, 1491 testContract *TestContract, 1492 testAccount *EOATestAccount, 1493 ) { 1494 tx := gethTypes.NewContractCreation( 1495 0, 1496 big.NewInt(0), 1497 uint64(1000000), 1498 big.NewInt(0), 1499 testContract.ByteCode, 1500 ) 1501 1502 result := dryRunTx(t, tx, ctx, vm, snapshot, testContract) 1503 require.Equal(t, types.ErrCodeNoError, result.ErrorCode) 1504 require.Equal(t, types.StatusSuccessful, result.Status) 1505 require.Greater(t, result.GasConsumed, uint64(0)) 1506 require.NotNil(t, result.ReturnedValue) 1507 require.NotNil(t, result.DeployedContractAddress) 1508 require.NotEmpty(t, result.DeployedContractAddress.String()) 1509 }) 1510 }) 1511 1512 t.Run("test dry run validation error", func(t *testing.T) { 1513 RunWithNewEnvironment(t, 1514 chain, func( 1515 ctx fvm.Context, 1516 vm fvm.VM, 1517 snapshot snapshot.SnapshotTree, 1518 testContract *TestContract, 1519 testAccount *EOATestAccount, 1520 ) { 1521 tx := gethTypes.NewContractCreation( 1522 0, 1523 big.NewInt(100), // more than available 1524 uint64(1000000), 1525 big.NewInt(0), 1526 nil, 1527 ) 1528 1529 result := dryRunTx(t, tx, ctx, vm, snapshot, testContract) 1530 assert.Equal(t, types.ValidationErrCodeInsufficientFunds, result.ErrorCode) 1531 assert.Equal(t, types.StatusInvalid, result.Status) 1532 assert.Equal(t, types.InvalidTransactionGasCost, int(result.GasConsumed)) 1533 }) 1534 }) 1535 } 1536 1537 func TestCadenceArch(t *testing.T) { 1538 t.Parallel() 1539 1540 t.Run("testing calling Cadence arch - flow block height (happy case)", func(t *testing.T) { 1541 chain := flow.Emulator.Chain() 1542 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1543 RunWithNewEnvironment(t, 1544 chain, func( 1545 ctx fvm.Context, 1546 vm fvm.VM, 1547 snapshot snapshot.SnapshotTree, 1548 testContract *TestContract, 1549 testAccount *EOATestAccount, 1550 ) { 1551 code := []byte(fmt.Sprintf( 1552 ` 1553 import EVM from %s 1554 1555 access(all) 1556 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]) { 1557 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 1558 let res = EVM.run(tx: tx, coinbase: coinbase) 1559 assert(res.status == EVM.Status.successful, message: "test failed: ".concat(res.errorCode.toString())) 1560 } 1561 `, 1562 sc.EVMContract.Address.HexWithPrefix(), 1563 )) 1564 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 1565 testContract.DeployedAt.ToCommon(), 1566 testContract.MakeCallData(t, "verifyArchCallToFlowBlockHeight", ctx.BlockHeader.Height), 1567 big.NewInt(0), 1568 uint64(10_000_000), 1569 big.NewInt(0), 1570 ) 1571 script := fvm.Script(code).WithArguments( 1572 json.MustEncode( 1573 cadence.NewArray( 1574 ConvertToCadence(innerTxBytes), 1575 ).WithType(stdlib.EVMTransactionBytesCadenceType), 1576 ), 1577 json.MustEncode( 1578 cadence.NewArray( 1579 ConvertToCadence(testAccount.Address().Bytes()), 1580 ).WithType(stdlib.EVMAddressBytesCadenceType), 1581 ), 1582 ) 1583 _, output, err := vm.Run( 1584 ctx, 1585 script, 1586 snapshot) 1587 require.NoError(t, err) 1588 require.NoError(t, output.Err) 1589 }) 1590 }) 1591 1592 t.Run("testing calling Cadence arch - revertible random", func(t *testing.T) { 1593 chain := flow.Emulator.Chain() 1594 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1595 RunWithNewEnvironment(t, 1596 chain, func( 1597 ctx fvm.Context, 1598 vm fvm.VM, 1599 snapshot snapshot.SnapshotTree, 1600 testContract *TestContract, 1601 testAccount *EOATestAccount, 1602 ) { 1603 code := []byte(fmt.Sprintf( 1604 ` 1605 import EVM from %s 1606 1607 access(all) 1608 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): [UInt8] { 1609 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 1610 let res = EVM.run(tx: tx, coinbase: coinbase) 1611 assert(res.status == EVM.Status.successful, message: "test failed: ".concat(res.errorCode.toString())) 1612 return res.data 1613 } 1614 `, 1615 sc.EVMContract.Address.HexWithPrefix(), 1616 )) 1617 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 1618 testContract.DeployedAt.ToCommon(), 1619 testContract.MakeCallData(t, "verifyArchCallToRevertibleRandom"), 1620 big.NewInt(0), 1621 uint64(10_000_000), 1622 big.NewInt(0), 1623 ) 1624 script := fvm.Script(code).WithArguments( 1625 json.MustEncode( 1626 cadence.NewArray( 1627 ConvertToCadence(innerTxBytes), 1628 ).WithType(stdlib.EVMTransactionBytesCadenceType), 1629 ), 1630 json.MustEncode( 1631 cadence.NewArray( 1632 ConvertToCadence(testAccount.Address().Bytes()), 1633 ).WithType(stdlib.EVMAddressBytesCadenceType), 1634 ), 1635 ) 1636 _, output, err := vm.Run( 1637 ctx, 1638 script, 1639 snapshot) 1640 require.NoError(t, err) 1641 require.NoError(t, output.Err) 1642 1643 res := make([]byte, 8) 1644 vals := output.Value.(cadence.Array).Values 1645 vals = vals[len(vals)-8:] // only last 8 bytes is the value 1646 for i := range res { 1647 res[i] = byte(vals[i].(cadence.UInt8)) 1648 } 1649 1650 actualRand := binary.BigEndian.Uint64(res) 1651 // because PRG uses script ID and random source we can not predict the random 1652 // we can set the random source but since script ID is generated by hashing 1653 // script and args, and since arg is a signed transaction which always changes 1654 // we can't fix the value 1655 require.Greater(t, actualRand, uint64(0)) 1656 }) 1657 }) 1658 1659 t.Run("testing calling Cadence arch - random source (happy case)", func(t *testing.T) { 1660 chain := flow.Emulator.Chain() 1661 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1662 RunWithNewEnvironment(t, 1663 chain, func( 1664 ctx fvm.Context, 1665 vm fvm.VM, 1666 snapshot snapshot.SnapshotTree, 1667 testContract *TestContract, 1668 testAccount *EOATestAccount, 1669 ) { 1670 entropy := []byte{13, 37} 1671 source := []byte{91, 161, 206, 171, 100, 17, 141, 44} // coresponding out to the above entropy 1672 1673 // we must record a new heartbeat with a fixed block, we manually execute a transaction to do so, 1674 // since doing this automatically would require a block computer and whole execution setup 1675 height := uint64(1) 1676 block1 := unittest.BlockFixture() 1677 block1.Header.Height = height 1678 ctx.BlockHeader = block1.Header 1679 ctx.EntropyProvider = testutil.EntropyProviderFixture(entropy) // fix the entropy 1680 1681 txBody := flow.NewTransactionBody(). 1682 SetScript([]byte(fmt.Sprintf(` 1683 import RandomBeaconHistory from %s 1684 1685 transaction { 1686 prepare(serviceAccount: auth(Capabilities, Storage) &Account) { 1687 let randomBeaconHistoryHeartbeat = serviceAccount.storage.borrow<&RandomBeaconHistory.Heartbeat>( 1688 from: RandomBeaconHistory.HeartbeatStoragePath) 1689 ?? panic("Couldn't borrow RandomBeaconHistory.Heartbeat Resource") 1690 randomBeaconHistoryHeartbeat.heartbeat(randomSourceHistory: randomSourceHistory()) 1691 } 1692 }`, sc.RandomBeaconHistory.Address.HexWithPrefix())), 1693 ). 1694 AddAuthorizer(sc.FlowServiceAccount.Address) 1695 1696 s, out, err := vm.Run(ctx, fvm.Transaction(txBody, 0), snapshot) 1697 require.NoError(t, err) 1698 require.NoError(t, out.Err) 1699 1700 snapshot = snapshot.Append(s) 1701 1702 code := []byte(fmt.Sprintf( 1703 ` 1704 import EVM from %s 1705 1706 access(all) 1707 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]): [UInt8] { 1708 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 1709 let res = EVM.run(tx: tx, coinbase: coinbase) 1710 assert(res.status == EVM.Status.successful, message: "evm tx wrong status") 1711 return res.data 1712 } 1713 `, 1714 sc.EVMContract.Address.HexWithPrefix(), 1715 )) 1716 1717 // we fake progressing to new block height since random beacon does the check the 1718 // current height (2) is bigger than the height requested (1) 1719 block1.Header.Height = 2 1720 ctx.BlockHeader = block1.Header 1721 1722 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 1723 testContract.DeployedAt.ToCommon(), 1724 testContract.MakeCallData(t, "verifyArchCallToRandomSource", height), 1725 big.NewInt(0), 1726 uint64(10_000_000), 1727 big.NewInt(0), 1728 ) 1729 script := fvm.Script(code).WithArguments( 1730 json.MustEncode( 1731 cadence.NewArray( 1732 ConvertToCadence(innerTxBytes), 1733 ).WithType(stdlib.EVMTransactionBytesCadenceType), 1734 ), 1735 json.MustEncode( 1736 cadence.NewArray( 1737 ConvertToCadence(testAccount.Address().Bytes()), 1738 ).WithType(stdlib.EVMAddressBytesCadenceType), 1739 ), 1740 ) 1741 _, output, err := vm.Run( 1742 ctx, 1743 script, 1744 snapshot) 1745 require.NoError(t, err) 1746 require.NoError(t, output.Err) 1747 1748 res := make([]byte, 8) 1749 vals := output.Value.(cadence.Array).Values 1750 vals = vals[len(vals)-8:] // only last 8 bytes is the value 1751 for i := range res { 1752 res[i] = byte(vals[i].(cadence.UInt8)) 1753 } 1754 require.Equal(t, source, res) 1755 }) 1756 }) 1757 1758 t.Run("testing calling Cadence arch - random source (failed due to incorrect height)", func(t *testing.T) { 1759 chain := flow.Emulator.Chain() 1760 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1761 RunWithNewEnvironment(t, 1762 chain, func( 1763 ctx fvm.Context, 1764 vm fvm.VM, 1765 snapshot snapshot.SnapshotTree, 1766 testContract *TestContract, 1767 testAccount *EOATestAccount, 1768 ) { 1769 // we must record a new heartbeat with a fixed block, we manually execute a transaction to do so, 1770 // since doing this automatically would require a block computer and whole execution setup 1771 height := uint64(1) 1772 block1 := unittest.BlockFixture() 1773 block1.Header.Height = height 1774 ctx.BlockHeader = block1.Header 1775 1776 txBody := flow.NewTransactionBody(). 1777 SetScript([]byte(fmt.Sprintf(` 1778 import RandomBeaconHistory from %s 1779 1780 transaction { 1781 prepare(serviceAccount: auth(Capabilities, Storage) &Account) { 1782 let randomBeaconHistoryHeartbeat = serviceAccount.storage.borrow<&RandomBeaconHistory.Heartbeat>( 1783 from: RandomBeaconHistory.HeartbeatStoragePath) 1784 ?? panic("Couldn't borrow RandomBeaconHistory.Heartbeat Resource") 1785 randomBeaconHistoryHeartbeat.heartbeat(randomSourceHistory: randomSourceHistory()) 1786 } 1787 }`, sc.RandomBeaconHistory.Address.HexWithPrefix())), 1788 ). 1789 AddAuthorizer(sc.FlowServiceAccount.Address) 1790 1791 s, out, err := vm.Run(ctx, fvm.Transaction(txBody, 0), snapshot) 1792 require.NoError(t, err) 1793 require.NoError(t, out.Err) 1794 1795 snapshot = snapshot.Append(s) 1796 1797 height = 1337 // invalid 1798 // we make sure the transaction fails, due to requested height being invalid 1799 code := []byte(fmt.Sprintf( 1800 ` 1801 import EVM from %s 1802 1803 access(all) 1804 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]) { 1805 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 1806 let res = EVM.run(tx: tx, coinbase: coinbase) 1807 } 1808 `, 1809 sc.EVMContract.Address.HexWithPrefix(), 1810 )) 1811 1812 // we fake progressing to new block height since random beacon does the check the 1813 // current height (2) is bigger than the height requested (1) 1814 block1.Header.Height = 2 1815 ctx.BlockHeader = block1.Header 1816 1817 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 1818 testContract.DeployedAt.ToCommon(), 1819 testContract.MakeCallData(t, "verifyArchCallToRandomSource", height), 1820 big.NewInt(0), 1821 uint64(10_000_000), 1822 big.NewInt(0), 1823 ) 1824 script := fvm.Script(code).WithArguments( 1825 json.MustEncode( 1826 cadence.NewArray( 1827 ConvertToCadence(innerTxBytes), 1828 ).WithType(stdlib.EVMTransactionBytesCadenceType), 1829 ), 1830 json.MustEncode( 1831 cadence.NewArray( 1832 ConvertToCadence(testAccount.Address().Bytes()), 1833 ).WithType(stdlib.EVMAddressBytesCadenceType), 1834 ), 1835 ) 1836 _, output, err := vm.Run( 1837 ctx, 1838 script, 1839 snapshot) 1840 require.NoError(t, err) 1841 // make sure the error is correct 1842 require.ErrorContains(t, output.Err, "Source of randomness not yet recorded") 1843 }) 1844 }) 1845 1846 t.Run("testing calling Cadence arch - COA ownership proof (happy case)", func(t *testing.T) { 1847 chain := flow.Emulator.Chain() 1848 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1849 RunWithNewEnvironment(t, 1850 chain, func( 1851 ctx fvm.Context, 1852 vm fvm.VM, 1853 snapshot snapshot.SnapshotTree, 1854 testContract *TestContract, 1855 testAccount *EOATestAccount, 1856 ) { 1857 // create a flow account 1858 privateKey, err := testutil.GenerateAccountPrivateKey() 1859 require.NoError(t, err) 1860 1861 snapshot, accounts, err := testutil.CreateAccounts( 1862 vm, 1863 snapshot, 1864 []flow.AccountPrivateKey{privateKey}, 1865 chain) 1866 require.NoError(t, err) 1867 flowAccount := accounts[0] 1868 1869 // create/store/link coa 1870 coaAddress, snapshot := setupCOA( 1871 t, 1872 ctx, 1873 vm, 1874 snapshot, 1875 flowAccount, 1876 0, 1877 ) 1878 1879 data := RandomCommonHash(t) 1880 1881 hasher, err := crypto.NewPrefixedHashing(privateKey.HashAlgo, "FLOW-V0.0-user") 1882 require.NoError(t, err) 1883 1884 sig, err := privateKey.PrivateKey.Sign(data.Bytes(), hasher) 1885 require.NoError(t, err) 1886 1887 proof := types.COAOwnershipProof{ 1888 KeyIndices: []uint64{0}, 1889 Address: types.FlowAddress(flowAccount), 1890 CapabilityPath: "coa", 1891 Signatures: []types.Signature{types.Signature(sig)}, 1892 } 1893 1894 encodedProof, err := proof.Encode() 1895 require.NoError(t, err) 1896 1897 // create transaction for proof verification 1898 code := []byte(fmt.Sprintf( 1899 ` 1900 import EVM from %s 1901 1902 access(all) 1903 fun main(tx: [UInt8], coinbaseBytes: [UInt8; 20]) { 1904 let coinbase = EVM.EVMAddress(bytes: coinbaseBytes) 1905 let res = EVM.run(tx: tx, coinbase: coinbase) 1906 assert(res.status == EVM.Status.successful, message: "test failed: ".concat(res.errorCode.toString())) 1907 } 1908 `, 1909 sc.EVMContract.Address.HexWithPrefix(), 1910 )) 1911 innerTxBytes := testAccount.PrepareSignAndEncodeTx(t, 1912 testContract.DeployedAt.ToCommon(), 1913 testContract.MakeCallData(t, "verifyArchCallToVerifyCOAOwnershipProof", 1914 true, 1915 coaAddress.ToCommon(), 1916 data, 1917 encodedProof), 1918 big.NewInt(0), 1919 uint64(10_000_000), 1920 big.NewInt(0), 1921 ) 1922 verifyScript := fvm.Script(code).WithArguments( 1923 json.MustEncode( 1924 cadence.NewArray( 1925 ConvertToCadence(innerTxBytes), 1926 ).WithType( 1927 stdlib.EVMTransactionBytesCadenceType, 1928 )), 1929 json.MustEncode( 1930 cadence.NewArray( 1931 ConvertToCadence( 1932 testAccount.Address().Bytes(), 1933 ), 1934 ).WithType( 1935 stdlib.EVMAddressBytesCadenceType, 1936 ), 1937 ), 1938 ) 1939 // run proof transaction 1940 _, output, err := vm.Run( 1941 ctx, 1942 verifyScript, 1943 snapshot) 1944 require.NoError(t, err) 1945 require.NoError(t, output.Err) 1946 }) 1947 }) 1948 } 1949 1950 func createAndFundFlowAccount( 1951 t *testing.T, 1952 ctx fvm.Context, 1953 vm fvm.VM, 1954 snapshot snapshot.SnapshotTree, 1955 ) (flow.Address, flow.AccountPrivateKey, snapshot.SnapshotTree) { 1956 1957 privateKey, err := testutil.GenerateAccountPrivateKey() 1958 require.NoError(t, err) 1959 1960 snapshot, accounts, err := testutil.CreateAccounts( 1961 vm, 1962 snapshot, 1963 []flow.AccountPrivateKey{privateKey}, 1964 ctx.Chain) 1965 require.NoError(t, err) 1966 flowAccount := accounts[0] 1967 1968 // fund the account with 100 tokens 1969 sc := systemcontracts.SystemContractsForChain(ctx.Chain.ChainID()) 1970 code := []byte(fmt.Sprintf( 1971 ` 1972 import FlowToken from %s 1973 import FungibleToken from %s 1974 1975 transaction { 1976 prepare(account: auth(BorrowValue) &Account) { 1977 let admin = account.storage 1978 .borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin)! 1979 1980 let minter <- admin.createNewMinter(allowedAmount: 100.0) 1981 let vault <- minter.mintTokens(amount: 100.0) 1982 1983 let receiverRef = getAccount(%s).capabilities 1984 .borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1985 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1986 receiverRef.deposit(from: <-vault) 1987 1988 destroy minter 1989 } 1990 } 1991 `, 1992 sc.FlowToken.Address.HexWithPrefix(), 1993 sc.FungibleToken.Address.HexWithPrefix(), 1994 flowAccount.HexWithPrefix(), 1995 )) 1996 1997 tx := fvm.Transaction( 1998 flow.NewTransactionBody(). 1999 SetScript(code). 2000 AddAuthorizer(sc.FlowServiceAccount.Address), 2001 0) 2002 2003 es, output, err := vm.Run(ctx, tx, snapshot) 2004 require.NoError(t, err) 2005 require.NoError(t, output.Err) 2006 snapshot = snapshot.Append(es) 2007 2008 bal := getFlowAccountBalance( 2009 t, 2010 ctx, 2011 vm, 2012 snapshot, 2013 flowAccount) 2014 // 100 flow in ufix64 2015 require.Equal(t, uint64(10_000_000_000), bal) 2016 2017 return flowAccount, privateKey, snapshot 2018 } 2019 2020 func setupCOA( 2021 t *testing.T, 2022 ctx fvm.Context, 2023 vm fvm.VM, 2024 snap snapshot.SnapshotTree, 2025 coaOwner flow.Address, 2026 initialFund uint64, 2027 ) (types.Address, snapshot.SnapshotTree) { 2028 2029 sc := systemcontracts.SystemContractsForChain(ctx.Chain.ChainID()) 2030 // create a COA and store it under flow account 2031 script := []byte(fmt.Sprintf( 2032 ` 2033 import EVM from %s 2034 import FungibleToken from %s 2035 import FlowToken from %s 2036 2037 transaction(amount: UFix64) { 2038 prepare(account: auth(Capabilities, Storage) &Account) { 2039 let cadenceOwnedAccount1 <- EVM.createCadenceOwnedAccount() 2040 2041 let vaultRef = account.storage 2042 .borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) 2043 ?? panic("Could not borrow reference to the owner's Vault!") 2044 2045 if amount > 0.0 { 2046 let vault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault 2047 cadenceOwnedAccount1.deposit(from: <-vault) 2048 } 2049 2050 account.storage.save<@EVM.CadenceOwnedAccount>( 2051 <-cadenceOwnedAccount1, 2052 to: /storage/coa 2053 ) 2054 2055 let cap = account.capabilities.storage 2056 .issue<&EVM.CadenceOwnedAccount>(/storage/coa) 2057 account.capabilities.publish(cap, at: /public/coa) 2058 } 2059 } 2060 `, 2061 sc.EVMContract.Address.HexWithPrefix(), 2062 sc.FungibleToken.Address.HexWithPrefix(), 2063 sc.FlowToken.Address.HexWithPrefix(), 2064 )) 2065 2066 tx := fvm.Transaction( 2067 flow.NewTransactionBody(). 2068 SetScript(script). 2069 AddAuthorizer(coaOwner). 2070 AddArgument(json.MustEncode(cadence.UFix64(initialFund))), 2071 0) 2072 es, output, err := vm.Run(ctx, tx, snap) 2073 require.NoError(t, err) 2074 require.NoError(t, output.Err) 2075 snap = snap.Append(es) 2076 2077 // 3rd event is the cadence owned account created event 2078 coaAddress, err := types.COAAddressFromFlowCOACreatedEvent(sc.EVMContract.Address, output.Events[2]) 2079 require.NoError(t, err) 2080 2081 return coaAddress, snap 2082 } 2083 2084 func getFlowAccountBalance( 2085 t *testing.T, 2086 ctx fvm.Context, 2087 vm fvm.VM, 2088 snap snapshot.SnapshotTree, 2089 address flow.Address, 2090 ) uint64 { 2091 code := []byte(fmt.Sprintf( 2092 ` 2093 access(all) fun main(): UFix64 { 2094 return getAccount(%s).balance 2095 } 2096 `, 2097 address.HexWithPrefix(), 2098 )) 2099 2100 script := fvm.Script(code) 2101 _, output, err := vm.Run( 2102 ctx, 2103 script, 2104 snap) 2105 require.NoError(t, err) 2106 require.NoError(t, output.Err) 2107 val, ok := output.Value.(cadence.UFix64) 2108 require.True(t, ok) 2109 return uint64(val) 2110 } 2111 2112 func getEVMAccountBalance( 2113 t *testing.T, 2114 ctx fvm.Context, 2115 vm fvm.VM, 2116 snap snapshot.SnapshotTree, 2117 address types.Address, 2118 ) types.Balance { 2119 code := []byte(fmt.Sprintf( 2120 ` 2121 import EVM from %s 2122 access(all) 2123 fun main(addr: [UInt8; 20]): UInt { 2124 return EVM.EVMAddress(bytes: addr).balance().inAttoFLOW() 2125 } 2126 `, 2127 systemcontracts.SystemContractsForChain( 2128 ctx.Chain.ChainID(), 2129 ).EVMContract.Address.HexWithPrefix(), 2130 )) 2131 2132 script := fvm.Script(code).WithArguments( 2133 json.MustEncode( 2134 cadence.NewArray( 2135 ConvertToCadence(address.Bytes()), 2136 ).WithType(stdlib.EVMAddressBytesCadenceType), 2137 ), 2138 ) 2139 _, output, err := vm.Run( 2140 ctx, 2141 script, 2142 snap) 2143 require.NoError(t, err) 2144 require.NoError(t, output.Err) 2145 val, ok := output.Value.(cadence.UInt) 2146 require.True(t, ok) 2147 return val.Big() 2148 } 2149 2150 func getEVMAccountNonce( 2151 t *testing.T, 2152 ctx fvm.Context, 2153 vm fvm.VM, 2154 snap snapshot.SnapshotTree, 2155 address types.Address, 2156 ) uint64 { 2157 code := []byte(fmt.Sprintf( 2158 ` 2159 import EVM from %s 2160 access(all) 2161 fun main(addr: [UInt8; 20]): UInt64 { 2162 return EVM.EVMAddress(bytes: addr).nonce() 2163 } 2164 `, 2165 systemcontracts.SystemContractsForChain( 2166 ctx.Chain.ChainID(), 2167 ).EVMContract.Address.HexWithPrefix(), 2168 )) 2169 2170 script := fvm.Script(code).WithArguments( 2171 json.MustEncode( 2172 cadence.NewArray( 2173 ConvertToCadence(address.Bytes()), 2174 ).WithType(stdlib.EVMAddressBytesCadenceType), 2175 ), 2176 ) 2177 _, output, err := vm.Run( 2178 ctx, 2179 script, 2180 snap) 2181 require.NoError(t, err) 2182 require.NoError(t, output.Err) 2183 val, ok := output.Value.(cadence.UInt64) 2184 require.True(t, ok) 2185 return uint64(val) 2186 } 2187 2188 func RunWithNewEnvironment( 2189 t *testing.T, 2190 chain flow.Chain, 2191 f func( 2192 fvm.Context, 2193 fvm.VM, 2194 snapshot.SnapshotTree, 2195 *TestContract, 2196 *EOATestAccount, 2197 ), 2198 ) { 2199 rootAddr, err := evm.StorageAccountAddress(chain.ChainID()) 2200 require.NoError(t, err) 2201 2202 RunWithTestBackend(t, func(backend *TestBackend) { 2203 RunWithDeployedContract(t, GetStorageTestContract(t), backend, rootAddr, func(testContract *TestContract) { 2204 RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) { 2205 2206 blocks := new(envMock.Blocks) 2207 block1 := unittest.BlockFixture() 2208 blocks.On("ByHeightFrom", 2209 block1.Header.Height, 2210 block1.Header, 2211 ).Return(block1.Header, nil) 2212 2213 opts := []fvm.Option{ 2214 fvm.WithChain(chain), 2215 fvm.WithBlockHeader(block1.Header), 2216 fvm.WithAuthorizationChecksEnabled(false), 2217 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 2218 fvm.WithEntropyProvider(testutil.EntropyProviderFixture(nil)), 2219 fvm.WithRandomSourceHistoryCallAllowed(true), 2220 fvm.WithBlocks(blocks), 2221 fvm.WithCadenceLogging(true), 2222 } 2223 ctx := fvm.NewContext(opts...) 2224 2225 vm := fvm.NewVirtualMachine() 2226 snapshotTree := snapshot.NewSnapshotTree(backend) 2227 2228 baseBootstrapOpts := []fvm.BootstrapProcedureOption{ 2229 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 2230 } 2231 2232 executionSnapshot, _, err := vm.Run( 2233 ctx, 2234 fvm.Bootstrap(unittest.ServiceAccountPublicKey, baseBootstrapOpts...), 2235 snapshotTree) 2236 require.NoError(t, err) 2237 2238 snapshotTree = snapshotTree.Append(executionSnapshot) 2239 2240 f( 2241 fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true)), 2242 vm, 2243 snapshotTree, 2244 testContract, 2245 testAccount, 2246 ) 2247 }) 2248 }) 2249 }) 2250 }