diff --git a/node/impl/full/trace.go b/node/impl/full/trace.go index cab153e53b8..dfd30e603db 100644 --- a/node/impl/full/trace.go +++ b/node/impl/full/trace.go @@ -84,9 +84,8 @@ type Action struct { Input string `json:"input"` Value ethtypes.EthBigInt `json:"value"` - // TODO: remove these fields from json output - Method abi.MethodNum `json:"method"` - CodeCid cid.Cid `json:"codeCid"` + method abi.MethodNum `json:"-"` + codeCid cid.Cid `json:"-"` } type Result struct { @@ -256,68 +255,6 @@ func write_padded[T any](w io.Writer, data T, size int) error { return nil } -func handle_filecoin_method_input(method abi.MethodNum, codec uint64, params []byte) ([]byte, error) { - NATIVE_METHOD_SELECTOR := []byte{0x86, 0x8e, 0x10, 0xc4} - EVM_WORD_SIZE := 32 - - staticArgs := []uint64{ - uint64(method), - codec, - uint64(EVM_WORD_SIZE) * 3, - uint64(len(params)), - } - totalWords := len(staticArgs) + (len(params) / EVM_WORD_SIZE) - if len(params)%EVM_WORD_SIZE != 0 { - totalWords++ - } - len := 4 + totalWords*EVM_WORD_SIZE - - w := &bytes.Buffer{} - err := binary.Write(w, binary.BigEndian, NATIVE_METHOD_SELECTOR) - if err != nil { - return nil, fmt.Errorf("handle_filecoin_method_input: failed writing method selector: %w", err) - } - - for _, arg := range staticArgs { - err := write_padded(w, arg, 32) - if err != nil { - return nil, fmt.Errorf("handle_filecoin_method_input: %w", err) - } - } - err = binary.Write(w, binary.BigEndian, params) - if err != nil { - return nil, fmt.Errorf("handle_filecoin_method_input: failed writing params: %w", err) - } - remain := len - w.Len() - for i := 0; i < remain; i++ { - err = binary.Write(w, binary.BigEndian, uint8(0)) - if err != nil { - return nil, fmt.Errorf("handle_filecoin_method_input: failed writing tailing zeros: %w", err) - } - } - - return w.Bytes(), nil -} - -func handle_filecoin_method_output(exitCode exitcode.ExitCode, codec uint64, data []byte) ([]byte, error) { - w := &bytes.Buffer{} - - values := []interface{}{uint32(exitCode), codec, uint32(w.Len()), uint32(len(data))} - for _, v := range values { - err := write_padded(w, v, 32) - if err != nil { - return nil, fmt.Errorf("handle_filecoin_method_output: %w", err) - } - } - - err := binary.Write(w, binary.BigEndian, []byte(data)) - if err != nil { - return nil, fmt.Errorf("handle_filecoin_method_output: failed writing data: %w", err) - } - - return w.Bytes(), nil -} - // buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.ExecutionTrace, height int64) error { trace := &Trace{ @@ -327,8 +264,8 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution Gas: ethtypes.EthUint64(et.Msg.GasLimit), Input: hex.EncodeToString(et.Msg.Params), Value: ethtypes.EthBigInt(et.Msg.Value), - Method: et.Msg.Method, - CodeCid: et.Msg.CodeCid, + method: et.Msg.Method, + codeCid: et.Msg.CodeCid, }, Result: Result{ GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), @@ -354,7 +291,9 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution // 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 { + 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 := handle_filecoin_method_input(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params) if err != nil { @@ -371,7 +310,9 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution // 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 { + if parent.Action.To == builtin.InitActorAddr.String() && + parent.Action.method == builtin.MethodsInit.Exec && + et.Msg.Method == builtin.MethodConstructor { log.Debugf("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() @@ -390,15 +331,16 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution // 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 == builtin.MethodsEAM.Create || - parent.parent.Action.Method == builtin.MethodsEAM.Create2) - eamCalledInitOnExec4 := parent.Action.To == builtin.InitActorAddr.String() && parent.Action.Method == builtin.MethodsInit.Exec4 - initCreatedActor := trace.Action.Method == builtin.MethodsInit.Constructor + calledCreateOnEAM := parent.parent.Action.To == builtin.EthereumAddressManagerActorAddr.String() && + (parent.parent.Action.method == builtin.MethodsEAM.Create || parent.parent.Action.method == builtin.MethodsEAM.Create2) + eamCalledInitOnExec4 := parent.Action.To == builtin.InitActorAddr.String() && + parent.Action.method == builtin.MethodsInit.Exec4 + initCreatedActor := trace.Action.method == builtin.MethodConstructor if calledCreateOnEAM && eamCalledInitOnExec4 && initCreatedActor { log.Debugf("EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.Msg.CodeCid.String(), height) - if parent.parent.Action.Method == builtin.MethodsEAM.Create { + if parent.parent.Action.method == builtin.MethodsEAM.Create { parent.parent.setCallType("create") } else { parent.parent.setCallType("create2") @@ -419,15 +361,19 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution // // 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.Debugf("found outbound call from an EVM actor on method 1-1023 method:%d, code:%s, height:%d", et.Msg.Method, parent.Action.CodeCid.String(), height) + if builtinactors.IsEvmActor(parent.Action.codeCid) && + et.Msg.Method > 0 && + 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.codeCid.String(), height) // TODO: if I handle this case and drop this call from the trace then I am not able to detect delegate calls } // EVM -> EVM calls // // Check for normal EVM to EVM calls and decode the params and return values - if builtinactors.IsEvmActor(parent.Action.CodeCid) && builtinactors.IsEthAccountActor(et.Msg.CodeCid) && et.Msg.Method == builtin.MethodsEVM.InvokeContract { + if builtinactors.IsEvmActor(parent.Action.codeCid) && + 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 := handle_filecoin_method_input(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params) if err != nil { @@ -446,10 +392,12 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution // 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 trace.Action.From == trace.Action.To && trace.Action.Method == builtin.MethodsEVM.InvokeContractDelegate && len(*traces) > 0 { + if trace.Action.From == trace.Action.To && + trace.Action.method == 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.Method == builtin.MethodsEVM.GetBytecode && prev.parent == trace.parent { + if prev.Action.From == trace.Action.From && prev.Action.method == builtin.MethodsEVM.GetBytecode && prev.parent == trace.parent { trace.setCallType("delegatecall") trace.Action.To = prev.Action.To } @@ -468,6 +416,68 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution return nil } +func handle_filecoin_method_input(method abi.MethodNum, codec uint64, params []byte) ([]byte, error) { + NATIVE_METHOD_SELECTOR := []byte{0x86, 0x8e, 0x10, 0xc4} + EVM_WORD_SIZE := 32 + + staticArgs := []uint64{ + uint64(method), + codec, + uint64(EVM_WORD_SIZE) * 3, + uint64(len(params)), + } + totalWords := len(staticArgs) + (len(params) / EVM_WORD_SIZE) + if len(params)%EVM_WORD_SIZE != 0 { + totalWords++ + } + len := 4 + totalWords*EVM_WORD_SIZE + + w := &bytes.Buffer{} + err := binary.Write(w, binary.BigEndian, NATIVE_METHOD_SELECTOR) + if err != nil { + return nil, fmt.Errorf("handle_filecoin_method_input: failed writing method selector: %w", err) + } + + for _, arg := range staticArgs { + err := write_padded(w, arg, 32) + if err != nil { + return nil, fmt.Errorf("handle_filecoin_method_input: %w", err) + } + } + err = binary.Write(w, binary.BigEndian, params) + if err != nil { + return nil, fmt.Errorf("handle_filecoin_method_input: failed writing params: %w", err) + } + remain := len - w.Len() + for i := 0; i < remain; i++ { + err = binary.Write(w, binary.BigEndian, uint8(0)) + if err != nil { + return nil, fmt.Errorf("handle_filecoin_method_input: failed writing tailing zeros: %w", err) + } + } + + return w.Bytes(), nil +} + +func handle_filecoin_method_output(exitCode exitcode.ExitCode, codec uint64, data []byte) ([]byte, error) { + w := &bytes.Buffer{} + + values := []interface{}{uint32(exitCode), codec, uint32(w.Len()), uint32(len(data))} + for _, v := range values { + err := write_padded(w, v, 32) + if err != nil { + return nil, fmt.Errorf("handle_filecoin_method_output: %w", err) + } + } + + err := binary.Write(w, binary.BigEndian, data) + if err != nil { + return nil, fmt.Errorf("handle_filecoin_method_output: failed writing data: %w", err) + } + + return w.Bytes(), nil +} + // TODO: refactor this to be shared code func (e *EthTrace) getTipsetByBlockNr(ctx context.Context, blkParam string, strict bool) (*types.TipSet, error) { if blkParam == "earliest" {