Skip to content

Commit

Permalink
Merge pull request #6084 from onflow/ramtin/5500-add-tracing-to-evm
Browse files Browse the repository at this point in the history
[Flow EVM] Add open tracer to EVM handler
  • Loading branch information
ramtinms authored Jun 24, 2024
2 parents 741cb37 + 372d884 commit f3c974a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 8 deletions.
9 changes: 1 addition & 8 deletions fvm/environment/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,17 @@ import (
"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime"
"github.com/rs/zerolog"
otelTrace "go.opentelemetry.io/otel/trace"

reusableRuntime "github.com/onflow/flow-go/fvm/runtime"
"github.com/onflow/flow-go/fvm/tracing"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/trace"
)

// Environment implements the accounts business logic and exposes cadence
// runtime interface methods for the runtime.
type Environment interface {
runtime.Interface

// Tracer
StartChildSpan(
name trace.SpanName,
options ...otelTrace.SpanStartOption,
) tracing.TracerSpan
Tracer

Meter

Expand Down
16 changes: 16 additions & 0 deletions fvm/environment/tracer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package environment

import (
otelTrace "go.opentelemetry.io/otel/trace"

"github.com/onflow/flow-go/fvm/tracing"
"github.com/onflow/flow-go/module/trace"
)

// Tracer captures traces
type Tracer interface {
StartChildSpan(
name trace.SpanName,
options ...otelTrace.SpanStartOption,
) tracing.TracerSpan
}
10 changes: 10 additions & 0 deletions fvm/evm/backends/wrappedEnv.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import (
"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"
otelTrace "go.opentelemetry.io/otel/trace"

"github.com/onflow/flow-go/fvm/environment"
"github.com/onflow/flow-go/fvm/errors"
"github.com/onflow/flow-go/fvm/evm/types"
"github.com/onflow/flow-go/fvm/meter"
"github.com/onflow/flow-go/fvm/tracing"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/trace"
)

// WrappedEnvironment wraps an FVM environment
Expand Down Expand Up @@ -144,6 +147,13 @@ func (we *WrappedEnvironment) GenerateUUID() (uint64, error) {
return uuid, handleEnvironmentError(err)
}

func (we *WrappedEnvironment) StartChildSpan(
name trace.SpanName,
options ...otelTrace.SpanStartOption,
) tracing.TracerSpan {
return we.env.StartChildSpan(name, options...)
}

func handleEnvironmentError(err error) error {
if err == nil {
return nil
Expand Down
20 changes: 20 additions & 0 deletions fvm/evm/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (
"github.com/onflow/cadence/runtime/common"
gethCommon "github.com/onflow/go-ethereum/common"
gethTypes "github.com/onflow/go-ethereum/core/types"
"go.opentelemetry.io/otel/attribute"

"github.com/onflow/flow-go/fvm/environment"
fvmErrors "github.com/onflow/flow-go/fvm/errors"
"github.com/onflow/flow-go/fvm/evm/debug"
"github.com/onflow/flow-go/fvm/evm/handler/coa"
"github.com/onflow/flow-go/fvm/evm/types"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/trace"
)

// ContractHandler is responsible for triggering calls to emulator, metering,
Expand Down Expand Up @@ -65,6 +67,8 @@ func NewContractHandler(

// DeployCOA deploys a cadence-owned-account and returns the address
func (h *ContractHandler) DeployCOA(uuid uint64) types.Address {
defer h.backend.StartChildSpan(trace.FVMEVMDeployCOA).End()

res, err := h.deployCOA(uuid)
panicOnErrorOrInvalidOrFailedState(res, err)
return *res.DeployedContractAddress
Expand Down Expand Up @@ -119,6 +123,8 @@ func (h *ContractHandler) RunOrPanic(rlpEncodedTx []byte, coinbase types.Address
// Run tries to run an rlpencoded evm transaction and
// collects the gas fees and pay it to the coinbase address provided.
func (h *ContractHandler) Run(rlpEncodedTx []byte, coinbase types.Address) *types.ResultSummary {
defer h.backend.StartChildSpan(trace.FVMEVMRun).End()

res, err := h.run(rlpEncodedTx, coinbase)
panicOnError(err)
return res.ResultSummary()
Expand All @@ -129,6 +135,10 @@ func (h *ContractHandler) Run(rlpEncodedTx []byte, coinbase types.Address) *type
// All transactions provided in the batch are included in a single block,
// except for invalid transactions
func (h *ContractHandler) BatchRun(rlpEncodedTxs [][]byte, coinbase types.Address) []*types.ResultSummary {
span := h.backend.StartChildSpan(trace.FVMEVMBatchRun)
span.SetAttributes(attribute.Int("tx_counts", len(rlpEncodedTxs)))
defer span.End()

res, err := h.batchRun(rlpEncodedTxs, coinbase)
panicOnError(err)

Expand Down Expand Up @@ -341,6 +351,8 @@ func (h *ContractHandler) DryRun(
rlpEncodedTx []byte,
from types.Address,
) *types.ResultSummary {
defer h.backend.StartChildSpan(trace.FVMEVMDryRun).End()

res, err := h.dryRun(rlpEncodedTx, from)
panicOnError(err)
return res.ResultSummary()
Expand Down Expand Up @@ -667,6 +679,8 @@ func (a *Account) codeHash() ([]byte, error) {
// Deposit deposits the token from the given vault into the flow evm main vault
// and update the account balance with the new amount
func (a *Account) Deposit(v *types.FLOWTokenVault) {
defer a.fch.backend.StartChildSpan(trace.FVMEVMDeposit).End()

res, err := a.deposit(v)
panicOnErrorOrInvalidOrFailedState(res, err)
}
Expand All @@ -692,6 +706,8 @@ func (a *Account) deposit(v *types.FLOWTokenVault) (*types.Result, error) {
// Withdraw deducts the balance from the account and
// withdraw and return flow token from the Flex main vault.
func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault {
defer a.fch.backend.StartChildSpan(trace.FVMEVMWithdraw).End()

res, err := a.withdraw(b)
panicOnErrorOrInvalidOrFailedState(res, err)

Expand Down Expand Up @@ -745,6 +761,8 @@ func (a *Account) transfer(to types.Address, balance types.Balance) (*types.Resu
// contained in the result summary as data and
// the contract data is not controlled by the caller accounts
func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary {
defer a.fch.backend.StartChildSpan(trace.FVMEVMDeploy).End()

res, err := a.deploy(code, gaslimit, balance)
panicOnError(err)
return res.ResultSummary()
Expand All @@ -771,6 +789,8 @@ func (a *Account) deploy(code types.Code, gaslimit types.GasLimit, balance types
// given it doesn't goes beyond what Flow transaction allows.
// the balance would be deducted from the OFA account and would be transferred to the target address
func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary {
defer a.fch.backend.StartChildSpan(trace.FVMEVMCall).End()

res, err := a.call(to, data, gaslimit, balance)
panicOnError(err)
return res.ResultSummary()
Expand Down
52 changes: 52 additions & 0 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/onflow/flow-go/fvm/evm/types"
"github.com/onflow/flow-go/fvm/systemcontracts"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/trace"
)

// TODO add test for fatal errors
Expand Down Expand Up @@ -1257,6 +1258,57 @@ func TestHandler_TransactionRun(t *testing.T) {
})
})
})

t.Run("test - open tracing", func(t *testing.T) {
t.Parallel()

testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) {
testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
testutils.RunWithEOATestAccount(t, backend, rootAddr, func(eoa *testutils.EOATestAccount) {

tx := gethTypes.NewTransaction(
uint64(1),
gethCommon.Address{1, 2},
big.NewInt(13),
uint64(0),
big.NewInt(1000),
[]byte{},
)

rlpTx, err := tx.MarshalBinary()
require.NoError(t, err)

handler := SetupHandler(t, backend, rootAddr)

backend.ExpectedSpan(t, trace.FVMEVMDryRun)
handler.DryRun(rlpTx, types.EmptyAddress)

backend.ExpectedSpan(t, trace.FVMEVMRun)
handler.Run(rlpTx, types.EmptyAddress)

backend.ExpectedSpan(t, trace.FVMEVMBatchRun)
handler.BatchRun([][]byte{rlpTx}, types.EmptyAddress)

backend.ExpectedSpan(t, trace.FVMEVMDeployCOA)
coa := handler.DeployCOA(1)

acc := handler.AccountByAddress(coa, true)

backend.ExpectedSpan(t, trace.FVMEVMCall)
acc.Call(types.EmptyAddress, nil, 1000, types.EmptyBalance)

backend.ExpectedSpan(t, trace.FVMEVMDeposit)
acc.Deposit(types.NewFlowTokenVault(types.EmptyBalance))

backend.ExpectedSpan(t, trace.FVMEVMWithdraw)
acc.Withdraw(types.EmptyBalance)

backend.ExpectedSpan(t, trace.FVMEVMDeploy)
acc.Deploy(nil, 1, types.EmptyBalance)
})
})
})
})
}

// returns true if error passes the checks
Expand Down
32 changes: 32 additions & 0 deletions fvm/evm/testutils/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/onflow/cadence/runtime/stdlib"
otelTrace "go.opentelemetry.io/otel/trace"

"github.com/onflow/atree"
"github.com/onflow/cadence"
Expand All @@ -19,7 +20,9 @@ import (
"github.com/onflow/flow-go/fvm/environment"
"github.com/onflow/flow-go/fvm/evm/types"
"github.com/onflow/flow-go/fvm/meter"
"github.com/onflow/flow-go/fvm/tracing"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/trace"
)

var TestFlowEVMRootAddress = flow.BytesToAddress([]byte("FlowEVM"))
Expand All @@ -40,6 +43,7 @@ func RunWithTestBackend(t testing.TB, f func(*TestBackend)) {
TestBlockInfo: getSimpleBlockStore(),
TestRandomGenerator: getSimpleRandomGenerator(),
TestContractFunctionInvoker: &TestContractFunctionInvoker{},
TestTracer: &TestTracer{},
}
f(tb)
}
Expand Down Expand Up @@ -185,6 +189,7 @@ type TestBackend struct {
*TestRandomGenerator
*TestContractFunctionInvoker
*testUUIDGenerator
*TestTracer
}

var _ types.Backend = &TestBackend{}
Expand Down Expand Up @@ -497,3 +502,30 @@ func (t *testUUIDGenerator) GenerateUUID() (uint64, error) {
}
return t.generateUUID()
}

type TestTracer struct {
StartChildSpanFunc func(trace.SpanName, ...otelTrace.SpanStartOption) tracing.TracerSpan
}

var _ environment.Tracer = &TestTracer{}

func (tt *TestTracer) StartChildSpan(
name trace.SpanName,
options ...otelTrace.SpanStartOption,
) tracing.TracerSpan {
// if not set we use noop tracer
if tt.StartChildSpanFunc == nil {
return tracing.NewMockTracerSpan()
}
return tt.StartChildSpanFunc(name, options...)
}

func (tt *TestTracer) ExpectedSpan(t *testing.T, expected trace.SpanName) {
tt.StartChildSpanFunc = func(
sn trace.SpanName,
sso ...otelTrace.SpanStartOption,
) tracing.TracerSpan {
require.Equal(t, expected, sn)
return tracing.NewMockTracerSpan()
}
}
1 change: 1 addition & 0 deletions fvm/evm/types/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ type Backend interface {
environment.RandomGenerator
environment.ContractFunctionInvoker
environment.UUIDGenerator
environment.Tracer
}
9 changes: 9 additions & 0 deletions module/trace/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,14 @@ const (
FVMEnvRemoveAccountContractCode SpanName = "fvm.env.removeAccountContractCode"
FVMEnvGetSigningAccounts SpanName = "fvm.env.getSigningAccounts"

FVMEVMDeployCOA SpanName = "fvm.evm.deployCOA"
FVMEVMRun SpanName = "fvm.evm.run"
FVMEVMDryRun SpanName = "fvm.evm.dryRun"
FVMEVMBatchRun SpanName = "fvm.evm.batchRun"
FVMEVMDeposit SpanName = "fvm.evm.deposit"
FVMEVMWithdraw SpanName = "fvm.evm.withdraw"
FVMEVMDeploy SpanName = "fvm.evm.deploy"
FVMEVMCall SpanName = "fvm.evm.call"

FVMCadenceTrace SpanName = "fvm.cadence.trace"
)

0 comments on commit f3c974a

Please sign in to comment.