Decode eth param/return values and change them to ethbytes type

This commit is contained in:
Fridrik Asmundsson 2023-08-24 20:05:11 +00:00
parent cb5e6e0cd1
commit cf255127a4
7 changed files with 88 additions and 37 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -967,7 +967,7 @@ type EthTraceAction struct {
From string `json:"from"` From string `json:"from"`
To string `json:"to"` To string `json:"to"`
Gas EthUint64 `json:"gas"` Gas EthUint64 `json:"gas"`
Input string `json:"input"` Input EthBytes `json:"input"`
Value EthBigInt `json:"value"` Value EthBigInt `json:"value"`
Method abi.MethodNum `json:"-"` Method abi.MethodNum `json:"-"`
@ -976,5 +976,5 @@ type EthTraceAction struct {
type EthTraceResult struct { type EthTraceResult struct {
GasUsed EthUint64 `json:"gasUsed"` GasUsed EthUint64 `json:"gasUsed"`
Output string `json:"output"` Output EthBytes `json:"output"`
} }

View File

@ -3109,12 +3109,12 @@ Response:
"from": "string value", "from": "string value",
"to": "string value", "to": "string value",
"gas": "0x5", "gas": "0x5",
"input": "string value", "input": "0x07",
"value": "0x0" "value": "0x0"
}, },
"result": { "result": {
"gasUsed": "0x5", "gasUsed": "0x5",
"output": "string value" "output": "0x07"
}, },
"subtraces": 123, "subtraces": 123,
"traceAddress": [ "traceAddress": [
@ -3158,12 +3158,12 @@ Response:
"from": "string value", "from": "string value",
"to": "string value", "to": "string value",
"gas": "0x5", "gas": "0x5",
"input": "string value", "input": "0x07",
"value": "0x0" "value": "0x0"
}, },
"result": { "result": {
"gasUsed": "0x5", "gasUsed": "0x5",
"output": "string value" "output": "0x07"
}, },
"subtraces": 123, "subtraces": 123,
"traceAddress": [ "traceAddress": [

View File

@ -4,29 +4,59 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
"encoding/hex"
"fmt" "fmt"
"io" "io"
"github.com/multiformats/go-multicodec"
cbg "github.com/whyrusleeping/cbor-gen"
"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/builtin"
"github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/exitcode"
"golang.org/x/xerrors"
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/chain/types/ethtypes"
) )
func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) {
if len(payload) == 0 {
return ethtypes.EthBytes(nil), nil
}
switch multicodec.Code(codec) {
case multicodec.Identity:
return ethtypes.EthBytes(nil), nil
case multicodec.DagCbor, multicodec.Cbor:
buf, err := cbg.ReadByteArray(bytes.NewReader(payload), uint64(len(payload)))
if err != nil {
return nil, xerrors.Errorf("decodePayload: failed to decode cbor payload: %w", err)
}
return buf, nil
case multicodec.Raw:
return ethtypes.EthBytes(payload), nil
}
return nil, xerrors.Errorf("decodePayload: unsupported codec: %d", codec)
}
// 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(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr []int, et types.ExecutionTrace, height int64, sa StateAPI) error { func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr []int, et types.ExecutionTrace, height int64, sa StateAPI) error {
// lookup the eth address from the from/to addresses. Note that this may fail but to support
// this we need to include the ActorID in the trace. For now, just log a warning and skip
// this trace.
//
// TODO: Add ActorID in trace, see https://github.com/filecoin-project/lotus/pull/11100#discussion_r1302442288
from, err := lookupEthAddress(ctx, et.Msg.From, sa) from, err := lookupEthAddress(ctx, et.Msg.From, sa)
if err != nil { if err != nil {
return xerrors.Errorf("buildTraces: failed to lookup from address %s: %w", et.Msg.From, err) log.Warnf("buildTraces: failed to lookup from address %s: %v", et.Msg.From, err)
return nil
} }
to, err := lookupEthAddress(ctx, et.Msg.To, sa) to, err := lookupEthAddress(ctx, et.Msg.To, sa)
if err != nil { if err != nil {
return xerrors.Errorf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err) log.Warnf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err)
return nil
} }
trace := &ethtypes.EthTrace{ trace := &ethtypes.EthTrace{
@ -34,14 +64,14 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
From: from.String(), From: from.String(),
To: to.String(), To: to.String(),
Gas: ethtypes.EthUint64(et.Msg.GasLimit), Gas: ethtypes.EthUint64(et.Msg.GasLimit),
Input: hex.EncodeToString(et.Msg.Params), Input: nil,
Value: ethtypes.EthBigInt(et.Msg.Value), Value: ethtypes.EthBigInt(et.Msg.Value),
Method: et.Msg.Method, Method: et.Msg.Method,
CodeCid: et.Msg.CodeCid, CodeCid: et.Msg.CodeCid,
}, },
Result: ethtypes.EthTraceResult{ Result: ethtypes.EthTraceResult{
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
Output: hex.EncodeToString(et.MsgRct.Return), Output: nil,
}, },
Subtraces: len(et.Subcalls), Subtraces: len(et.Subcalls),
TraceAddress: addr, TraceAddress: addr,
@ -51,6 +81,48 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
trace.SetCallType("call") trace.SetCallType("call")
if parent != nil && builtinactors.IsEvmActor(parent.Action.CodeCid) && et.Msg.Method == builtin.MethodsEVM.InvokeContract {
log.Debugf("found InvokeContract call at height: %d", height)
trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec)
if err != nil {
return xerrors.Errorf("buildTraces: %w", err)
}
trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec)
if err != nil {
return xerrors.Errorf("buildTraces: %w", err)
}
} else if et.Msg.To == builtin.EthereumAddressManagerActorAddr &&
et.Msg.Method == builtin.MethodsEAM.CreateExternal {
log.Debugf("found CreateExternal call at height: %d", height)
trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec)
if err != nil {
return xerrors.Errorf("buildTraces: %w", err)
}
if et.MsgRct.ExitCode.IsSuccess() {
// ignore return value
trace.Result.Output = nil
} else {
// return value is the error message
trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec)
if err != nil {
return xerrors.Errorf("buildTraces: %w", err)
}
}
// treat this as a contract creation
trace.SetCallType("create")
} else {
trace.Action.Input, err = handleFilecoinMethodInput(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
if err != nil {
return xerrors.Errorf("buildTraces: %w", err)
}
trace.Result.Output, err = handleFilecoinMethodOutput(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
if err != nil {
return xerrors.Errorf("buildTraces: %w", err)
}
}
// TODO: is it OK to check this here or is this only specific to certain edge case (evm to evm)? // TODO: is it OK to check this here or is this only specific to certain edge case (evm to evm)?
if et.Msg.ReadOnly { if et.Msg.ReadOnly {
trace.SetCallType("staticcall") trace.SetCallType("staticcall")
@ -58,27 +130,6 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
// there are several edge cases thar require special handling when displaying the traces // there are several edge cases thar require special handling when displaying the traces
if parent != nil { if parent != nil {
// Handle Native calls
//
// 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
// the input & output to solidity ABI format).
if builtinactors.IsEvmActor(parent.Action.CodeCid) &&
et.Msg.Method > 1023 &&
et.Msg.Method != builtin.MethodsEVM.InvokeContract {
log.Debugf("found Native call! method:%d, code:%s, height:%d", et.Msg.Method, 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 = hex.EncodeToString(input)
output, err := handleFilecoinMethodOutput(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
if err != nil {
return err
}
trace.Result.Output = hex.EncodeToString(output)
}
// Handle Native actor creation // 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 // 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
@ -88,8 +139,8 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
log.Debugf("Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height) log.Debugf("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 = et.Msg.To.String()
parent.Action.Input = hex.EncodeToString([]byte{0x0, 0x0, 0x0, 0xFE}) parent.Action.Input = []byte{0x0, 0x0, 0x0, 0xFE}
parent.Result.Output = "" 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
return nil return nil
@ -151,12 +202,12 @@ func buildTraces(ctx context.Context, traces *[]*ethtypes.EthTrace, parent *etht
if err != nil { if err != nil {
return err return err
} }
trace.Action.Input = hex.EncodeToString(input) trace.Action.Input = input
output, err := handleFilecoinMethodOutput(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return) output, err := handleFilecoinMethodOutput(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return)
if err != nil { if err != nil {
return err return err
} }
trace.Result.Output = hex.EncodeToString(output) trace.Result.Output = output
} }
// Handle delegate calls // Handle delegate calls