Commit 8c17425a authored by Inphi's avatar Inphi Committed by GitHub

cannon: Add EVM tracer with sourcemap location (#11583)

parent 2718b756
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"math/big" "math/big"
"os" "os"
"github.com/ethereum-optimism/optimism/op-chain-ops/srcmap"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -57,24 +58,24 @@ func TestContractsSetup(t require.TestingT, version MipsVersion) *ContractMetada ...@@ -57,24 +58,24 @@ func TestContractsSetup(t require.TestingT, version MipsVersion) *ContractMetada
// loadArtifacts loads the Cannon contracts, from the contracts package. // loadArtifacts loads the Cannon contracts, from the contracts package.
func loadArtifacts(version MipsVersion) (*Artifacts, error) { func loadArtifacts(version MipsVersion) (*Artifacts, error) {
var mipsMetadata string artifactFS := foundry.OpenArtifactsDir("../../../packages/contracts-bedrock/forge-artifacts")
var mips *foundry.Artifact
var err error
switch version { switch version {
case MipsSingleThreaded: case MipsSingleThreaded:
mipsMetadata = "../../../packages/contracts-bedrock/forge-artifacts/MIPS.sol/MIPS.json" mips, err = artifactFS.ReadArtifact("MIPS.sol", "MIPS")
case MipsMultithreaded: case MipsMultithreaded:
mipsMetadata = "../../../packages/contracts-bedrock/forge-artifacts/MIPS2.sol/MIPS2.json" mips, err = artifactFS.ReadArtifact("MIPS2.sol", "MIPS2")
default: default:
return nil, fmt.Errorf("Unknown MipsVersion supplied: %v", version) return nil, fmt.Errorf("Unknown MipsVersion supplied: %v", version)
} }
mips, err := foundry.ReadArtifact(mipsMetadata)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load MIPS contract: %w", err) return nil, err
} }
oracle, err := foundry.ReadArtifact("../../../packages/contracts-bedrock/forge-artifacts/PreimageOracle.sol/PreimageOracle.json") oracle, err := artifactFS.ReadArtifact("PreimageOracle.sol", "PreimageOracle")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load Oracle contract: %w", err) return nil, err
} }
return &Artifacts{ return &Artifacts{
...@@ -156,3 +157,25 @@ func (d *testChain) GetHeader(h common.Hash, n uint64) *types.Header { ...@@ -156,3 +157,25 @@ func (d *testChain) GetHeader(h common.Hash, n uint64) *types.Header {
func MarkdownTracer() *tracing.Hooks { func MarkdownTracer() *tracing.Hooks {
return logger.NewMarkdownLogger(&logger.Config{}, os.Stdout).Hooks() return logger.NewMarkdownLogger(&logger.Config{}, os.Stdout).Hooks()
} }
func SourceMapTracer(t require.TestingT, version MipsVersion, mips *foundry.Artifact, oracle *foundry.Artifact, addrs *Addresses) *tracing.Hooks {
srcFS := foundry.NewSourceMapFS(os.DirFS("../../../packages/contracts-bedrock"))
var mipsSrcMap *srcmap.SourceMap
var err error
switch version {
case MipsSingleThreaded:
mipsSrcMap, err = srcFS.SourceMap(mips, "MIPS")
case MipsMultithreaded:
mipsSrcMap, err = srcFS.SourceMap(mips, "MIPS2")
default:
require.Fail(t, "invalid mips version")
}
require.NoError(t, err)
oracleSrcMap, err := srcFS.SourceMap(oracle, "PreimageOracle")
require.NoError(t, err)
return srcmap.NewSourceMapTracer(map[common.Address]*srcmap.SourceMap{
addrs.MIPS: mipsSrcMap,
addrs.Oracle: oracleSrcMap,
}, os.Stdout).Hooks()
}
...@@ -43,6 +43,10 @@ func (m *MIPSEVM) SetLocalOracle(oracle mipsevm.PreimageOracle) { ...@@ -43,6 +43,10 @@ func (m *MIPSEVM) SetLocalOracle(oracle mipsevm.PreimageOracle) {
m.localOracle = oracle m.localOracle = oracle
} }
func (m *MIPSEVM) SetSourceMapTracer(t *testing.T, version MipsVersion) {
m.env.Config.Tracer = SourceMapTracer(t, version, m.artifacts.MIPS, m.artifacts.Oracle, m.addrs)
}
// Step is a pure function that computes the poststate from the VM state encoded in the StepWitness. // Step is a pure function that computes the poststate from the VM state encoded in the StepWitness.
func (m *MIPSEVM) Step(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, stateHashFn mipsevm.HashFn) []byte { func (m *MIPSEVM) Step(t *testing.T, stepWitness *mipsevm.StepWitness, step uint64, stateHashFn mipsevm.HashFn) []byte {
m.lastStep = step m.lastStep = step
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
) )
...@@ -222,40 +223,44 @@ type SourceMapTracer struct { ...@@ -222,40 +223,44 @@ type SourceMapTracer struct {
out io.Writer out io.Writer
} }
func (s *SourceMapTracer) info(codeAddr *common.Address, pc uint64) string { func (s *SourceMapTracer) info(codeAddr common.Address, pc uint64) string {
info := "non-contract" info := "unknown-contract"
if codeAddr != nil { srcMap, ok := s.srcMaps[codeAddr]
srcMap, ok := s.srcMaps[*codeAddr]
if ok { if ok {
info = srcMap.FormattedInfo(pc) info = srcMap.FormattedInfo(pc)
} else {
info = "unknown-contract"
}
} }
return info return info
} }
func (s *SourceMapTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { func (s *SourceMapTracer) OnOpCode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
op := vm.OpCode(opcode)
if op.IsPush() { if op.IsPush() {
start := uint64(op) - uint64(vm.PUSH1) + 1 start := uint64(op) - uint64(vm.PUSH1) + 1
end := pc + 1 + start val := scope.StackData()[:start]
val := scope.Contract.Code[pc+1 : end] fmt.Fprintf(s.out, "%-40s : pc %x opcode %s (%x)\n", s.info(scope.Address(), pc), pc, op.String(), val)
fmt.Fprintf(s.out, "%-40s : pc %x opcode %s (%x)\n", s.info(scope.Contract.CodeAddr, pc), pc, op.String(), val)
return return
} }
fmt.Fprintf(s.out, "%-40s : pc %x opcode %s\n", s.info(scope.Contract.CodeAddr, pc), pc, op.String()) fmt.Fprintf(s.out, "%-40s : pc %x opcode %s\n", s.info(scope.Address(), pc), pc, op.String())
} }
func (s *SourceMapTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { func (s *SourceMapTracer) OnFault(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
fmt.Fprintf(s.out, "%-40s: pc %x opcode %s FAULT %v\n", s.info(scope.Contract.CodeAddr, pc), pc, op.String(), err) op := vm.OpCode(opcode)
fmt.Fprintf(s.out, "%-40s: pc %x opcode %s FAULT %v\n", s.info(scope.Address(), pc), pc, op.String(), err)
fmt.Println("----") fmt.Println("----")
fmt.Fprintf(s.out, "calldata: %x\n", scope.Contract.Input) fmt.Fprintf(s.out, "calldata: %x\n", scope.CallInput())
fmt.Println("----") fmt.Println("----")
fmt.Fprintf(s.out, "memory: %x\n", scope.Memory.Data()) fmt.Fprintf(s.out, "memory: %x\n", scope.MemoryData())
fmt.Println("----") fmt.Println("----")
fmt.Fprintf(s.out, "stack:\n") fmt.Fprintf(s.out, "stack:\n")
stack := scope.Stack.Data() stack := scope.StackData()
for i := range stack { for i := range stack {
fmt.Fprintf(s.out, "%3d: %x\n", -i, stack[len(stack)-1-i].Bytes32()) fmt.Fprintf(s.out, "%3d: %x\n", -i, stack[len(stack)-1-i].Bytes32())
} }
} }
func (s *SourceMapTracer) Hooks() *tracing.Hooks {
return &tracing.Hooks{
OnOpcode: s.OnOpCode,
OnFault: s.OnFault,
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment