diff --git a/action/protocol/execution/evm/context.go b/action/protocol/execution/evm/context.go index a4f2f0d954..2d7208d9fd 100644 --- a/action/protocol/execution/evm/context.go +++ b/action/protocol/execution/evm/context.go @@ -3,12 +3,15 @@ package evm import ( "context" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/iotexproject/iotex-core/v2/action/protocol" "github.com/iotexproject/iotex-core/v2/pkg/log" ) type ( helperContextKey struct{} + loggerContextKey struct{} // HelperContext is the context for EVM helper HelperContext struct { @@ -16,6 +19,10 @@ type ( GetBlockTime GetBlockTime DepositGasFunc protocol.DepositGas } + // LoggerContext is the context for EVM logger + LoggerContext struct { + buildLogger func() vm.EVMLogger + } ) // WithHelperCtx returns a new context with helper context @@ -31,3 +38,14 @@ func mustGetHelperCtx(ctx context.Context) HelperContext { } return hc } + +// WithLoggerCtx returns a new context with logger context +func WithLoggerCtx(ctx context.Context, buildLogger func() vm.EVMLogger) context.Context { + return context.WithValue(ctx, loggerContextKey{}, LoggerContext{buildLogger: buildLogger}) +} + +// GetLoggerCtx returns the logger context from the context +func GetLoggerCtx(ctx context.Context) (LoggerContext, bool) { + tc, ok := ctx.Value(loggerContextKey{}).(LoggerContext) + return tc, ok +} diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index 116e856a96..58326d56c5 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -237,6 +237,14 @@ func ExecuteContract( ctx, span := tracer.NewSpan(ctx, "evm.ExecuteContract") defer span.End() + var evmLogger vm.EVMLogger + if tCtx, ok := GetLoggerCtx(ctx); ok { + evmLogger = tCtx.buildLogger() + ctx = protocol.WithVMConfigCtx(ctx, vm.Config{ + Tracer: evmLogger, + }) + } + stateDB, err := prepareStateDB(ctx, sm) if err != nil { return nil, nil, err @@ -308,6 +316,7 @@ func ExecuteContract( revertMsg := string(data[64 : 64+msgLength]) receipt.SetExecutionRevertMsg(revertMsg) } + receipt.EvmLogger = evmLogger log.S().Debugf("Receipt: %+v, %v", receipt, err) return retval, receipt, nil } diff --git a/action/receipt.go b/action/receipt.go index a2dc3f41c5..f6908a2427 100644 --- a/action/receipt.go +++ b/action/receipt.go @@ -11,6 +11,7 @@ import ( "google.golang.org/protobuf/proto" + "github.com/ethereum/go-ethereum/core/vm" "github.com/iotexproject/go-pkgs/hash" "github.com/iotexproject/iotex-proto/golang/iotextypes" @@ -35,6 +36,8 @@ type ( logs []*Log transactionLogs []*TransactionLog executionRevertMsg string + // EVMLogger is the logger for EVM, it is optional + EvmLogger vm.EVMLogger } // Log stores an evm contract event diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 80d509ca25..1cd3b5d54a 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -13,6 +13,7 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/core/vm" "github.com/facebookgo/clock" "github.com/iotexproject/go-pkgs/hash" "github.com/iotexproject/iotex-address/address" @@ -22,6 +23,7 @@ import ( "github.com/iotexproject/iotex-core/v2/action" "github.com/iotexproject/iotex-core/v2/action/protocol" + "github.com/iotexproject/iotex-core/v2/action/protocol/execution/evm" "github.com/iotexproject/iotex-core/v2/blockchain/block" "github.com/iotexproject/iotex-core/v2/blockchain/blockdao" "github.com/iotexproject/iotex-core/v2/blockchain/filedao" @@ -121,6 +123,7 @@ type ( clk clock.Clock pubSubManager PubSubManager timerFactory *prometheustimer.TimerFactory + buildEvmLogger func() vm.EVMLogger // used by account-based model bbf BlockBuilderFactory @@ -161,6 +164,13 @@ func ClockOption(clk clock.Clock) Option { } } +func EVMLoggerOption(buildLogger func() vm.EVMLogger) Option { + return func(bc *blockchain) error { + bc.buildEvmLogger = buildLogger + return nil + } +} + type ( BlockValidationCfg struct { skipSidecarValidation bool @@ -326,6 +336,9 @@ func (bc *blockchain) ValidateBlock(blk *block.Block, opts ...BlockValidationOpt if err != nil { return err } + if bc.buildEvmLogger != nil { + ctx = evm.WithLoggerCtx(ctx, bc.buildEvmLogger) + } cfg := BlockValidationCfg{} for _, opt := range opts { opt(&cfg) @@ -406,6 +419,9 @@ func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) { if err != nil { return nil, err } + if bc.buildEvmLogger != nil { + ctx = evm.WithLoggerCtx(ctx, bc.buildEvmLogger) + } tip := protocol.MustGetBlockchainCtx(ctx).Tip ctx = bc.contextWithBlock(ctx, bc.config.ProducerAddress(), newblockHeight, timestamp, protocol.CalcBaseFee(genesis.MustExtractGenesisContext(ctx).Blockchain, &tip), protocol.CalcExcessBlobGas(tip.ExcessBlobGas, tip.BlobGasUsed)) ctx = protocol.WithFeatureCtx(ctx) @@ -497,6 +513,9 @@ func (bc *blockchain) commitBlock(blk *block.Block) error { if err != nil { return err } + if bc.buildEvmLogger != nil { + ctx = evm.WithLoggerCtx(ctx, bc.buildEvmLogger) + } ctx = bc.contextWithBlock(ctx, blk.PublicKey().Address(), blk.Height(), blk.Timestamp(), blk.BaseFee(), blk.ExcessBlobGas()) ctx = protocol.WithFeatureCtx(ctx) // write block into DB diff --git a/chainservice/builder.go b/chainservice/builder.go index 69664e37f9..3ecbb389b1 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -11,6 +11,7 @@ import ( "net/url" "time" + "github.com/ethereum/go-ethereum/core/vm" "github.com/iotexproject/iotex-address/address" "github.com/iotexproject/iotex-election/committee" "github.com/iotexproject/iotex-proto/golang/iotextypes" @@ -505,6 +506,9 @@ func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.B } else { chainOpts = append(chainOpts, blockchain.BlockValidatorOption(builder.cs.factory)) } + if evmLogBuilder, ok := builder.cfg.Plugins[config.EVMLoggerPlugin]; ok { + chainOpts = append(chainOpts, blockchain.EVMLoggerOption(evmLogBuilder.(func() vm.EVMLogger))) + } var mintOpts []factory.MintOption if builder.cfg.Consensus.Scheme == config.RollDPoSScheme { diff --git a/chainservice/chainservice.go b/chainservice/chainservice.go index 0a4e234a10..0507738bcf 100644 --- a/chainservice/chainservice.go +++ b/chainservice/chainservice.go @@ -8,6 +8,7 @@ package chainservice import ( "context" + "github.com/ethereum/go-ethereum/core/vm" "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" @@ -79,6 +80,7 @@ type ChainService struct { apiStats *nodestats.APILocalStats blockTimeCalculator *blockutil.BlockTimeCalculator actionsync *actsync.ActionSync + buildEvmLogger func() vm.EVMLogger } // Start starts the server diff --git a/config/config.go b/config/config.go index 7a50fd5095..e2cb798147 100644 --- a/config/config.go +++ b/config/config.go @@ -10,8 +10,11 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/pkg/errors" uconfig "go.uber.org/config" + "go.uber.org/zap" "github.com/iotexproject/iotex-core/v2/actpool" "github.com/iotexproject/iotex-core/v2/actsync" @@ -44,6 +47,8 @@ const ( const ( // GatewayPlugin is the plugin of accepting user API requests and serving blockchain data to users GatewayPlugin = iota + // EVMLoggerPlugin is the plugin of logging EVM execution + EVMLoggerPlugin ) type strs []string @@ -180,6 +185,21 @@ func New(configPaths []string, _plugins []string, validates ...Validate) (Config switch strings.ToLower(plugin) { case "gateway": cfg.Plugins[GatewayPlugin] = nil + case "evmlogger": + var ( + name = "callTracer" + config = &tracers.TraceConfig{ + Tracer: &name, + } + ) + cfg.Plugins[EVMLoggerPlugin] = func() vm.EVMLogger { + t, err := tracers.DefaultDirectory.New(*config.Tracer, new(tracers.Context), config.TracerConfig) + if err != nil { + log.L().Error("failed to create EVM logger", zap.Error(err)) + return nil + } + return t + } default: return Config{}, errors.Errorf("Plugin %s is not supported", plugin) }