Handle more edge cases
This commit is contained in:
parent
c1eaa2f864
commit
2c902db0e1
@ -12,6 +12,7 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/builtin"
|
||||||
"github.com/filecoin-project/go-state-types/exitcode"
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
|
||||||
@ -52,6 +53,13 @@ type Trace struct {
|
|||||||
Subtraces int `json:"subtraces"`
|
Subtraces int `json:"subtraces"`
|
||||||
TraceAddress []int `json:"traceAddress"`
|
TraceAddress []int `json:"traceAddress"`
|
||||||
Type string `json:"Type"`
|
Type string `json:"Type"`
|
||||||
|
|
||||||
|
parent *Trace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Trace) setCallType(callType string) {
|
||||||
|
t.Action.CallType = callType
|
||||||
|
t.Type = callType
|
||||||
}
|
}
|
||||||
|
|
||||||
type TraceBlock struct {
|
type TraceBlock struct {
|
||||||
@ -147,7 +155,7 @@ func (e *EthTrace) TraceBlock(ctx context.Context, blkNum string) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
traces := []*Trace{}
|
traces := []*Trace{}
|
||||||
buildTraces(&traces, []int{}, ir.ExecutionTrace, nil, int64(ts.Height()))
|
buildTraces(&traces, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()))
|
||||||
|
|
||||||
traceBlocks := make([]*TraceBlock, 0, len(trace))
|
traceBlocks := make([]*TraceBlock, 0, len(trace))
|
||||||
for _, trace := range traces {
|
for _, trace := range traces {
|
||||||
@ -204,7 +212,7 @@ func (e *EthTrace) TraceReplayBlockTransactions(ctx context.Context, blkNum stri
|
|||||||
VmTrace: nil,
|
VmTrace: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTraces(&t.Trace, []int{}, ir.ExecutionTrace, nil, int64(ts.Height()))
|
buildTraces(&t.Trace, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()))
|
||||||
|
|
||||||
allTraces = append(allTraces, &t)
|
allTraces = append(allTraces, &t)
|
||||||
}
|
}
|
||||||
@ -296,15 +304,9 @@ func handle_filecoin_method_output(exitCode exitcode.ExitCode, codec uint64, dat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls
|
// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls
|
||||||
func buildTraces(traces *[]*Trace, addr []int, et types.ExecutionTrace, parentEt *types.ExecutionTrace, height int64) {
|
func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.ExecutionTrace, height int64) {
|
||||||
callType := "call"
|
|
||||||
if et.Msg.ReadOnly {
|
|
||||||
callType = "staticcall"
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := &Trace{
|
trace := &Trace{
|
||||||
Action: Action{
|
Action: Action{
|
||||||
CallType: callType,
|
|
||||||
From: et.Msg.From.String(),
|
From: et.Msg.From.String(),
|
||||||
To: et.Msg.To.String(),
|
To: et.Msg.To.String(),
|
||||||
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
|
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
|
||||||
@ -319,70 +321,114 @@ func buildTraces(traces *[]*Trace, addr []int, et types.ExecutionTrace, parentEt
|
|||||||
},
|
},
|
||||||
Subtraces: len(et.Subcalls),
|
Subtraces: len(et.Subcalls),
|
||||||
TraceAddress: addr,
|
TraceAddress: addr,
|
||||||
Type: callType,
|
|
||||||
|
parent: parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native calls
|
trace.setCallType("call")
|
||||||
|
if et.Msg.ReadOnly {
|
||||||
|
trace.setCallType("staticcall")
|
||||||
|
}
|
||||||
|
|
||||||
|
// there are several edge cases thar require special handling when displaying the traces
|
||||||
|
if parent != nil {
|
||||||
|
// Handle Native calls
|
||||||
//
|
//
|
||||||
// When an EVM actor is invoked with a method number above 1023 that's not frc42(InvokeEVM)
|
// When an EVM actor is invoked with a method number above 1023 that's not frc42(InvokeEVM)
|
||||||
// then we need to format native calls in a way that makes sense to Ethereum tooling (convert
|
// then we need to format native calls in a way that makes sense to Ethereum tooling (convert
|
||||||
// the input & output to solidity ABI format).
|
// the input & output to solidity ABI format).
|
||||||
if parentEt != nil {
|
if builtinactors.IsEvmActor(parent.Action.CodeCid) && et.Msg.Method > 1023 && et.Msg.Method != builtin2.MethodsEVM.InvokeContract {
|
||||||
if builtinactors.IsEvmActor(parentEt.Msg.CodeCid) && et.Msg.Method > 1023 && et.Msg.Method != builtin2.MethodsEVM.InvokeContract {
|
|
||||||
log.Infof("found Native call! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
log.Infof("found Native call! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
||||||
input, _ := handle_filecoin_method_input(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
input, _ := handle_filecoin_method_input(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
||||||
trace.Action.Input = hex.EncodeToString(input)
|
trace.Action.Input = hex.EncodeToString(input)
|
||||||
output, _ := handle_filecoin_method_output(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
|
output, _ := handle_filecoin_method_output(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
|
||||||
trace.Result.Output = hex.EncodeToString(output)
|
trace.Result.Output = hex.EncodeToString(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Native actor creation
|
||||||
|
//
|
||||||
|
// Actor A calls to the init actor on method 2 and The init actor creates the target actor B then calls it on method 1
|
||||||
|
if parent.Action.To == builtin.InitActorAddr.String() && parent.Action.Method == 2 && et.Msg.Method == 1 {
|
||||||
|
log.Infof("Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
||||||
|
parent.setCallType("create")
|
||||||
|
parent.Action.To = et.Msg.To.String()
|
||||||
|
parent.Action.Input = hex.EncodeToString([]byte{0x0, 0x0, 0x0, 0xFE})
|
||||||
|
parent.Result.Output = ""
|
||||||
|
|
||||||
|
// there should never be any subcalls when creating a native actor
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native actor creation
|
// Handle EVM contract creation
|
||||||
//
|
//
|
||||||
// TODO...
|
// To detect EVM contract creation we need to check for the following sequence of events:
|
||||||
|
|
||||||
// EVM contract creation
|
|
||||||
//
|
//
|
||||||
// TODO...
|
// 1) EVM contract A calls the EAM (Ethereum Address Manager) on method 2 (create) or 3 (create2).
|
||||||
|
// 2) The EAM calls the init actor on method 3 (Exec4).
|
||||||
|
// 3) The init actor creates the target actor B then calls it on method 1.
|
||||||
|
if parent.parent != nil {
|
||||||
|
calledCreateOnEAM := parent.parent.Action.To == builtin.EthereumAddressManagerActorAddr.String() && (parent.parent.Action.Method == builtin2.MethodsEAM.Create ||
|
||||||
|
parent.parent.Action.Method == builtin2.MethodsEAM.Create2)
|
||||||
|
eamCalledInitOnExec4 := parent.Action.To == builtin.InitActorAddr.String() && parent.Action.Method == builtin.MethodsInit.Exec4
|
||||||
|
initCreatedActor := trace.Action.Method == builtin.MethodsInit.Constructor
|
||||||
|
|
||||||
// EVM call special casing
|
if calledCreateOnEAM && eamCalledInitOnExec4 && initCreatedActor {
|
||||||
//
|
log.Infof("EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
||||||
// Any outbound call from an EVM actor on methods 1-1023 are side-effects from EVM instructions
|
|
||||||
// and should be dropped from the trace.
|
|
||||||
if parentEt != nil {
|
|
||||||
if builtinactors.IsEvmActor(parentEt.Msg.CodeCid) && et.Msg.Method > 0 && et.Msg.Method <= 1023 {
|
|
||||||
log.Infof("found outbound call from an EVM actor on method 1-1023 method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
|
||||||
|
|
||||||
// skip current trace but process subcalls
|
if parent.parent.Action.Method == builtin2.MethodsEAM.Create {
|
||||||
for i, call := range et.Subcalls {
|
parent.parent.setCallType("CREATE")
|
||||||
buildTraces(traces, append(addr, i), call, &et, height)
|
} else {
|
||||||
|
parent.parent.setCallType("CREATE2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the parent.parent to make this
|
||||||
|
parent.parent.Action.To = trace.Action.To
|
||||||
|
parent.parent.Subtraces = 0
|
||||||
|
|
||||||
|
// delete the parent (the EAM) and skip the current trace (init)
|
||||||
|
*traces = (*traces)[:len(*traces)-1]
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle EVM call special casing
|
||||||
|
//
|
||||||
|
// Any outbound call from an EVM actor on methods 1-1023 are side-effects from EVM instructions
|
||||||
|
// and should be dropped from the trace.
|
||||||
|
if builtinactors.IsEvmActor(parent.Action.CodeCid) && et.Msg.Method > 0 && et.Msg.Method <= 1023 {
|
||||||
|
log.Infof("found outbound call from an EVM actor on method 1-1023 method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
||||||
|
|
||||||
|
for i, call := range et.Subcalls {
|
||||||
|
buildTraces(traces, trace, append(addr, i), call, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// EVM -> EVM calls
|
// EVM -> EVM calls
|
||||||
//
|
//
|
||||||
// Check for normal EVM to EVM calls and decode the params and return values
|
// Check for normal EVM to EVM calls and decode the params and return values
|
||||||
if parentEt != nil {
|
if builtinactors.IsEvmActor(parent.Action.CodeCid) && builtinactors.IsEthAccountActor(et.Msg.CodeCid) && et.Msg.Method == builtin2.MethodsEVM.InvokeContract {
|
||||||
if builtinactors.IsEvmActor(parentEt.Msg.CodeCid) && builtinactors.IsEthAccountActor(et.Msg.CodeCid) && et.Msg.Method == builtin2.MethodsEVM.InvokeContract {
|
log.Infof("found evm to evm! ! height: %d", height)
|
||||||
log.Infof("evm to evm! ! ")
|
|
||||||
input, _ := handle_filecoin_method_input(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
input, _ := handle_filecoin_method_input(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
||||||
trace.Action.Input = hex.EncodeToString(input)
|
trace.Action.Input = hex.EncodeToString(input)
|
||||||
output, _ := handle_filecoin_method_output(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
|
output, _ := handle_filecoin_method_output(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
|
||||||
trace.Result.Output = hex.EncodeToString(output)
|
trace.Result.Output = hex.EncodeToString(output)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if et.Msg.From == et.Msg.To && et.Msg.Method == builtin2.MethodsEVM.InvokeContractDelegate {
|
// Handle delegate calls
|
||||||
log.Info("from and to are the same, and method is InvokeContractDelegate!!!!!!!!, height:%d", height)
|
//
|
||||||
|
// 1) Look for from an EVM actor to itself on InvokeContractDelegate, method 6.
|
||||||
|
// 2) Search backwards in the trace for a call to another actor (A) on method 3 (GetBytecode)
|
||||||
|
// 3) Treat this as a delegate call to actor A.
|
||||||
|
// TODO: implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
*traces = append(*traces, trace)
|
*traces = append(*traces, trace)
|
||||||
|
|
||||||
for i, call := range et.Subcalls {
|
for i, call := range et.Subcalls {
|
||||||
buildTraces(traces, append(addr, i), call, &et, height)
|
buildTraces(traces, trace, append(addr, i), call, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user