From c0a1b1ed7366f846b5fc327851efa25f3ae1ee17 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 9 Jul 2024 19:02:12 -0700 Subject: [PATCH 01/22] delay block commit --- .../systemChunkTransactionTemplate.cdc | 6 ++++ fvm/evm/handler/handler.go | 20 ------------ fvm/evm/handler/handler_test.go | 31 +++++++++++-------- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc b/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc index 222f6926ab2..9eaa1d464a1 100644 --- a/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc +++ b/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc @@ -1,6 +1,7 @@ import FlowEpoch from "FlowEpoch" import NodeVersionBeacon from "NodeVersionBeacon" import RandomBeaconHistory from "RandomBeaconHistory" +import EVM from "EVM" transaction { prepare(serviceAccount: auth(BorrowValue) &Account) { @@ -17,5 +18,10 @@ transaction { .borrow<&RandomBeaconHistory.Heartbeat>(from: RandomBeaconHistory.HeartbeatStoragePath) ?? panic("Couldn't borrow RandomBeaconHistory.Heartbeat Resource") randomBeaconHistoryHeartbeat.heartbeat(randomSourceHistory: randomSourceHistory()) + + let evmHeartbeat = serviceAccount.storage + .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + ?? panic("Couldn't borrow EVM.Heartbeat Resource") + evmHeartbeat.heartbeat() } } diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index d41e5f591f7..af2547da9f0 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -240,12 +240,6 @@ func (h *ContractHandler) batchRun(rlpEncodedTxs [][]byte, coinbase types.Addres return nil, err } - // TODO: don't commit right away. - err = h.commitBlockProposal() - if err != nil { - return nil, err - } - return res, nil } @@ -344,14 +338,7 @@ func (h *ContractHandler) run( return nil, err } - // TODO: don't commit right away. - err = h.commitBlockProposal() - if err != nil { - return nil, err - } - h.tracer.Collect(res.TxHash) - return res, nil } @@ -543,14 +530,7 @@ func (h *ContractHandler) executeAndHandleCall( return nil, err } - // TODO: don't commit right away. - err = h.commitBlockProposal() - if err != nil { - return nil, err - } - h.tracer.Collect(res.TxHash) - return res, nil } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 5cdf7d709bf..9ae0a4f56dd 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -36,8 +36,6 @@ import ( "github.com/onflow/flow-go/module/trace" ) -// TODO add test for fatal errors - var flowTokenAddress = common.MustBytesToAddress(systemcontracts.SystemContractsForChain(flow.Emulator).FlowToken.Address.Bytes()) var randomBeaconAddress = systemcontracts.SystemContractsForChain(flow.Emulator).RandomBeaconHistory.Address @@ -97,10 +95,9 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { // require.NoError(t, err) // require.Equal(t, result.GasConsumed, computationUsed) - // check events (1 extra for block event) + // check event events := backend.Events() - - require.Len(t, events, 2) + require.Len(t, events, 1) event := events[0] assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) @@ -123,7 +120,10 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { assert.Equal(t, l, logs[i]) } + handler.CommitBlockProposal() // check block event + events = backend.Events() + require.Len(t, events, 2) event = events[1] assert.Equal(t, event.Type, types.EventTypeBlockExecuted) @@ -312,6 +312,7 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) { bal := types.OneFlowBalance account.Deposit(types.NewFlowTokenVault(bal)) + handler.CommitBlockProposal() // check if block height has been incremented b = handler.LastExecutedBlock() require.Equal(t, uint64(1), b.Height) @@ -361,20 +362,20 @@ func TestHandler_COA(t *testing.T) { require.Equal(t, zeroBalance, foa.Balance()) events := backend.Events() - require.Len(t, events, 6) + require.Len(t, events, 3) // first two transactions are for COA setup // transaction event - event := events[2] + event := events[0] assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) // block event - event = events[3] - assert.Equal(t, event.Type, types.EventTypeBlockExecuted) + event = events[1] + assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) // transaction event - event = events[4] + event = events[2] assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) _, err := jsoncdc.Decode(nil, event.Payload) require.NoError(t, err) @@ -383,7 +384,10 @@ func TestHandler_COA(t *testing.T) { // assert.Equal(t, balance, ret.Amount) // block event - event = events[5] + handler.CommitBlockProposal() + events = backend.Events() + require.Len(t, events, 4) + event = events[3] assert.Equal(t, event.Type, types.EventTypeBlockExecuted) // check gas usage @@ -638,9 +642,9 @@ func TestHandler_COA(t *testing.T) { require.Equal(t, big.NewInt(int64(blockHeight)), new(big.Int).SetBytes(ret.ReturnedData)) events := backend.Events() - require.Len(t, events, 6) + require.Len(t, events, 3) // last transaction executed event - event := events[4] + event := events[2] assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) ev, err := jsoncdc.Decode(nil, event.Payload) require.NoError(t, err) @@ -923,6 +927,7 @@ func TestHandler_TransactionRun(t *testing.T) { require.Equal(t, types.ErrCodeNoError, rs.ErrorCode) } + handler.CommitBlockProposal() events := backend.Events() require.Len(t, events, batchSize+1) // +1 block event From 40e5e7a6526cb39356e607164d8c6d6dec8224cb Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 9 Jul 2024 19:29:04 -0700 Subject: [PATCH 02/22] update tests --- fvm/evm/evm_test.go | 93 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 8414016870d..306a24f94f9 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -105,9 +105,12 @@ func TestEVMRun(t *testing.T) { require.NoError(t, output.Err) require.NotEmpty(t, state.WriteSet) - // assert event fiedls are correct - require.Len(t, output.Events, 2) + // assert event fields are correct + require.Len(t, output.Events, 1) + txEvent := output.Events[0] + // TODO: + // commit block blockEvent := output.Events[1] assert.Equal( @@ -129,8 +132,6 @@ func TestEVMRun(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, blockEventPayload.Hash) - txEvent := output.Events[0] - assert.Equal( t, common.NewAddressLocation( @@ -488,7 +489,10 @@ func TestEVMBatchRun(t *testing.T) { require.NoError(t, output.Err) require.NotEmpty(t, state.WriteSet) - require.Len(t, output.Events, batchCount+1) // +1 block executed + // append the state + snapshot = snapshot.Append(state) + + require.Len(t, output.Events, batchCount) for i, event := range output.Events { if i == batchCount { // last one is block executed continue @@ -516,8 +520,35 @@ func TestEVMBatchRun(t *testing.T) { assert.Equal(t, storedValues[i], last.Big().Int64()) } + // commit block + heartBeatCode := []byte(fmt.Sprintf( + ` + import EVM from %s + transaction { + prepare(serviceAccount: auth(BorrowValue) &Account) { + let evmHeartbeat = serviceAccount.storage + .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + ?? panic("Couldn't borrow EVM.Heartbeat Resource") + evmHeartbeat.heartbeat() + } + } + `, + sc.EVMContract.Address.HexWithPrefix(), + )) + tx = fvm.Transaction( + flow.NewTransactionBody(). + SetScript(heartBeatCode). + AddAuthorizer(sc.FlowServiceAccount.Address), + 0) + + state, output, err = vm.Run(ctx, tx, snapshot) + require.NoError(t, err) + require.NoError(t, output.Err) + require.NotEmpty(t, state.WriteSet) + snapshot = snapshot.Append(state) + // last one is block executed, make sure TotalGasUsed is non-zero - blockEvent := output.Events[batchCount] + blockEvent := output.Events[0] assert.Equal( t, @@ -1001,8 +1032,44 @@ func TestEVMAddressDeposit(t *testing.T) { bal := getEVMAccountBalance(t, ctx, vm, snapshot, addr) require.Equal(t, expectedBalance, bal) + // deposit event + depositEvent := output.Events[3] + depEv, err := types.FlowEventToCadenceEvent(depositEvent) + require.NoError(t, err) + + depEvPayload, err := types.DecodeFLOWTokensDepositedEventPayload(depEv) + require.NoError(t, err) + + require.Equal(t, types.OneFlow, depEvPayload.BalanceAfterInAttoFlow.Value) + + // commit block + heartBeatCode := []byte(fmt.Sprintf( + ` + import EVM from %s + transaction { + prepare(serviceAccount: auth(BorrowValue) &Account) { + let evmHeartbeat = serviceAccount.storage + .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + ?? panic("Couldn't borrow EVM.Heartbeat Resource") + evmHeartbeat.heartbeat() + } + } + `, + sc.EVMContract.Address.HexWithPrefix(), + )) + tx = fvm.Transaction( + flow.NewTransactionBody(). + SetScript(heartBeatCode). + AddAuthorizer(sc.FlowServiceAccount.Address), + 0) + + execSnap, output, err = vm.Run(ctx, tx, snapshot) + require.NoError(t, err) + require.NoError(t, output.Err) + require.NotEmpty(t, execSnap.WriteSet) + // block executed event, make sure TotalGasUsed is non-zero - blockEvent := output.Events[3] + blockEvent := output.Events[0] assert.Equal( t, @@ -1023,16 +1090,6 @@ func TestEVMAddressDeposit(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) - - // deposit event - depositEvent := output.Events[4] - depEv, err := types.FlowEventToCadenceEvent(depositEvent) - require.NoError(t, err) - - depEvPayload, err := types.DecodeFLOWTokensDepositedEventPayload(depEv) - require.NoError(t, err) - - require.Equal(t, types.OneFlow, depEvPayload.BalanceAfterInAttoFlow.Value) }) } @@ -1195,7 +1252,7 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) { require.NoError(t, err) require.NoError(t, output.Err) - withdrawEvent := output.Events[10] + withdrawEvent := output.Events[8] ev, err := types.FlowEventToCadenceEvent(withdrawEvent) require.NoError(t, err) From 3dbc1959df973570c70d3e3d6d41e4c22ce13e4a Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 9 Jul 2024 23:26:13 -0700 Subject: [PATCH 03/22] update benchmark --- fvm/evm/handler/handler_benchmark_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fvm/evm/handler/handler_benchmark_test.go b/fvm/evm/handler/handler_benchmark_test.go index 8c7ff388706..136a431aec7 100644 --- a/fvm/evm/handler/handler_benchmark_test.go +++ b/fvm/evm/handler/handler_benchmark_test.go @@ -10,10 +10,10 @@ import ( "github.com/onflow/flow-go/model/flow" ) -func BenchmarkStorage(b *testing.B) { benchmarkStorageGrowth(b, 100, 100) } +func BenchmarkStorage(b *testing.B) { benchmarkStorageGrowth(b, 100, 100, 100) } // benchmark -func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) { +func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount, txPerBlock int) { testutils.RunWithTestBackend(b, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(b, backend, func(rootAddr flow.Address) { testutils.RunWithDeployedContract(b, @@ -48,12 +48,17 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) { generation, genes, ), - 300_000_000, + 50_000, types.NewBalanceFromUFix64(0), ) - require.Equal(b, 2, len(backend.Events())) + require.Equal(b, 1, len(backend.Events())) backend.DropEvents() // this would make things lighter backend.ResetStats() // reset stats + + if i%txPerBlock == 0 { + handler.CommitBlockProposal() + backend.DropEvents() + } } accounts[0].Call( @@ -65,7 +70,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) { testutils.RandomBigInt(1000), testutils.RandomBigInt(1000), ), - 300_000_000, + 50_000, types.NewBalanceFromUFix64(0), ) From b9ac720c0c3f40f117e3ec62edbd4592673984e0 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 9 Jul 2024 23:55:16 -0700 Subject: [PATCH 04/22] update evm integration tests --- fvm/evm/evm_test.go | 192 ++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 115 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 306a24f94f9..410cfaa5b1e 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -104,32 +104,21 @@ func TestEVMRun(t *testing.T) { require.NoError(t, err) require.NoError(t, output.Err) require.NotEmpty(t, state.WriteSet) + snapshot = snapshot.Append(state) // assert event fields are correct require.Len(t, output.Events, 1) txEvent := output.Events[0] - // TODO: // commit block - blockEvent := output.Events[1] - - assert.Equal( - t, - common.NewAddressLocation( - nil, - common.Address(sc.EVMContract.Address), - string(types.EventTypeBlockExecuted), - ).ID(), - string(blockEvent.Type), - ) - - ev, err := ccf.Decode(nil, blockEvent.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) + var blockEventPayload *types.BlockEventPayload + blockEventPayload, snapshot = callEVMHeartBeat(t, + ctx, + vm, + snapshot) - blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) - require.NoError(t, err) + require.NotEmpty(t, blockEventPayload.Hash) + require.Equal(t, uint64(43785), blockEventPayload.TotalGasUsed) require.NotEmpty(t, blockEventPayload.Hash) assert.Equal( @@ -142,9 +131,9 @@ func TestEVMRun(t *testing.T) { string(txEvent.Type), ) - ev, err = ccf.Decode(nil, txEvent.Payload) + ev, err := ccf.Decode(nil, txEvent.Payload) require.NoError(t, err) - cadenceEvent, ok = ev.(cadence.Event) + cadenceEvent, ok := ev.(cadence.Event) require.True(t, ok) txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent) @@ -521,58 +510,14 @@ func TestEVMBatchRun(t *testing.T) { } // commit block - heartBeatCode := []byte(fmt.Sprintf( - ` - import EVM from %s - transaction { - prepare(serviceAccount: auth(BorrowValue) &Account) { - let evmHeartbeat = serviceAccount.storage - .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) - ?? panic("Couldn't borrow EVM.Heartbeat Resource") - evmHeartbeat.heartbeat() - } - } - `, - sc.EVMContract.Address.HexWithPrefix(), - )) - tx = fvm.Transaction( - flow.NewTransactionBody(). - SetScript(heartBeatCode). - AddAuthorizer(sc.FlowServiceAccount.Address), - 0) - - state, output, err = vm.Run(ctx, tx, snapshot) - require.NoError(t, err) - require.NoError(t, output.Err) - require.NotEmpty(t, state.WriteSet) - snapshot = snapshot.Append(state) - - // last one is block executed, make sure TotalGasUsed is non-zero - blockEvent := output.Events[0] - - assert.Equal( - t, - common.NewAddressLocation( - nil, - common.Address(sc.EVMContract.Address), - string(types.EventTypeBlockExecuted), - ).ID(), - string(blockEvent.Type), - ) - - ev, err := ccf.Decode(nil, blockEvent.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) + blockEventPayload, snapshot := callEVMHeartBeat(t, + ctx, + vm, + snapshot) - blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) - require.NoError(t, err) require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(155513), blockEventPayload.TotalGasUsed) - // append the state - snapshot = snapshot.Append(state) - // retrieve the values retrieveCode := []byte(fmt.Sprintf( ` @@ -1043,51 +988,11 @@ func TestEVMAddressDeposit(t *testing.T) { require.Equal(t, types.OneFlow, depEvPayload.BalanceAfterInAttoFlow.Value) // commit block - heartBeatCode := []byte(fmt.Sprintf( - ` - import EVM from %s - transaction { - prepare(serviceAccount: auth(BorrowValue) &Account) { - let evmHeartbeat = serviceAccount.storage - .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) - ?? panic("Couldn't borrow EVM.Heartbeat Resource") - evmHeartbeat.heartbeat() - } - } - `, - sc.EVMContract.Address.HexWithPrefix(), - )) - tx = fvm.Transaction( - flow.NewTransactionBody(). - SetScript(heartBeatCode). - AddAuthorizer(sc.FlowServiceAccount.Address), - 0) - - execSnap, output, err = vm.Run(ctx, tx, snapshot) - require.NoError(t, err) - require.NoError(t, output.Err) - require.NotEmpty(t, execSnap.WriteSet) - - // block executed event, make sure TotalGasUsed is non-zero - blockEvent := output.Events[0] - - assert.Equal( - t, - common.NewAddressLocation( - nil, - common.Address(sc.EVMContract.Address), - string(types.EventTypeBlockExecuted), - ).ID(), - string(blockEvent.Type), - ) - - ev, err := ccf.Decode(nil, blockEvent.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) + blockEventPayload, _ := callEVMHeartBeat(t, + ctx, + vm, + snapshot) - blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) - require.NoError(t, err) require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) }) @@ -1252,7 +1157,7 @@ func TestCadenceOwnedAccountFunctionalities(t *testing.T) { require.NoError(t, err) require.NoError(t, output.Err) - withdrawEvent := output.Events[8] + withdrawEvent := output.Events[7] ev, err := types.FlowEventToCadenceEvent(withdrawEvent) require.NoError(t, err) @@ -2612,12 +2517,69 @@ func setupCOA( snap = snap.Append(es) // 3rd event is the cadence owned account created event - coaAddress, err := types.COAAddressFromFlowCOACreatedEvent(sc.EVMContract.Address, output.Events[2]) + coaAddress, err := types.COAAddressFromFlowCOACreatedEvent(sc.EVMContract.Address, output.Events[1]) require.NoError(t, err) return coaAddress, snap } +func callEVMHeartBeat( + t *testing.T, + ctx fvm.Context, + vm fvm.VM, + snap snapshot.SnapshotTree, +) (*types.BlockEventPayload, snapshot.SnapshotTree) { + sc := systemcontracts.SystemContractsForChain(ctx.Chain.ChainID()) + + heartBeatCode := []byte(fmt.Sprintf( + ` + import EVM from %s + transaction { + prepare(serviceAccount: auth(BorrowValue) &Account) { + let evmHeartbeat = serviceAccount.storage + .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + ?? panic("Couldn't borrow EVM.Heartbeat Resource") + evmHeartbeat.heartbeat() + } + } + `, + sc.EVMContract.Address.HexWithPrefix(), + )) + tx := fvm.Transaction( + flow.NewTransactionBody(). + SetScript(heartBeatCode). + AddAuthorizer(sc.FlowServiceAccount.Address), + 0) + + state, output, err := vm.Run(ctx, tx, snap) + require.NoError(t, err) + require.NoError(t, output.Err) + require.NotEmpty(t, state.WriteSet) + snap = snap.Append(state) + + // last one is block executed, make sure TotalGasUsed is non-zero + blockEvent := output.Events[0] + + assert.Equal( + t, + common.NewAddressLocation( + nil, + common.Address(sc.EVMContract.Address), + string(types.EventTypeBlockExecuted), + ).ID(), + string(blockEvent.Type), + ) + + ev, err := ccf.Decode(nil, blockEvent.Payload) + require.NoError(t, err) + cadenceEvent, ok := ev.(cadence.Event) + require.True(t, ok) + + blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) + require.NoError(t, err) + return blockEventPayload, snap +} + func getFlowAccountBalance( t *testing.T, ctx fvm.Context, From 14abf81a3e0e00cf29c322b245d4be83bd7dc86b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 10 Jul 2024 00:02:29 -0700 Subject: [PATCH 05/22] update evm tests --- fvm/fvm_test.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index 1ddb1d61709..b2008f0173c 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -3015,10 +3015,17 @@ func TestEVM(t *testing.T) { .borrow(from: /storage/flowTokenVault) ?? panic("Could not borrow reference to the owner's Vault!") + let evmHeartbeat = acc.storage + .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + ?? panic("Couldn't borrow EVM.Heartbeat Resource") + let acc <- EVM.createCadenceOwnedAccount() let amount <- vaultRef.withdraw(amount: 0.0000001) as! @FlowToken.Vault acc.deposit(from: <- amount) destroy acc + + // commit blocks + evmHeartbeat.heartbeat() } }`, sc.FungibleToken.Address.HexWithPrefix(), @@ -3040,9 +3047,9 @@ func TestEVM(t *testing.T) { require.NoError(t, err) require.NoError(t, output.Err) - require.Len(t, output.Events, 7) + require.Len(t, output.Events, 6) - txExe, blockExe := output.Events[4], output.Events[5] + txExe, blockExe := output.Events[3], output.Events[5] txExecutedID := common.NewAddressLocation( nil, common.Address(sc.EVMContract.Address), @@ -3067,12 +3074,11 @@ func TestEVM(t *testing.T) { t, []common.TypeID{ common.TypeID(txExecutedID), - common.TypeID(blockExecutedID), "A.f8d6e0586b0a20c7.EVM.CadenceOwnedAccountCreated", "A.ee82856bf20e2aa6.FungibleToken.Withdrawn", common.TypeID(txExecutedID), - common.TypeID(blockExecutedID), "A.f8d6e0586b0a20c7.EVM.FLOWTokensDeposited", + common.TypeID(blockExecutedID), }, eventTypeIDs, ) From 16b07199ef5d31bcf9c5f7e8a0c16dc0dfa65621 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 10 Jul 2024 00:45:28 -0700 Subject: [PATCH 06/22] add benchmark --- fvm/evm/handler/blockstore_benchmark_test.go | 55 ++++++++++++++++++++ fvm/evm/testutils/misc.go | 10 ++++ 2 files changed, 65 insertions(+) create mode 100644 fvm/evm/handler/blockstore_benchmark_test.go diff --git a/fvm/evm/handler/blockstore_benchmark_test.go b/fvm/evm/handler/blockstore_benchmark_test.go new file mode 100644 index 00000000000..f1b7b03eb7b --- /dev/null +++ b/fvm/evm/handler/blockstore_benchmark_test.go @@ -0,0 +1,55 @@ +package handler_test + +import ( + "testing" + "time" + + "github.com/onflow/flow-go/fvm/evm/handler" + "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/model/flow" + "github.com/stretchr/testify/require" +) + +func BenchmarkProposalGrowth(b *testing.B) { benchmarkBlockProposalGrowth(b, 1000) } + +func benchmarkBlockProposalGrowth(b *testing.B, txCounts int) { + testutils.RunWithTestBackend(b, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(b, backend, func(rootAddr flow.Address) { + + bs := handler.NewBlockStore(backend, rootAddr) + for i := 0; i < txCounts; i++ { + bp, err := bs.BlockProposal() + require.NoError(b, err) + res := testutils.GetRandomResultFixture(b) + bp.AppendTransaction(res) + bs.UpdateBlockProposal(bp) + } + + // check the impact of updating block proposal after x number of transactions + backend.ResetStats() + startTime := time.Now() + bp, err := bs.BlockProposal() + require.NoError(b, err) + res := testutils.GetRandomResultFixture(b) + bp.AppendTransaction(res) + bs.UpdateBlockProposal(bp) + + b.ReportMetric(float64(time.Since(startTime).Nanoseconds()), "proposal_update_time_ns") + b.ReportMetric(float64(backend.TotalBytesRead()), "proposal_update_bytes_read") + b.ReportMetric(float64(backend.TotalBytesWritten()), "proposal_update_bytes_written") + b.ReportMetric(float64(backend.TotalStorageSize()), "proposal_update_total_storage_size") + + // check the impact of block commit after x number of transactions + backend.ResetStats() + startTime = time.Now() + bp, err = bs.BlockProposal() + require.NoError(b, err) + bs.CommitBlockProposal(bp) + + b.ReportMetric(float64(time.Since(startTime).Nanoseconds()), "block_commit_time_ns") + b.ReportMetric(float64(backend.TotalBytesRead()), "block_commit_bytes_read") + b.ReportMetric(float64(backend.TotalBytesWritten()), "block_commit_bytes_written") + b.ReportMetric(float64(backend.TotalStorageSize()), "block_commit_total_storage_size") + }) + }) +} diff --git a/fvm/evm/testutils/misc.go b/fvm/evm/testutils/misc.go index b2f5493cdd7..b7020556fb9 100644 --- a/fvm/evm/testutils/misc.go +++ b/fvm/evm/testutils/misc.go @@ -79,3 +79,13 @@ func COAOwnershipProofInContextFixture(t testing.TB) *types.COAOwnershipProofInC EVMAddress: RandomAddress(t), } } + +func GetRandomResultFixture(t testing.TB) *types.Result { + return &types.Result{ + GasConsumed: RandomGas(10000), + TxHash: RandomCommonHash(t), + Logs: []*gethTypes.Log{ + GetRandomLogFixture(t), + }, + } +} From e2e2470b02b38d4955a8fbcae0e0cded9554e7e6 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 10 Jul 2024 13:11:08 -0700 Subject: [PATCH 07/22] update system contract code to replace EVM address --- fvm/blueprints/system.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/fvm/blueprints/system.go b/fvm/blueprints/system.go index 1dff14a0824..466f8ee47f9 100644 --- a/fvm/blueprints/system.go +++ b/fvm/blueprints/system.go @@ -2,6 +2,7 @@ package blueprints import ( _ "embed" + "strings" "github.com/onflow/flow-core-contracts/lib/go/templates" @@ -17,17 +18,31 @@ const SystemChunkTransactionGasLimit = 100_000_000 //go:embed scripts/systemChunkTransactionTemplate.cdc var systemChunkTransactionTemplate string +// TODO: when the EVM contract is moved to the flow-core-contracts, we can +// just directly use the replace address functionality of the templates package. + +var placeholderEVMAddress = "\"EVM\"" + +func prepareSystemContractCode(chainID flow.ChainID) string { + sc := systemcontracts.SystemContractsForChain(chainID) + code := templates.ReplaceAddresses( + systemChunkTransactionTemplate, + sc.AsTemplateEnv(), + ) + code = strings.ReplaceAll( + code, + placeholderEVMAddress, + sc.EVMContract.Address.HexWithPrefix(), + ) + return code +} + // SystemChunkTransaction creates and returns the transaction corresponding to the // system chunk for the given chain. func SystemChunkTransaction(chain flow.Chain) (*flow.TransactionBody, error) { - contracts := systemcontracts.SystemContractsForChain(chain.ChainID()) - tx := flow.NewTransactionBody(). SetScript( - []byte(templates.ReplaceAddresses( - systemChunkTransactionTemplate, - contracts.AsTemplateEnv(), - )), + []byte(prepareSystemContractCode(chain.ChainID())), ). // The heartbeat resources needed by the system tx have are on the service account, // therefore, the service account is the only authorizer needed. From 01b51c104680a2bf993c1be6d23f7a98c1a33f8a Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 10 Jul 2024 13:13:47 -0700 Subject: [PATCH 08/22] update system transaction hashes --- fvm/blueprints/system_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fvm/blueprints/system_test.go b/fvm/blueprints/system_test.go index 6dab3d9f00e..0f6c87aa77f 100644 --- a/fvm/blueprints/system_test.go +++ b/fvm/blueprints/system_test.go @@ -18,10 +18,10 @@ func TestSystemChunkTransactionHash(t *testing.T) { // this is formatted in a way that the resulting error message is easy to copy-paste into the test. expectedHashes := []chainHash{ - {chainId: "flow-mainnet", expectedHash: "0a7ea89ad32d79a30b91b4c1202230a1e29310e1b92e01c76d036d2e3839159b"}, - {chainId: "flow-testnet", expectedHash: "368434cb7c792c3c35647f30aa90aae5798a45efcf2ff6abb7123b70c1e7850c"}, - {chainId: "flow-previewnet", expectedHash: "e90268cb6e8385d9eb50f2956f47c1c5f77a7b3111de2f66756b2a48855e05ce"}, - {chainId: "flow-emulator", expectedHash: "c6ccd6b805adcfaa6f9719f1dc71c831c40712977f12d82332ba23e2cb499475"}, + {chainId: "flow-mainnet", expectedHash: "5d576e7fe4ea81d0bd86fb9aa2aaa5c66d5e496d1e6bd239e8c4ee18041b0635"}, + {chainId: "flow-testnet", expectedHash: "ec5a556387a6081f5c6a5d8df22a066455dd205f18a9a2ce3d0d8e71502e63fe"}, + {chainId: "flow-previewnet", expectedHash: "e147c73620afb0398a87cfb6e3f3f992ae3793197c50eba0795c1a73570f28ea"}, + {chainId: "flow-emulator", expectedHash: "1305a3553a44b3fb8d5de68ddb7394f14a0f32621a080b828ea8cc2c64b49b33"}, } var actualHashes []chainHash From 6fa1fb81a748396ab9d839686957d855c0fdaf12 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 10 Jul 2024 13:17:38 -0700 Subject: [PATCH 09/22] typo --- fvm/evm/evm_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 410cfaa5b1e..b5313daee27 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -2557,7 +2557,8 @@ func callEVMHeartBeat( require.NotEmpty(t, state.WriteSet) snap = snap.Append(state) - // last one is block executed, make sure TotalGasUsed is non-zero + // validate block event + require.Len(t, output.Events, 1) blockEvent := output.Events[0] assert.Equal( From 065126f83ed98c1277a9afeb08c46a4e74736550 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 10 Jul 2024 13:19:11 -0700 Subject: [PATCH 10/22] remove stale TODO --- fvm/evm/precompiles/arch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fvm/evm/precompiles/arch.go b/fvm/evm/precompiles/arch.go index ae6fe57ce50..5f9bc418347 100644 --- a/fvm/evm/precompiles/arch.go +++ b/fvm/evm/precompiles/arch.go @@ -8,7 +8,7 @@ import ( var ( FlowBlockHeightFuncSig = ComputeFunctionSelector("flowBlockHeight", nil) - // TODO: fix me + ProofVerifierFuncSig = ComputeFunctionSelector( "verifyCOAOwnershipProof", []string{"address", "bytes32", "bytes"}, From 7130421b76e8fba947243a2a1a116ee510e4c9b2 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 11 Jul 2024 11:03:53 -0700 Subject: [PATCH 11/22] improve tests --- fvm/evm/evm_test.go | 42 +++++------------------------ fvm/evm/testutils/event.go | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 fvm/evm/testutils/event.go diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index b5313daee27..c71030609bb 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -111,8 +111,7 @@ func TestEVMRun(t *testing.T) { txEvent := output.Events[0] // commit block - var blockEventPayload *types.BlockEventPayload - blockEventPayload, snapshot = callEVMHeartBeat(t, + blockEventPayload, snapshot := callEVMHeartBeat(t, ctx, vm, snapshot) @@ -131,13 +130,7 @@ func TestEVMRun(t *testing.T) { string(txEvent.Type), ) - ev, err := ccf.Decode(nil, txEvent.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) - - txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent) - require.NoError(t, err) + txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) require.NotEmpty(t, txEventPayload.Hash) require.Equal(t, hex.EncodeToString(innerTxBytes), txEventPayload.Payload) require.Equal(t, uint16(types.ErrCodeNoError), txEventPayload.ErrorCode) @@ -375,16 +368,11 @@ func TestEVMRun(t *testing.T) { require.NotEmpty(t, state.WriteSet) txEvent := output.Events[0] - ev, err := ccf.Decode(nil, txEvent.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) + txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) - event, err := types.DecodeTransactionEventPayload(cadenceEvent) - require.NoError(t, err) - require.NotEmpty(t, event.Hash) + require.NotEmpty(t, txEventPayload.Hash) - encodedLogs, err := hex.DecodeString(event.Logs) + encodedLogs, err := hex.DecodeString(txEventPayload.Logs) require.NoError(t, err) var logs []*gethTypes.Log @@ -2560,25 +2548,7 @@ func callEVMHeartBeat( // validate block event require.Len(t, output.Events, 1) blockEvent := output.Events[0] - - assert.Equal( - t, - common.NewAddressLocation( - nil, - common.Address(sc.EVMContract.Address), - string(types.EventTypeBlockExecuted), - ).ID(), - string(blockEvent.Type), - ) - - ev, err := ccf.Decode(nil, blockEvent.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) - - blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) - require.NoError(t, err) - return blockEventPayload, snap + return BlockEventToPayload(t, blockEvent, sc.EVMContract.Address), snap } func getFlowAccountBalance( diff --git a/fvm/evm/testutils/event.go b/fvm/evm/testutils/event.go new file mode 100644 index 00000000000..29866f8b3a6 --- /dev/null +++ b/fvm/evm/testutils/event.go @@ -0,0 +1,55 @@ +package testutils + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gotest.tools/assert" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/ccf" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/flow-go/fvm/evm/types" + "github.com/onflow/flow-go/model/flow" +) + +func flowToCadenceEvent(t testing.TB, event flow.Event) cadence.Event { + ev, err := ccf.Decode(nil, event.Payload) + require.NoError(t, err) + cadenceEvent, ok := ev.(cadence.Event) + require.True(t, ok) + return cadenceEvent +} + +func TxEventToPayload(t testing.TB, event flow.Event, evmContract flow.Address) *types.TransactionEventPayload { + assert.Equal( + t, + common.NewAddressLocation( + nil, + common.Address(evmContract), + string(types.EventTypeTransactionExecuted), + ).ID(), + string(event.Type), + ) + cadenceEvent := flowToCadenceEvent(t, event) + txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent) + require.NoError(t, err) + return txEventPayload +} + +func BlockEventToPayload(t testing.TB, event flow.Event, evmContract flow.Address) *types.BlockEventPayload { + assert.Equal( + t, + common.NewAddressLocation( + nil, + common.Address(evmContract), + string(types.EventTypeBlockExecuted), + ).ID(), + string(event.Type), + ) + + cadenceEvent := flowToCadenceEvent(t, event) + blockEventPayload, err := types.DecodeBlockEventPayload(cadenceEvent) + require.NoError(t, err) + return blockEventPayload +} From f335f37e5154fbe0002a054034b169a12abb512b Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 11 Jul 2024 11:11:09 -0700 Subject: [PATCH 12/22] improve tests --- fvm/evm/evm_test.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index c71030609bb..fb485f71950 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -8,8 +8,6 @@ import ( "math/big" "testing" - "github.com/onflow/cadence/runtime/common" - "github.com/onflow/cadence/encoding/ccf" gethTypes "github.com/onflow/go-ethereum/core/types" gethParams "github.com/onflow/go-ethereum/params" @@ -119,16 +117,8 @@ func TestEVMRun(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(43785), blockEventPayload.TotalGasUsed) require.NotEmpty(t, blockEventPayload.Hash) - - assert.Equal( - t, - common.NewAddressLocation( - nil, - common.Address(sc.EVMContract.Address), - string(types.EventTypeTransactionExecuted), - ).ID(), - string(txEvent.Type), - ) + require.Len(t, blockEventPayload.TransactionHashes, 1) + require.NotEmpty(t, blockEventPayload.ReceiptRoot) txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) require.NotEmpty(t, txEventPayload.Hash) @@ -505,6 +495,7 @@ func TestEVMBatchRun(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(155513), blockEventPayload.TotalGasUsed) + require.Len(t, blockEventPayload.TransactionHashes, 5) // retrieve the values retrieveCode := []byte(fmt.Sprintf( From 893ff5a53b819b0ef91edc9d0e5cbced2891cbde Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 11 Jul 2024 11:26:10 -0700 Subject: [PATCH 13/22] clean up --- fvm/evm/handler/handler.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index af2547da9f0..445e392eedd 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -213,7 +213,7 @@ func (h *ContractHandler) batchRun(rlpEncodedTxs [][]byte, coinbase types.Addres } // if there were no valid transactions skip emitting events - // and commiting a new block + // and committing a new block if len(bp.TransactionHashes) == 0 { return res, nil } @@ -317,27 +317,24 @@ func (h *ContractHandler) run( return res, nil } - // step 3 - update block proposal + // step 3 - update the block proposal bp, err := h.blockStore.BlockProposal() if err != nil { return nil, err } - - // append tx to the block proposal bp.AppendTransaction(res) - - // step 4 - emit events - err = h.emitEvent(types.NewTransactionEvent(res, rlpEncodedTx, bp.Height)) + err = h.blockStore.UpdateBlockProposal(bp) if err != nil { return nil, err } - // update the block proposal - err = h.blockStore.UpdateBlockProposal(bp) + // step 4 - emit events + err = h.emitEvent(types.NewTransactionEvent(res, rlpEncodedTx, bp.Height)) if err != nil { return nil, err } + // step 5 - collect traces h.tracer.Collect(res.TxHash) return res, nil } @@ -475,7 +472,7 @@ func (h *ContractHandler) executeAndHandleCall( return nil, err } - // saftey check for result + // safety check for result if res == nil { return nil, types.ErrUnexpectedEmptyResult } @@ -511,6 +508,12 @@ func (h *ContractHandler) executeAndHandleCall( } } + // update the block proposal + err = h.blockStore.UpdateBlockProposal(bp) + if err != nil { + return nil, err + } + // emit events encoded, err := call.Encode() if err != nil { @@ -524,12 +527,7 @@ func (h *ContractHandler) executeAndHandleCall( return nil, err } - // update the block proposal - err = h.blockStore.UpdateBlockProposal(bp) - if err != nil { - return nil, err - } - + // collect traces h.tracer.Collect(res.TxHash) return res, nil } From 322940932edf01ebe9ac5c516e2ed2e14f3d2257 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Thu, 11 Jul 2024 20:40:43 -0700 Subject: [PATCH 14/22] update tests with new event utils --- fvm/evm/handler/blockstore_test.go | 6 +- fvm/evm/handler/handler.go | 2 +- fvm/evm/handler/handler_test.go | 113 ++++++++++++----------------- fvm/evm/testutils/backend.go | 13 ++-- 4 files changed, 57 insertions(+), 77 deletions(-) diff --git a/fvm/evm/handler/blockstore_test.go b/fvm/evm/handler/blockstore_test.go index 9e17f7b504b..76116c1c8c4 100644 --- a/fvm/evm/handler/blockstore_test.go +++ b/fvm/evm/handler/blockstore_test.go @@ -185,11 +185,7 @@ func TestBlockStore_AddedTimestamp(t *testing.T) { bp, err := bs.BlockProposal() require.NoError(t, err) - blockBytes, err = bp.ToBytes() - require.NoError(t, err) - - err = backend.SetValue(root[:], []byte(handler.BlockStoreLatestBlockKey), blockBytes) - require.NoError(t, err) + bs.CommitBlockProposal(bp) bb, err := bs.LatestBlock() require.NoError(t, err) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 445e392eedd..fa7e53d8ceb 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -301,7 +301,7 @@ func (h *ContractHandler) run( return nil, err } - // saftey check for result + // safety check for result if res == nil { return nil, types.ErrUnexpectedEmptyResult } diff --git a/fvm/evm/handler/handler_test.go b/fvm/evm/handler/handler_test.go index 9ae0a4f56dd..14062a56888 100644 --- a/fvm/evm/handler/handler_test.go +++ b/fvm/evm/handler/handler_test.go @@ -11,7 +11,6 @@ import ( "time" "github.com/onflow/cadence" - jsoncdc "github.com/onflow/cadence/encoding/json" "github.com/onflow/cadence/runtime/common" gethCommon "github.com/onflow/go-ethereum/common" gethCore "github.com/onflow/go-ethereum/core" @@ -89,54 +88,31 @@ func TestHandler_TransactionRunOrPanic(t *testing.T) { // successfully run (no-panic) handler.RunOrPanic(tx, coinbase) - // check gas usage - // TODO: uncomment and investigate me - // computationUsed, err := backend.ComputationUsed() - // require.NoError(t, err) - // require.Equal(t, result.GasConsumed, computationUsed) - // check event events := backend.Events() require.Len(t, events, 1) + txEventPayload := testutils.TxEventToPayload(t, events[0], rootAddr) - event := events[0] - assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) - ev, err := jsoncdc.Decode(nil, event.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) - - // TODO: add an event decoder in types.event - cadenceLogs := cadence.SearchFieldByName(cadenceEvent, "logs") - - encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", "")) + // check logs + encodedLogs, err := hex.DecodeString(strings.ReplaceAll(txEventPayload.Logs, "\"", "")) require.NoError(t, err) - var logs []*gethTypes.Log err = rlp.DecodeBytes(encodedLogs, &logs) require.NoError(t, err) - for i, l := range result.Logs { assert.Equal(t, l, logs[i]) } + // form block handler.CommitBlockProposal() + // check block event events = backend.Events() require.Len(t, events, 2) - event = events[1] - - assert.Equal(t, event.Type, types.EventTypeBlockExecuted) - ev, err = jsoncdc.Decode(nil, event.Payload) - require.NoError(t, err) - + blockEventPayload := testutils.BlockEventToPayload(t, events[1], rootAddr) // make sure block transaction list references the above transaction id - cadenceEvent, ok = ev.(cadence.Event) - require.True(t, ok) - blockEvent, err := types.DecodeBlockEventPayload(cadenceEvent) - require.NoError(t, err) - - eventTxID := blockEvent.TransactionHashes[0] // only one hash in block + require.Len(t, blockEventPayload.TransactionHashes, 1) + eventTxID := blockEventPayload.TransactionHashes[0] // only one hash in block // make sure the transaction id included in the block transaction list is the same as tx sumbmitted assert.Equal( t, @@ -364,31 +340,50 @@ func TestHandler_COA(t *testing.T) { events := backend.Events() require.Len(t, events, 3) - // first two transactions are for COA setup - - // transaction event - event := events[0] - assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) + // Block level expected values + txHashes := make([]gethCommon.Hash, 0) + totalGasUsed := uint64(0) - // block event - event = events[1] - assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) + // deploy COA transaction event + txEventPayload := testutils.TxEventToPayload(t, events[0], rootAddr) + txContent, err := hex.DecodeString(txEventPayload.Payload) + require.NoError(t, err) + tx, err := types.DirectCallFromEncoded(txContent) + require.NoError(t, err) + txHashes = append(txHashes, tx.Hash()) + totalGasUsed += txEventPayload.GasConsumed - // transaction event - event = events[2] - assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) - _, err := jsoncdc.Decode(nil, event.Payload) + // deposit transaction event + txEventPayload = testutils.TxEventToPayload(t, events[1], rootAddr) + txContent, err = hex.DecodeString(txEventPayload.Payload) + require.NoError(t, err) + tx, err = types.DirectCallFromEncoded(txContent) require.NoError(t, err) - // TODO: decode encoded tx and check for the amount and value - // assert.Equal(t, foa.Address(), ret.Address) - // assert.Equal(t, balance, ret.Amount) + require.Equal(t, foa.Address(), tx.To) + require.Equal(t, types.BalanceToBigInt(balance), tx.Value) + txHashes = append(txHashes, tx.Hash()) + totalGasUsed += txEventPayload.GasConsumed + + // withdraw transaction event + txEventPayload = testutils.TxEventToPayload(t, events[2], rootAddr) + txContent, err = hex.DecodeString(txEventPayload.Payload) + require.NoError(t, err) + tx, err = types.DirectCallFromEncoded(txContent) + require.NoError(t, err) + require.Equal(t, foa.Address(), tx.From) + require.Equal(t, types.BalanceToBigInt(balance), tx.Value) + txHashes = append(txHashes, tx.Hash()) + totalGasUsed += txEventPayload.GasConsumed // block event handler.CommitBlockProposal() events = backend.Events() require.Len(t, events, 4) - event = events[3] - assert.Equal(t, event.Type, types.EventTypeBlockExecuted) + blockEventPayload := testutils.BlockEventToPayload(t, events[3], rootAddr) + for i, txHash := range txHashes { + require.Equal(t, txHash.Hex(), string(blockEventPayload.TransactionHashes[i])) + } + require.Equal(t, totalGasUsed, blockEventPayload.TotalGasUsed) // check gas usage computationUsed, err := backend.ComputationUsed() @@ -645,14 +640,7 @@ func TestHandler_COA(t *testing.T) { require.Len(t, events, 3) // last transaction executed event event := events[2] - assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) - ev, err := jsoncdc.Decode(nil, event.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) - txEventPayload, err := types.DecodeTransactionEventPayload(cadenceEvent) - require.NoError(t, err) - + txEventPayload := testutils.TxEventToPayload(t, event, rootAddr) values := txEventPayload.PrecompiledCalls.Values aggregated := make([]byte, len(values)) for i, v := range values { @@ -935,15 +923,8 @@ func TestHandler_TransactionRun(t *testing.T) { if i == batchSize { continue // don't check last block event } - assert.Equal(t, event.Type, types.EventTypeTransactionExecuted) - ev, err := jsoncdc.Decode(nil, event.Payload) - require.NoError(t, err) - cadenceEvent, ok := ev.(cadence.Event) - require.True(t, ok) - - // TODO: add an event decoder in types.event - cadenceLogs := cadence.SearchFieldByName(cadenceEvent, "logs") - encodedLogs, err := hex.DecodeString(strings.ReplaceAll(cadenceLogs.String(), "\"", "")) + txEventPayload := testutils.TxEventToPayload(t, event, rootAddr) + encodedLogs, err := hex.DecodeString(strings.ReplaceAll(txEventPayload.Logs, "\"", "")) require.NoError(t, err) var logs []*gethTypes.Log diff --git a/fvm/evm/testutils/backend.go b/fvm/evm/testutils/backend.go index 6d5616a488e..498fd2948b0 100644 --- a/fvm/evm/testutils/backend.go +++ b/fvm/evm/testutils/backend.go @@ -11,7 +11,7 @@ import ( "github.com/onflow/atree" "github.com/onflow/cadence" - jsoncdc "github.com/onflow/cadence/encoding/json" + "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/common" "github.com/stretchr/testify/require" @@ -25,7 +25,7 @@ import ( "github.com/onflow/flow-go/module/trace" ) -var TestFlowEVMRootAddress = flow.BytesToAddress([]byte("FlowEVM")) +var TestFlowEVMRootAddress = flow.Address{1, 2, 3, 4} var TestComputationLimit = uint(100_000_000) func RunWithTestFlowEVMRootAddress(t testing.TB, backend atree.Ledger, f func(flow.Address)) { @@ -127,12 +127,15 @@ func getSimpleEventEmitter() *testEventEmitter { events := make(flow.EventsList, 0) return &testEventEmitter{ emitEvent: func(event cadence.Event) error { - payload, err := jsoncdc.Encode(event) + payload, err := ccf.Encode(event) if err != nil { return err } - - events = append(events, flow.Event{Type: flow.EventType(event.EventType.QualifiedIdentifier), Payload: payload}) + eventType := flow.EventType(event.EventType.ID()) + events = append(events, flow.Event{ + Type: eventType, + Payload: payload, + }) return nil }, events: func() flow.EventsList { From 8bde16a63f4f3e2dd93f0ef6bfae79403b70d1aa Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 10:17:13 -0700 Subject: [PATCH 15/22] update test --- fvm/evm/evm_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index 3fdf4d92fbd..e0e0936474b 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -978,6 +978,7 @@ func TestEVMAddressDeposit(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) + require.Len(t, blockEventPayload.TransactionHashes, 1) }) } From 55e42d2f2ae686d9cf0ce642138fe4c15f008fd6 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 10:21:26 -0700 Subject: [PATCH 16/22] check tx hash --- fvm/evm/evm_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fvm/evm/evm_test.go b/fvm/evm/evm_test.go index e0e0936474b..fa702e653ac 100644 --- a/fvm/evm/evm_test.go +++ b/fvm/evm/evm_test.go @@ -960,6 +960,11 @@ func TestEVMAddressDeposit(t *testing.T) { bal := getEVMAccountBalance(t, ctx, vm, snapshot, addr) require.Equal(t, expectedBalance, bal) + // tx executed event + txEvent := output.Events[2] + txEventPayload := testutils.TxEventToPayload(t, txEvent, sc.EVMContract.Address) + require.NoError(t, err) + // deposit event depositEvent := output.Events[3] depEv, err := types.FlowEventToCadenceEvent(depositEvent) @@ -979,6 +984,7 @@ func TestEVMAddressDeposit(t *testing.T) { require.NotEmpty(t, blockEventPayload.Hash) require.Equal(t, uint64(21000), blockEventPayload.TotalGasUsed) require.Len(t, blockEventPayload.TransactionHashes, 1) + require.Equal(t, txEventPayload.Hash, string(blockEventPayload.TransactionHashes[0])) }) } From 635b0c92f353b47ccb1cd031809f8162ce71b715 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 12:55:37 -0700 Subject: [PATCH 17/22] add heartbeat creation functionality --- fvm/evm/stdlib/contract.cdc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 4856ada9537..ecb006a0d55 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -815,16 +815,23 @@ contract EVM { } /// The Heartbeat resource controls the block production - access(all) resource Heartbeat { - + access(all) + resource Heartbeat { /// heartbeat calls commit block proposals and forms new blocks including all the /// recently executed transactions. /// The Flow protocol makes sure to call this function once per block as a system call. - access(all) fun heartbeat() { + access(all) + fun heartbeat() { InternalEVM.commitBlockProposal() } } + /// createHeartBeat creates a heartbeat resource + access(account) + fun createHeartBeat(): @Heartbeat{ + return <-create Heartbeat() + } + init() { self.account.storage.save(<-create Heartbeat(), to: /storage/EVMHeartbeat) } From 180fcd8269ea9df8a496ccb7d3505668a23793f4 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 13:03:12 -0700 Subject: [PATCH 18/22] make heartbeat optional --- fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc | 8 ++++---- fvm/blueprints/system_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc b/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc index 9eaa1d464a1..7c9564e1486 100644 --- a/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc +++ b/fvm/blueprints/scripts/systemChunkTransactionTemplate.cdc @@ -19,9 +19,9 @@ transaction { ?? panic("Couldn't borrow RandomBeaconHistory.Heartbeat Resource") randomBeaconHistoryHeartbeat.heartbeat(randomSourceHistory: randomSourceHistory()) - let evmHeartbeat = serviceAccount.storage - .borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) - ?? panic("Couldn't borrow EVM.Heartbeat Resource") - evmHeartbeat.heartbeat() + let evmHeartbeat = serviceAccount.storage.borrow<&EVM.Heartbeat>(from: /storage/EVMHeartbeat) + if evmHeartbeat != nil { // skip if not available + evmHeartbeat!.heartbeat() + } } } diff --git a/fvm/blueprints/system_test.go b/fvm/blueprints/system_test.go index 0f6c87aa77f..a63ce1c28c9 100644 --- a/fvm/blueprints/system_test.go +++ b/fvm/blueprints/system_test.go @@ -18,10 +18,10 @@ func TestSystemChunkTransactionHash(t *testing.T) { // this is formatted in a way that the resulting error message is easy to copy-paste into the test. expectedHashes := []chainHash{ - {chainId: "flow-mainnet", expectedHash: "5d576e7fe4ea81d0bd86fb9aa2aaa5c66d5e496d1e6bd239e8c4ee18041b0635"}, - {chainId: "flow-testnet", expectedHash: "ec5a556387a6081f5c6a5d8df22a066455dd205f18a9a2ce3d0d8e71502e63fe"}, - {chainId: "flow-previewnet", expectedHash: "e147c73620afb0398a87cfb6e3f3f992ae3793197c50eba0795c1a73570f28ea"}, - {chainId: "flow-emulator", expectedHash: "1305a3553a44b3fb8d5de68ddb7394f14a0f32621a080b828ea8cc2c64b49b33"}, + {chainId: "flow-mainnet", expectedHash: "0e56f890392ad3f2a0dcc7a0e859b6d41f3c17556aa85a0f8831ae25a6537e39"}, + {chainId: "flow-testnet", expectedHash: "69c922177af520e8ecaf0ba97cb174a3ebbd03de6d5b5d1f6b6c043a9638dba3"}, + {chainId: "flow-previewnet", expectedHash: "2ec3b6b7e7d70a651600eb80f775cb57613a0a168c847b70040c469f09066a55"}, + {chainId: "flow-emulator", expectedHash: "7dcc0daaecebc7be33b068ed5c8da0620c89fd896abfc498fc0cc32a261aab1f"}, } var actualHashes []chainHash From 1ef80772d4e679aaaed045b04f5675360e3bf203 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 13:55:29 -0700 Subject: [PATCH 19/22] lint fix --- fvm/evm/testutils/event.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fvm/evm/testutils/event.go b/fvm/evm/testutils/event.go index 29866f8b3a6..004f42ca166 100644 --- a/fvm/evm/testutils/event.go +++ b/fvm/evm/testutils/event.go @@ -3,12 +3,12 @@ package testutils import ( "testing" - "github.com/stretchr/testify/require" - "gotest.tools/assert" - "github.com/onflow/cadence" "github.com/onflow/cadence/encoding/ccf" "github.com/onflow/cadence/runtime/common" + "github.com/stretchr/testify/require" + "gotest.tools/assert" + "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" ) From aa6ddc50c1bc3440e240542144aee2d3141aba24 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 14:21:39 -0700 Subject: [PATCH 20/22] commit change due to evm contract change --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index 9cd60681595..c90afc7673f 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("48460c42a43f700562d6c17408af8b52823774cc24fe795148c8e37b62f77615") + expectedStateCommitmentBytes, _ := hex.DecodeString("a738c835be7196f62900595d186557ba56d5ee4221b0a108782967c5d1d110d7") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index cf331cdfd7d..4be593292ef 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "d31a2d57a74c97aca5df275819b4b42141672808e0bece2fda417e8dc563c818" +const GenesisStateCommitmentHex = "84ff0ef1d33db21e3520f1db78e4a676146c92f8300298320f9a186735074035" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "e222dc960f317d13cb2b10b92760a8f3ee4c89052745d560f3a7cebb4517dc08" + return "48a9f343a11898d75abe8875f31136f1f892f193abf3f4ac6c92a39211844f2c" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "5e178f8a23aa21c27b127104a3964b3e3ea273c4c531d48b572086fa68e07eba" + return "4322de872268ac2188e55d2649bb65736f6bbb143bd010348f87df72afa50380" } From 7c637360ce97bd1cd4ec34263f28914cb1861a2f Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 14:35:16 -0700 Subject: [PATCH 21/22] update system chunk tests --- engine/execution/computation/computer/computer_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/execution/computation/computer/computer_test.go b/engine/execution/computation/computer/computer_test.go index c0ba76bfac0..72a4c54d4c4 100644 --- a/engine/execution/computation/computer/computer_test.go +++ b/engine/execution/computation/computer/computer_test.go @@ -378,6 +378,7 @@ func TestBlockExecutor_ExecuteBlock(t *testing.T) { // include all fees. System chunk should ignore them contextOptions := []fvm.Option{ + fvm.WithEVMEnabled(true), fvm.WithTransactionFeesEnabled(true), fvm.WithAccountStorageLimit(true), fvm.WithBlocks(&environment.NoopBlockFinder{}), @@ -1223,6 +1224,7 @@ func (f *FixedAddressGenerator) AddressCount() uint64 { func Test_ExecutingSystemCollection(t *testing.T) { execCtx := fvm.NewContext( + fvm.WithEVMEnabled(true), fvm.WithChain(flow.Localnet.Chain()), fvm.WithBlocks(&environment.NoopBlockFinder{}), ) @@ -1245,8 +1247,8 @@ func Test_ExecutingSystemCollection(t *testing.T) { noopCollector := metrics.NewNoopCollector() - expectedNumberOfEvents := 3 - expectedEventSize := 1497 + expectedNumberOfEvents := 4 + expectedEventSize := 1961 // bootstrapping does not cache programs expectedCachedPrograms := 0 From 5135a137f387fd847702cdf2699c06ae9c687c29 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 16 Jul 2024 15:23:05 -0700 Subject: [PATCH 22/22] fix test --- engine/execution/computation/programs_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/execution/computation/programs_test.go b/engine/execution/computation/programs_test.go index f918a378ae1..75467f4054d 100644 --- a/engine/execution/computation/programs_test.go +++ b/engine/execution/computation/programs_test.go @@ -207,6 +207,7 @@ func TestPrograms_TestBlockForks(t *testing.T) { chain := flow.Emulator.Chain() vm := fvm.NewVirtualMachine() execCtx := fvm.NewContext( + fvm.WithEVMEnabled(true), fvm.WithBlockHeader(block.Header), fvm.WithBlocks(blockProvider{map[uint64]*flow.Block{0: &block}}), fvm.WithChain(chain))