Address most recent comments
This commit is contained in:
parent
10a5480895
commit
029a4a72b8
@ -939,6 +939,7 @@ type EthTrace struct {
|
|||||||
Type string `json:"Type"`
|
Type string `json:"Type"`
|
||||||
|
|
||||||
Parent *EthTrace `json:"-"`
|
Parent *EthTrace `json:"-"`
|
||||||
|
LastByteCode *EthTrace `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *EthTrace) SetCallType(callType string) {
|
func (t *EthTrace) SetCallType(callType string) {
|
||||||
@ -964,8 +965,8 @@ type EthTraceReplayBlockTransaction struct {
|
|||||||
|
|
||||||
type EthTraceAction struct {
|
type EthTraceAction struct {
|
||||||
CallType string `json:"callType"`
|
CallType string `json:"callType"`
|
||||||
From string `json:"from"`
|
From EthAddress `json:"from"`
|
||||||
To string `json:"to"`
|
To EthAddress `json:"to"`
|
||||||
Gas EthUint64 `json:"gas"`
|
Gas EthUint64 `json:"gas"`
|
||||||
Input EthBytes `json:"input"`
|
Input EthBytes `json:"input"`
|
||||||
Value EthBigInt `json:"value"`
|
Value EthBigInt `json:"value"`
|
||||||
|
@ -20,14 +20,15 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// decodePayload is a utility function which decodes the payload using the given codec
|
||||||
func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) {
|
func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) {
|
||||||
if len(payload) == 0 {
|
if len(payload) == 0 {
|
||||||
return ethtypes.EthBytes(nil), nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch multicodec.Code(codec) {
|
switch multicodec.Code(codec) {
|
||||||
case multicodec.Identity:
|
case multicodec.Identity:
|
||||||
return ethtypes.EthBytes(nil), nil
|
return nil, nil
|
||||||
case multicodec.DagCbor, multicodec.Cbor:
|
case multicodec.DagCbor, multicodec.Cbor:
|
||||||
buf, err := cbg.ReadByteArray(bytes.NewReader(payload), uint64(len(payload)))
|
buf, err := cbg.ReadByteArray(bytes.NewReader(payload), uint64(len(payload)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -61,8 +62,8 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
|
|
||||||
trace := ðtypes.EthTrace{
|
trace := ðtypes.EthTrace{
|
||||||
Action: ethtypes.EthTraceAction{
|
Action: ethtypes.EthTraceAction{
|
||||||
From: from.String(),
|
From: from,
|
||||||
To: to.String(),
|
To: to,
|
||||||
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
|
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
|
||||||
Input: nil,
|
Input: nil,
|
||||||
Value: ethtypes.EthBigInt(et.Msg.Value),
|
Value: ethtypes.EthBigInt(et.Msg.Value),
|
||||||
@ -76,16 +77,17 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
|
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
|
||||||
Output: nil,
|
Output: nil,
|
||||||
},
|
},
|
||||||
Subtraces: len(et.Subcalls),
|
Subtraces: 0, // will be updated by the children once they are added to the trace
|
||||||
TraceAddress: addr,
|
TraceAddress: addr,
|
||||||
|
|
||||||
Parent: parent,
|
Parent: parent,
|
||||||
|
LastByteCode: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
trace.SetCallType("call")
|
trace.SetCallType("call")
|
||||||
|
|
||||||
if et.Msg.Method == builtin.MethodsEVM.InvokeContract {
|
if et.Msg.Method == builtin.MethodsEVM.InvokeContract {
|
||||||
log.Debugf("found InvokeContract call at height: %d", height)
|
log.Debugf("COND1 found InvokeContract call at height: %d", height)
|
||||||
|
|
||||||
// TODO: ignore return errors since actors can send gibberish and we don't want
|
// TODO: ignore return errors since actors can send gibberish and we don't want
|
||||||
// to fail the whole trace in that case
|
// to fail the whole trace in that case
|
||||||
@ -99,7 +101,7 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
}
|
}
|
||||||
} else if et.Msg.To == builtin.EthereumAddressManagerActorAddr &&
|
} else if et.Msg.To == builtin.EthereumAddressManagerActorAddr &&
|
||||||
et.Msg.Method == builtin.MethodsEAM.CreateExternal {
|
et.Msg.Method == builtin.MethodsEAM.CreateExternal {
|
||||||
log.Debugf("found CreateExternal call at height: %d", height)
|
log.Debugf("COND2 found CreateExternal call at height: %d", height)
|
||||||
trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec)
|
trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("buildTraces: %w", err)
|
return xerrors.Errorf("buildTraces: %w", err)
|
||||||
@ -119,6 +121,8 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
// treat this as a contract creation
|
// treat this as a contract creation
|
||||||
trace.SetCallType("create")
|
trace.SetCallType("create")
|
||||||
} else {
|
} else {
|
||||||
|
// we are going to assume a native method, but we may change it in one of the edge cases below
|
||||||
|
// TODO: only do this if we know it's a native method (optimization)
|
||||||
trace.Action.Input, err = handleFilecoinMethodInput(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
trace.Action.Input, err = handleFilecoinMethodInput(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("buildTraces: %w", err)
|
return xerrors.Errorf("buildTraces: %w", err)
|
||||||
@ -134,7 +138,8 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
trace.SetCallType("staticcall")
|
trace.SetCallType("staticcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
// there are several edge cases thar require special handling when displaying the traces
|
// there are several edge cases that require special handling when displaying the traces. Note that while iterating over
|
||||||
|
// the traces we update the trace backwards (through the parent pointer)
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
// Handle Native actor creation
|
// Handle Native actor creation
|
||||||
//
|
//
|
||||||
@ -142,10 +147,10 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
if parent.Action.FilecoinTo == builtin.InitActorAddr &&
|
if parent.Action.FilecoinTo == builtin.InitActorAddr &&
|
||||||
parent.Action.FilecoinMethod == builtin.MethodsInit.Exec &&
|
parent.Action.FilecoinMethod == builtin.MethodsInit.Exec &&
|
||||||
et.Msg.Method == builtin.MethodConstructor {
|
et.Msg.Method == builtin.MethodConstructor {
|
||||||
log.Debugf("Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
log.Debugf("COND3 Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
||||||
parent.SetCallType("create")
|
parent.SetCallType("create")
|
||||||
parent.Action.To = et.Msg.To.String()
|
parent.Action.To = to
|
||||||
parent.Action.Input = []byte{0x0, 0x0, 0x0, 0xFE}
|
parent.Action.Input = []byte{0xFE}
|
||||||
parent.Result.Output = nil
|
parent.Result.Output = nil
|
||||||
|
|
||||||
// there should never be any subcalls when creating a native actor
|
// there should never be any subcalls when creating a native actor
|
||||||
@ -166,8 +171,9 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
parent.Action.FilecoinMethod == builtin.MethodsInit.Exec4
|
parent.Action.FilecoinMethod == builtin.MethodsInit.Exec4
|
||||||
initCreatedActor := trace.Action.FilecoinMethod == builtin.MethodConstructor
|
initCreatedActor := trace.Action.FilecoinMethod == builtin.MethodConstructor
|
||||||
|
|
||||||
|
// TODO: We need to handle failures in contract creations and support resurrections on an existing but dead EVM actor)
|
||||||
if calledCreateOnEAM && eamCalledInitOnExec4 && initCreatedActor {
|
if calledCreateOnEAM && eamCalledInitOnExec4 && initCreatedActor {
|
||||||
log.Debugf("EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
log.Debugf("COND4 EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height)
|
||||||
|
|
||||||
if parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create {
|
if parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create {
|
||||||
parent.Parent.SetCallType("create")
|
parent.Parent.SetCallType("create")
|
||||||
@ -186,6 +192,20 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle delegate calls
|
||||||
|
//
|
||||||
|
// 1) Look for trace from an EVM actor to itself on InvokeContractDelegate, method 6.
|
||||||
|
// 2) Check that the previous trace calls another actor on method 3 (GetByteCode) and they are at the same level (same parent)
|
||||||
|
// 3) Treat this as a delegate call to actor A.
|
||||||
|
if parent.LastByteCode != nil && trace.Action.From == trace.Action.To &&
|
||||||
|
trace.Action.FilecoinMethod == builtin.MethodsEVM.InvokeContractDelegate {
|
||||||
|
log.Debugf("COND7 found delegate call, height: %d", height)
|
||||||
|
prev := parent.LastByteCode
|
||||||
|
if prev.Action.From == trace.Action.From && prev.Action.FilecoinMethod == builtin.MethodsEVM.GetBytecode && prev.Parent == trace.Parent {
|
||||||
|
trace.SetCallType("delegatecall")
|
||||||
|
trace.Action.To = prev.Action.To
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Handle EVM call special casing
|
// Handle EVM call special casing
|
||||||
//
|
//
|
||||||
// Any outbound call from an EVM actor on methods 1-1023 are side-effects from EVM instructions
|
// Any outbound call from an EVM actor on methods 1-1023 are side-effects from EVM instructions
|
||||||
@ -193,44 +213,21 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
|
|||||||
if builtinactors.IsEvmActor(parent.Action.FilecoinCodeCid) &&
|
if builtinactors.IsEvmActor(parent.Action.FilecoinCodeCid) &&
|
||||||
et.Msg.Method > 0 &&
|
et.Msg.Method > 0 &&
|
||||||
et.Msg.Method <= 1023 {
|
et.Msg.Method <= 1023 {
|
||||||
log.Debugf("found outbound call from an EVM actor on method 1-1023 method:%d, code:%s, height:%d", et.Msg.Method, parent.Action.FilecoinCodeCid.String(), height)
|
log.Debugf("COND5 found outbound call from an EVM actor on method 1-1023 method:%d, code:%s, height:%d", et.Msg.Method, parent.Action.FilecoinCodeCid.String(), height)
|
||||||
// TODO: if I handle this case and drop this call from the trace then I am not able to detect delegate calls
|
|
||||||
|
if et.Msg.Method == builtin.MethodsEVM.GetBytecode {
|
||||||
|
// save the last bytecode trace to handle delegate calls
|
||||||
|
parent.LastByteCode = trace
|
||||||
}
|
}
|
||||||
|
|
||||||
// EVM -> EVM calls
|
return nil
|
||||||
//
|
|
||||||
// Check for normal EVM to EVM calls and decode the params and return values
|
|
||||||
if builtinactors.IsEvmActor(parent.Action.FilecoinCodeCid) &&
|
|
||||||
builtinactors.IsEthAccountActor(et.Msg.CodeCid) &&
|
|
||||||
et.Msg.Method == builtin.MethodsEVM.InvokeContract {
|
|
||||||
log.Debugf("found evm to evm call, code:%s, height: %d", et.Msg.CodeCid.String(), height)
|
|
||||||
input, err := handleFilecoinMethodInput(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
trace.Action.Input = input
|
|
||||||
output, err := handleFilecoinMethodOutput(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
trace.Result.Output = output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle delegate calls
|
// we are adding trace to the traces so update the parent subtraces count as it was originally set to zero
|
||||||
//
|
if parent != nil {
|
||||||
// 1) Look for trace from an EVM actor to itself on InvokeContractDelegate, method 6.
|
parent.Subtraces += 1
|
||||||
// 2) Check that the previous trace calls another actor on method 3 (GetByteCode) and they are at the same level (same parent)
|
|
||||||
// 3) Treat this as a delegate call to actor A.
|
|
||||||
if trace.Action.From == trace.Action.To &&
|
|
||||||
trace.Action.FilecoinMethod == builtin.MethodsEVM.InvokeContractDelegate &&
|
|
||||||
len(*traces) > 0 {
|
|
||||||
log.Debugf("found delegate call, height: %d", height)
|
|
||||||
prev := (*traces)[len(*traces)-1]
|
|
||||||
if prev.Action.From == trace.Action.From && prev.Action.FilecoinMethod == builtin.MethodsEVM.GetBytecode && prev.Parent == trace.Parent {
|
|
||||||
trace.SetCallType("delegatecall")
|
|
||||||
trace.Action.To = prev.Action.To
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*traces = append(*traces, trace)
|
*traces = append(*traces, trace)
|
||||||
|
Loading…
Reference in New Issue
Block a user