From fefa8f36336b78884aaa0373f70daff05d4cdf9d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 29 Nov 2023 16:51:24 +0400 Subject: [PATCH] Improve the correctness of Eth's trace_block - Improve encoding/decoding of parameters and return values: - Encode "native" parameters and return values with Solidity ABI. - Correctly decode parameters to "create" calls. - Use the correct (ish) output for "create" calls. - Handle all forms of "create". - Make robust with respect to reverts: - Use the actor ID/address from the trace instead of looking it up in the state-tree (may not exist in the state-tree due to a revert). - Gracefully handle failed actor/contract creation. - Improve performance: - We avoid looking anything up in the state-tree when translating the trace, which should significantly improve performance. - Improve code readability: - Remove all "backtracking" logic. - Use an "environment" struct to store temporary state instead of attaching it to the trace. - Fix random bugs: - Fix an allocation bug in the "address" logic (need to set the capacity before modifying the slice). - Improved error checking/handling. --- build/openrpc/full.json.gz | Bin 34712 -> 34723 bytes build/openrpc/gateway.json.gz | Bin 11931 -> 11942 bytes chain/types/ethtypes/eth_types.go | 38 +- documentation/en/api-v1-unstable-methods.md | 6 +- node/impl/full/eth.go | 58 +-- node/impl/full/eth_trace.go | 551 ++++++++++++++------ node/impl/full/eth_utils.go | 31 +- 7 files changed, 452 insertions(+), 232 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index dc9bc44ec13c8002b79e97069b6d6e6e6ed4ece5..a716b92330ca5fd46db0f448d18223892d01e12a 100644 GIT binary patch delta 22675 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 WeEkXJcTw9<&2lhImiEvNIJ=KZYCRJxTLLG@t z36>Qc@--q@Tuj_;sABPRhjOk~36~4rZO8$%bX+>)0tLvLa3Klwmr6#6xY>GDdij2(JQmGBPYN^85K*^@rpduM%g1VZ+?ZA#eTayd-1c2+yDc|QI4F= zKoE&9io;lqHv`XemY4>a0g9~wy4i9netzan^SbMRR~4N;#Hrj2*~8^q7mmcye{}fw zvN=GvJXQ0v8>|r8!}S)S{7kHkApHl`5j|?OA2c{{M+bkcXzH(#`g z3Kt>|SIS9Pw(!U`7_KN>OOqJOY`ro1{jPF=er3VOG^&X4z+t=7?e*7Gks+CFFE`uu zZt-CaJDh*d_1rJ2j*m}|3eoE7@lF2Y@}4#kXKbSWWQgM(aCYsOBe)T1QqJ&6apzN zSwO!PYe;0$FYL!HbnV+Iw1`m3i=u2#Az_qEsFr`K$7M_9rB9P2PkPS;%NRsCfk*vr z%e?c@DYy}!Dx4W zKc9d8GjzL;L+i)hvW8B!f0>Pwcd}(~b15@% zE|H7eQ3Sur#>vj57W1ZHflak+n(Uo=nU|+%)uk@gB~8TzDu4F9kj5X<=C$`oZ?Xtg z6o`KxJ^!A6bPFx)`Lz8lB&+m>=Fvg)hD+?B55iBGQ#}+v6B!ZOh1!f7$Z&VRc>X=4 z^GvieI{91l1DZ#@esA}1zjx5vJ7}5LK72Ta!1CNP7yD#wH{Q&(sU~+yFH^~>U{5VG z>4eyRi}d^6GS3_|LIVeW@O=7Nm8_}E@M?cif2Zu$0wv9aEw`Rx_ywC%Xytsp%xENAsNpmFTy|o7vI=aw)*Y8gW}nAI?m)X%T-b zjZ=b))S+2%5xeFdiZ_%L_e;t|5hP`WMwKX$^(`aByqWs-J9EF?qLB-2ffG4?oa)4~ z0>D!7A(<65jbgPOC6y&sfDJ_%Va3gab~b8$^eWuLEi>FCK=JpdNz;9*Eu&h!)2caH z+*i`>ZSCGxbZ_&96i)`5GX3K0I_ZB2zk+m`gOZYSRG`las+|hyHnU*7BoryFYZz}V zWbd3Hd$~F@3&s_?3sPOacIG+1*;aUwb(0+u(Vowr8{=meT*M8(orgLJq= zxd|y_nZ6WrbNl_82c42KK#X_)34?ILYNCyHUO}>z#34&6`g9s=RPTS|b?RtjE#uZF z<8%bHwuTxYF?uCsqc~=W+|jKyhW5-+^$ca8tY#gBFd0BWals8;AvZX>EEfISqJLgxIvv_~dFR2!G1@R{oVCX71!1%t!x9%+~1js|EsSIFdQcbu@PZzJt zq2q2z!Jw;9sw!O>pY$w7#ptSwN@T3O7XF+0&6LE%W?lUrU)}KBH%v4XDfde9x^FSS z@V#YuGneTkQka?PzM3kWYgLZVnQ<%h;A}3843_6tr1aE&%3C@4nrhoZ{a=-nO zR6Ii9C&0<8Fd}>}>?l#Iub^hdh5O)H%kOHAN2{e$1wZ#D@-tnZNY^LQai-5*pGeE_ znkt%bJU-9r(l3P)0)o*At?yS)<&n!^^lFj|E`!mHMLy2i9N`nshUQVbW!?}GSv-OB z=t`!;mU;FNioJgu&%nvDh86^~;@8ThzT4qSK_t1WCVR~x&*F`wbcDqfSx0tM#LaysW zeuSV#lII9G?GcYJ>54u{grH;Kj~M^C;{7ej?T#ZtAr}27p#NZO9__W`|Ap-Fb~dnp z#wtz9~uLH;q03v%CvBhxomJ*CE=nL0{a>32I6-D<`%VG zch(&qRFxY3dHEXR|7g&FBT<@@|+{7jD?;%#a zjXw`z#rxKHP)oUAs&&zaucR=}ggxuet8RZo6GocY%J@TkQE@1eJr0b5PVnj47zFjUZFzMy>D6w`f6) zWX3vx_*JlGjx6#gr>S>>2c}am08aRV6m}{sudjMUnv{xS=cIo^9}N+-$qd=DBWG?> z;5%`?CeJ>B0d(k63U28}54F3rP z`18~bKV4=T4hY8SN6{8g$N5oP_7)fZiz&IxCG&uDn<5J#`8Fc1Y4)9E2jOv7($k;d zsBBc5~v3izkA-7^B?mczyI&Q z5AfrE@brA|ec*ro*Rl0~W&ZK_!)^CtaOR!g&Hc+y55NBh56zZ&Idd=y5xcb1*%##6 z-s81*i?4mp8{UyUKZqp?(FuC}-tOUk@1VDLkm>+@Ouim%8_=RJC!QNXH<$$8joq3H1)^oi*C_sqk9DXpJ>18DQV^VxOrzy*-5 z;A6?KSMZSsdj%hJkXz>a>D2cgpnbeZL817Wyu9)z$c2#It|O`MrXWj~zsWCI>cRrK zj0d)1vXkUK;p$c8OHfc``guYXy@)0^3-%zsz=fwp4yaO5_AG3U=}6xtKv|l!CeJui z!CgD$q$X4CshF&Pif#cZAhM!nSvr?pafbYbs`SSUWObx27Es0PCHp7R zMW~}9HW!G}*B0Wu+wNAC)S`Emc#j3XPhqQsbYHO)WCBRcFdgrb zE+@hCJHZ0(QHP_S<_&l}USP<66SeiT z(23&(F!?oq295*WQSyX1BTi=68xG@VUx;Z$28qasxzWBbqbZ~?+92I2hEGkbl9!)6 z?|uNR`}8DEq2&N-$EM~Zn}(Y#QOLDI_eZE{>RX8PNNuighk}g|>B}%cq$$M^p+i+3 z)r3ssk}oaaH$oxdo;i#a@0rHv*JqpWR@LbuVwF07SWdi|i`DfG4oD5hhs@_ArgU&a zFFQzNa23xI(`(eaht@qde54q;LJLn7lb0ge$0zi_E=_oE;rMSNq!!`dvUepgAi|Px zAsCq?2zA@I_Fh%hs4IvcJ^%hHe5b;&x&8&b`mOQCrj*IGTgr*KwiQe`4_KZRr)R%u z?ps8EoB37cK1*Q}0p3ioMg4}bhGO@ARY?@KN73EPDZpNCW2&zIxqa?%Jveh$ZL0{?befe&{e-Iv7wCTpsMJ^4FoogpjF{HaqksfuJ2i3 zEnY5j<00$wv+}FZ297pxHt1YU5I^r+O}!I;F>lg0!PR?xT@+<69aOj3YvGn81Ms(z zZtcO_VO1578#r~qLXp{rT?G|O!s#t!@0=hzK*q;=GKKEJAgT`0&n`p!$T^h=?5n_v`v-`d32WiSeXMaBd1``vkb82Sg_o20_h zlN(n51kNmys^=j1geC}^M~CFc2MAApz%+Sz?ZXM0P1pl^wYz!LX_?oI8RYW=T4rG? z>|ijp;poBdp}yq>@DYy}!Dx4WKcD_Hbi0p3>&M>Yp*x;<_xN#O&+oy!9}r=78H{c$ zOvB0%rgF61GH(cTES|tQ2tJeO?ZcLN_7Kv+jpyOIw*QF?aN-37;w20MFsf01HHjxr zw)_|b@B@U$bgki&KOUfe7GN|XN08hL8eaijKs>X_qdOFR&>J|Mxe;U%Xhc^*gS5`( z(3*uX?|eX29EN6DTuNgak4}OJLf;W?LQyh7!|+#V!2IDlEFnS-1?anY2yy`X;N3&} zeN=Js!j8&Y$6D5wT#?KWbPW6t5#v8sl&w*|XgrWF)-v&vksc_2)!;nD(^-`LkO3iK zb|o-G4xHFT3bIV{EKXt){Qik1_@p7wEiagtH4?kth{gJ(abJyPXopYFsFhLJ338F{h%AoB}W9{o|Bz1+4!Z$d^)U z@fL<0dq^7u(!<8KgLHOifrTQSjKEA4*?yc=Qwt4kiXldZ_j-9LZ_&%6p*SBF7ECL% z?2Yl*R^n|@<5h_dNRo<`7e{Ui1t#QbbR8BkM z-Un*ABEtFsYCUEe!2bu(*Mbz_&Bc zqng=#A5u)_7zCLTr6h#W%7dai`8!cWNN^xBks1T%H66G@JY#h1YI0HnyKBPTLfsu)Fz@nF~S$B!&o$j`gG)y z6*e(4{$;7jTF;cwGqXgIS1=1&2z>qaYr6S=a<|i~DnZLA-Cvy@`p?P;{WY;bUo$_= z{511>vCOYuRbTWAKv?zUpY<(ccgY!l*O(y}@Ad0D?q@^S$Lo@{H2J7spISHL)H=`D zDT1%f<=e00mQQ`wuFu-_S-U=K*Jtheti4=a+$$*wt5|Zayv*8LOuaSUP(Q^n8eO=RWoT01D+rW_Cq<&! z`8TbSVdLCSMVy7-{Suc{{8IZ*vUGV%P*RWd3mo+5>5!sm%mFACl9b<7QehTQF(rO8 yznN0BS1`XO2udaZvb!BW7C_;YRNS%xx}C$G`SkSt>Hh)%0RR7a{~G2>lL7#mAID$- delta 5499 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 diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index bff15ed24ad..a7341aac40f 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -349,6 +349,13 @@ func IsEthAddress(addr address.Address) bool { return namespace == builtintypes.EthereumAddressManagerActorID && len(payload) == 20 && !bytes.HasPrefix(payload, maskedIDPrefix[:]) } +func EthAddressFromActorID(id abi.ActorID) EthAddress { + var ethaddr EthAddress + ethaddr[0] = 0xff + binary.BigEndian.PutUint64(ethaddr[12:], uint64(id)) + return ethaddr +} + func EthAddressFromFilecoinAddress(addr address.Address) (EthAddress, error) { switch addr.Protocol() { case address.ID: @@ -356,10 +363,7 @@ func EthAddressFromFilecoinAddress(addr address.Address) (EthAddress, error) { if err != nil { return EthAddress{}, err } - var ethaddr EthAddress - ethaddr[0] = 0xff - binary.BigEndian.PutUint64(ethaddr[12:], id) - return ethaddr, nil + return EthAddressFromActorID(abi.ActorID(id)), nil case address.Delegated: payload := addr.Payload() namespace, n, err := varint.FromUvarint(payload) @@ -987,13 +991,8 @@ type EthTrace struct { Result EthTraceResult `json:"result"` Subtraces int `json:"subtraces"` TraceAddress []int `json:"traceAddress"` - Type string `json:"Type"` - - Parent *EthTrace `json:"-"` - - // if a subtrace makes a call to GetBytecode, we store a pointer to that subtrace here - // which we then lookup when checking for delegatecall (InvokeContractDelegate) - LastByteCode *EthTrace `json:"-"` + Type string `json:"type"` + Error string `json:"error,omitempty"` } func (t *EthTrace) SetCallType(callType string) { @@ -1018,17 +1017,12 @@ type EthTraceReplayBlockTransaction struct { } type EthTraceAction struct { - CallType string `json:"callType"` - From EthAddress `json:"from"` - To EthAddress `json:"to"` - Gas EthUint64 `json:"gas"` - Input EthBytes `json:"input"` - Value EthBigInt `json:"value"` - - FilecoinMethod abi.MethodNum `json:"-"` - FilecoinCodeCid cid.Cid `json:"-"` - FilecoinFrom address.Address `json:"-"` - FilecoinTo address.Address `json:"-"` + CallType string `json:"callType"` + From EthAddress `json:"from"` + To *EthAddress `json:"to"` + Gas EthUint64 `json:"gas"` + Input EthBytes `json:"input"` + Value EthBigInt `json:"value"` } type EthTraceResult struct { diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index c24cdc6dd33..6c128010c3e 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -3110,7 +3110,8 @@ Response: "traceAddress": [ 123 ], - "Type": "string value", + "type": "string value", + "error": "string value", "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "blockNumber": 9, "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", @@ -3159,7 +3160,8 @@ Response: "traceAddress": [ 123 ], - "Type": "string value" + "type": "string value", + "error": "string value" } ], "transactionHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index cbe491afe1c..92f3fe81336 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -20,7 +20,6 @@ import ( "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/builtin" builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/builtin/v10/evm" "github.com/filecoin-project/go-state-types/exitcode" @@ -875,19 +874,21 @@ func (a *EthModule) EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtyp return nil, xerrors.Errorf("failed to get transaction hash by cid: %w", err) } if txHash == nil { - log.Warnf("cannot find transaction hash for cid %s", ir.MsgCid) - continue + return nil, xerrors.Errorf("cannot find transaction hash for cid %s", ir.MsgCid) } - traces := []*ethtypes.EthTrace{} - err = buildTraces(&traces, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), st) + env, err := baseEnvironment(st, ir.Msg.From) if err != nil { - return nil, xerrors.Errorf("failed building traces: %w", err) + return nil, xerrors.Errorf("when processing message %s: %w", ir.MsgCid, err) } - traceBlocks := make([]*ethtypes.EthTraceBlock, 0, len(traces)) - for _, trace := range traces { - traceBlocks = append(traceBlocks, ðtypes.EthTraceBlock{ + err = buildTraces(env, []int{}, &ir.ExecutionTrace) + if err != nil { + return nil, xerrors.Errorf("failed building traces for msg %s: %w", ir.MsgCid, err) + } + + for _, trace := range env.traces { + allTraces = append(allTraces, ðtypes.EthTraceBlock{ EthTrace: trace, BlockHash: blkHash, BlockNumber: int64(ts.Height()), @@ -895,8 +896,6 @@ func (a *EthModule) EthTraceBlock(ctx context.Context, blkNum string) ([]*ethtyp TransactionPosition: msgIdx, }) } - - allTraces = append(allTraces, traceBlocks...) } return allTraces, nil @@ -934,34 +933,31 @@ func (a *EthModule) EthTraceReplayBlockTransactions(ctx context.Context, blkNum return nil, xerrors.Errorf("failed to get transaction hash by cid: %w", err) } if txHash == nil { - log.Warnf("cannot find transaction hash for cid %s", ir.MsgCid) - continue + return nil, xerrors.Errorf("cannot find transaction hash for cid %s", ir.MsgCid) } - var output ethtypes.EthBytes - invokeCreateOnEAM := ir.Msg.To == builtin.EthereumAddressManagerActorAddr && (ir.Msg.Method == builtin.MethodsEAM.Create || ir.Msg.Method == builtin.MethodsEAM.Create2) - if ir.Msg.Method == builtin.MethodsEVM.InvokeContract || invokeCreateOnEAM { - output, err = decodePayload(ir.ExecutionTrace.MsgRct.Return, ir.ExecutionTrace.MsgRct.ReturnCodec) - if err != nil { - return nil, xerrors.Errorf("failed to decode payload: %w", err) - } - } else { - output = encodeFilecoinReturnAsABI(ir.ExecutionTrace.MsgRct.ExitCode, ir.ExecutionTrace.MsgRct.ReturnCodec, ir.ExecutionTrace.MsgRct.Return) + env, err := baseEnvironment(st, ir.Msg.From) + if err != nil { + return nil, xerrors.Errorf("when processing message %s: %w", ir.MsgCid, err) } - t := ethtypes.EthTraceReplayBlockTransaction{ - Output: output, - TransactionHash: *txHash, - StateDiff: nil, - VmTrace: nil, + err = buildTraces(env, []int{}, &ir.ExecutionTrace) + if err != nil { + return nil, xerrors.Errorf("failed building traces for msg %s: %w", ir.MsgCid, err) } - err = buildTraces(&t.Trace, nil, []int{}, ir.ExecutionTrace, int64(ts.Height()), st) - if err != nil { - return nil, xerrors.Errorf("failed building traces: %w", err) + var output []byte + if len(env.traces) > 0 { + output = env.traces[0].Result.Output } - allTraces = append(allTraces, &t) + allTraces = append(allTraces, ðtypes.EthTraceReplayBlockTransaction{ + Output: output, + TransactionHash: *txHash, + Trace: env.traces, + StateDiff: nil, + VmTrace: nil, + }) } return allTraces, nil diff --git a/node/impl/full/eth_trace.go b/node/impl/full/eth_trace.go index 123d96fe103..e1d1ee1ec58 100644 --- a/node/impl/full/eth_trace.go +++ b/node/impl/full/eth_trace.go @@ -7,8 +7,11 @@ import ( cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/builtin" - "github.com/filecoin-project/go-state-types/builtin/v10/evm" + "github.com/filecoin-project/go-state-types/builtin/v12/eam" + "github.com/filecoin-project/go-state-types/builtin/v12/evm" + "github.com/filecoin-project/go-state-types/exitcode" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/state" @@ -38,217 +41,441 @@ func decodePayload(payload []byte, codec uint64) (ethtypes.EthBytes, error) { return nil, xerrors.Errorf("decodePayload: unsupported codec: %d", codec) } -// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls -func buildTraces(traces *[]*ethtypes.EthTrace, parent *ethtypes.EthTrace, addr []int, et types.ExecutionTrace, height int64, st *state.StateTree) 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(et.Msg.From, st) +func decodeParams[P any, T interface { + *P + cbg.CBORUnmarshaler +}](msg *types.MessageTrace) (T, error) { + var params T = new(P) + switch msg.ParamsCodec { + case uint64(multicodec.DagCbor), uint64(multicodec.Cbor): + default: + return nil, xerrors.Errorf("Method called with unexpected codec %d", msg.ParamsCodec) + } + + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return nil, xerrors.Errorf("failed to decode params: %w", err) + } + + return params, nil +} + +func find[T any](values []T, cb func(t *T) *T) *T { + for i := range values { + if o := cb(&values[i]); o != nil { + return o + } + } + return nil +} + +type environment struct { + caller ethtypes.EthAddress + isEVM bool + subtraceCount int + traces []*ethtypes.EthTrace + lastByteCode *ethtypes.EthAddress +} + +func baseEnvironment(st *state.StateTree, from address.Address) (*environment, error) { + sender, err := lookupEthAddress(from, st) if err != nil { - log.Warnf("buildTraces: failed to lookup from address %s: %v", et.Msg.From, err) - return nil + return nil, xerrors.Errorf("top-level message sender %s s could not be found: %w", from, err) } - to, err := lookupEthAddress(et.Msg.To, st) + return &environment{caller: sender}, nil +} + +func traceToAddress(act *types.ActorTrace) ethtypes.EthAddress { + if act.State.Address != nil { + if addr, err := ethtypes.EthAddressFromFilecoinAddress(*act.State.Address); err == nil { + return addr + } + } + return ethtypes.EthAddressFromActorID(act.Id) +} + +// buildTraces recursively builds the traces for a given ExecutionTrace by walking the subcalls +func buildTraces(env *environment, addr []int, et *types.ExecutionTrace) error { + trace, recurseInto, err := buildTrace(env, addr, et) if err != nil { - log.Warnf("buildTraces: failed to lookup to address %s: %w", et.Msg.To, err) + return xerrors.Errorf("at trace %v: %w", addr, err) + } + + if trace != nil { + env.traces = append(env.traces, trace) + env.subtraceCount++ + } + + // Skip if there's nothing more to do and/or `buildTrace` told us to skip this one. + if recurseInto == nil || recurseInto.InvokedActor == nil || len(recurseInto.Subcalls) == 0 { return nil } - // Skip the trace if we never reached the point where we invoked this actor. + subEnv := &environment{ + caller: traceToAddress(recurseInto.InvokedActor), + isEVM: builtinactors.IsEvmActor(recurseInto.InvokedActor.State.Code), + traces: env.traces, + } + addr = addr[:len(addr):len(addr)] + for i := range recurseInto.Subcalls { + err := buildTraces(subEnv, append(addr, subEnv.subtraceCount), &recurseInto.Subcalls[i]) + if err != nil { + return err + } + } + trace.Subtraces = subEnv.subtraceCount + env.traces = subEnv.traces + + return nil +} + +func decodeCreate(msg *types.MessageTrace) (callType string, initcode []byte, err error) { + switch msg.Method { + case builtin.MethodsEAM.Create: + params, err := decodeParams[eam.CreateParams](msg) + if err != nil { + return "", nil, err + } + return "create", params.Initcode, nil + case builtin.MethodsEAM.Create2: + params, err := decodeParams[eam.Create2Params](msg) + if err != nil { + return "", nil, err + } + return "create2", params.Initcode, nil + case builtin.MethodsEAM.CreateExternal: + input, err := decodePayload(msg.Params, msg.ParamsCodec) + if err != nil { + return "", nil, err + } + return "create", input, nil + default: + return "", nil, xerrors.Errorf("unexpected CREATE method %d", msg.Method) + } +} + +// buildTrace processes the passed execution trace and updates the environment, if necessary. +// +// On success, it returns a trace to add (or nil to skip) and the trace recurse into (or nil to skip). +func buildTrace(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) { + // This function first assumes that the call is a "native" call, then handles all the "not + // native" cases. If we get any unexpected results in any of these special cases, we just + // keep the "native" interpretation and move on. + // + // 1. If we're invoking a contract (even if the caller is a native account/actor), we + // attempt to decode the params/return value as a contract invocation. + // 2. If we're calling the EAM and/or init actor, we try to treat the call as a CREATE. + // 3. Finally, if the caller is an EVM smart contract and it's calling a "private" (1-1023) + // method, we know something special is going on. We look for calls related to + // DELEGATECALL and drop everything else (everything else includes calls triggered by, + // e.g., EXTCODEHASH). + + // If we don't have sufficient funds, or we have a fatal error, or we have some + // other syscall error: skip the entire trace to mimic Ethereum (Ethereum records + // traces _after_ checking things like this). + // + // NOTE: The FFI currently folds all unknown syscall errors into "sys assertion + // failed" which is turned into SysErrFatal. + if len(addr) > 0 { + switch et.MsgRct.ExitCode { + case exitcode.SysErrInsufficientFunds, exitcode.SysErrFatal: + return nil, nil, nil + } + } + + // We may fail before we can even invoke the actor. In that case, we have no 100% reliable + // way of getting its address (e.g., due to reverts) so we're just going to drop the entire + // trace. This is OK (ish) because the call never really "happened". if et.InvokedActor == nil { - return nil + return nil, nil, nil } + ///////////////////////////////////// + // Step 1: Decode as a native call // + ///////////////////////////////////// + + to := traceToAddress(et.InvokedActor) + var errMsg string + if et.MsgRct.ExitCode.IsError() { + errMsg = et.MsgRct.ExitCode.Error() + } trace := ðtypes.EthTrace{ Action: ethtypes.EthTraceAction{ - From: from, - To: to, + From: env.caller, + To: &to, Gas: ethtypes.EthUint64(et.Msg.GasLimit), - Input: nil, Value: ethtypes.EthBigInt(et.Msg.Value), - - FilecoinFrom: et.Msg.From, - FilecoinTo: et.Msg.To, - FilecoinMethod: et.Msg.Method, - FilecoinCodeCid: et.InvokedActor.State.Code, + Input: encodeFilecoinParamsAsABI(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params), }, Result: ethtypes.EthTraceResult{ GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas), - Output: nil, + Output: encodeFilecoinReturnAsABI(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return), }, - Subtraces: 0, // will be updated by the children once they are added to the trace TraceAddress: addr, + Error: errMsg, + } - Parent: parent, - LastByteCode: nil, + // Set the assumed call mode. We'll override this if the call ends up looking like a create, + // delegatecall, etc. + if et.Msg.ReadOnly { + trace.SetCallType("staticcall") + } else { + trace.SetCallType("call") } - trace.SetCallType("call") + ///////////////////////////////////////////// + // Step 2: Decode as a contract invocation // + ///////////////////////////////////////////// + // Normal EVM calls. We don't care if the caller/receiver are actually EVM actors, we only + // care if the call _looks_ like an EVM call. If we fail to decode it as an EVM call, we + // fallback on interpreting it as a native call. if et.Msg.Method == builtin.MethodsEVM.InvokeContract { - log.Debugf("COND1 found InvokeContract call at height: %d", height) - - // TODO: ignore return errors since actors can send gibberish and we don't want - // to fail the whole trace in that case - 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) + input, err := decodePayload(et.Msg.Params, et.Msg.ParamsCodec) if err != nil { - return xerrors.Errorf("buildTraces: %w", err) + log.Debugf("failed to decode contract invocation payload: %w", err) + return trace, et, nil } - } else if et.Msg.To == builtin.EthereumAddressManagerActorAddr && - et.Msg.Method == builtin.MethodsEAM.CreateExternal { - log.Debugf("COND2 found CreateExternal call at height: %d", height) - trace.Action.Input, err = decodePayload(et.Msg.Params, et.Msg.ParamsCodec) + output, err := decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) if err != nil { - return xerrors.Errorf("buildTraces: %w", err) + log.Debugf("failed to decode contract invocation return: %w", err) + return trace, et, nil } + trace.Action.Input = input + trace.Result.Output = output + return trace, et, nil + } - 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) + ///////////////////////////////////////////// + // Step 3: Decode as a contract deployment // + ///////////////////////////////////////////// + + switch et.Msg.To { + // NOTE: this will only catch _direct_ calls to the init actor. Calls through the EAM will + // be caught and _skipped_ below in the next case. + case builtin.InitActorAddr: + switch et.Msg.Method { + case builtin.MethodsInit.Exec, builtin.MethodsInit.Exec4: + if et.Msg.ReadOnly { + // "create" isn't valid in a staticcall, so we just skip this trace + // (couldn't have created an actor anyways). + // This mimic's the EVM: it doesn't trace CREATE calls when in + // read-only mode. + return nil, nil, nil } - } - // treat this as a contract creation - trace.SetCallType("create") - } 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 = encodeFilecoinParamsAsABI(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params) - trace.Result.Output = encodeFilecoinReturnAsABI(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return) - } - - // TODO: is it OK to check this here or is this only specific to certain edge case (evm to evm)? - if et.Msg.ReadOnly { - trace.SetCallType("staticcall") - } + subTrace := find(et.Subcalls, func(c *types.ExecutionTrace) *types.ExecutionTrace { + if c.Msg.Method == builtin.MethodConstructor { + return c + } + return nil + }) + if subTrace == nil { + // If we succeed in calling Exec/Exec4 but don't even try to construct + // something, we have a bug in our tracing logic or a mismatch between our + // tracing logic and the actors. + if et.MsgRct.ExitCode.IsSuccess() { + return nil, nil, xerrors.Errorf("successful Exec/Exec4 call failed to call a constructor") + } + // Otherwise, this can happen if creation fails early (bad params, + // out of gas, contract already exists, etc.). The EVM wouldn't + // trace such cases, so we don't either. + // + // NOTE: It's actually impossible to run out of gas before calling + // initcode in the EVM (without running out of gas in the calling + // contract), but this is an equivalent edge-case to InvokedActor + // being nil, so we treat it the same way and skip the entire + // operation. + return nil, nil, nil + } - // 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 { - // 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.FilecoinTo == builtin.InitActorAddr && - parent.Action.FilecoinMethod == builtin.MethodsInit.Exec && - et.Msg.Method == builtin.MethodConstructor { - log.Debugf("COND3 Native actor creation! method:%d, code:%s, height:%d", et.Msg.Method, et.InvokedActor.State.Code.String(), height) - parent.SetCallType("create") - parent.Action.To = to - parent.Action.Input = []byte{0xFE} - parent.Result.Output = nil - - // there should never be any subcalls when creating a native actor - // - // TODO: add support for native actors calling another when created - return nil - } + // Native actors that aren't the EAM can attempt to call Exec4, but such + // call should fail immediately without ever attempting to construct an + // actor. I'm catching this here because it likely means that there's a bug + // in our trace-conversion logic. + if et.Msg.Method == builtin.MethodsInit.Exec4 { + return nil, nil, xerrors.Errorf("direct call to Exec4 successfully called a constructor!") + } - // Handle EVM contract creation - // - // To detect EVM contract creation we need to check for the following sequence of events: - // - // 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.FilecoinTo == builtin.EthereumAddressManagerActorAddr && - (parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create || parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create2) - eamCalledInitOnExec4 := parent.Action.FilecoinTo == builtin.InitActorAddr && - parent.Action.FilecoinMethod == builtin.MethodsInit.Exec4 - 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 { - log.Debugf("COND4 EVM contract creation method:%d, code:%s, height:%d", et.Msg.Method, et.InvokedActor.State.Code.String(), height) - - if parent.Parent.Action.FilecoinMethod == builtin.MethodsEAM.Create { - parent.Parent.SetCallType("create") + // Contract creation has no "to". + trace.Action.To = nil + // If we get here, this isn't a native EVM create. Those always go through + // the EAM. So we have no "real" initcode and must use the sentinel value + // for "invalid" initcode. + trace.Action.Input = []byte{0xFE} + trace.SetCallType("create") + // Handle the output. + if et.MsgRct.ExitCode.IsError() { + // If the sub-call fails, record the reason. It's possible for the + // call to the init actor to fail, but for the construction to + // succeed, so we need to check the exit code here. + if subTrace.MsgRct.ExitCode.IsError() { + trace.Result.Output = encodeFilecoinReturnAsABI( + subTrace.MsgRct.ExitCode, + subTrace.MsgRct.ReturnCodec, + subTrace.MsgRct.Return, + ) } else { - parent.Parent.SetCallType("create2") + // Otherwise, output nothing. + trace.Result.Output = nil } + } else { + // We're supposed to put the "installed bytecode" here. But this + // isn't an EVM actor, so we just put some invalid bytecode (this is + // the answer you'd get if you called EXTCODECOPY on a native + // non-account actor, anyways). + trace.Result.Output = []byte{0xFE} + } + return trace, subTrace, nil + } + case builtin.EthereumAddressManagerActorAddr: + switch et.Msg.Method { + case builtin.MethodsEAM.Create, builtin.MethodsEAM.Create2, builtin.MethodsEAM.CreateExternal: + // Same as the Init actor case above, see the comment there. + if et.Msg.ReadOnly { + return nil, nil, nil + } - // 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] - + // Look for a call to either a constructor or the EVM's resurrect method. + subTrace := find(et.Subcalls, func(et *types.ExecutionTrace) *types.ExecutionTrace { + if et.Msg.To == builtinactors.InitActorAddr { + return find(et.Subcalls, func(et *types.ExecutionTrace) *types.ExecutionTrace { + if et.Msg.Method == builtinactors.MethodConstructor { + return et + } + return nil + }) + } + if et.Msg.Method == builtin.MethodsEVM.Resurrect { + return et + } return nil + }) + + // Same as the Init actor case above, see the comment there. + if subTrace == nil { + if et.MsgRct.ExitCode.IsSuccess() { + return nil, nil, xerrors.Errorf("successful Create/Create2 call failed to call a constructor") + } + return nil, nil, nil } - } - if builtinactors.IsEvmActor(parent.Action.FilecoinCodeCid) { - // 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 - - var dp evm.DelegateCallParams - err := dp.UnmarshalCBOR(bytes.NewReader(et.Msg.Params)) - if err != nil { - return xerrors.Errorf("failed UnmarshalCBOR: %w", err) - } - trace.Action.Input = dp.Input + // Contract creation has no "to". + trace.Action.To = nil - trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) + // Decode inputs & determine create type. + method, initcode, err := decodeCreate(&et.Msg) + if err != nil { + return nil, nil, xerrors.Errorf("EAM called with invalid params, but it still tried to construct the contract: %w", err) + } + trace.Action.Input = initcode + trace.SetCallType(method) + + // Handle the output. + if et.MsgRct.ExitCode.IsError() { + if subTrace.MsgRct.ExitCode.IsError() { + // If we managed to call the constructor, parse/return its + // revert message. + output, err := decodePayload(subTrace.MsgRct.Return, subTrace.MsgRct.ReturnCodec) if err != nil { - return xerrors.Errorf("failed decodePayload: %w", err) + log.Debugf("EVM actor returned indecipherable create error: %w", err) + output = encodeFilecoinReturnAsABI( + subTrace.MsgRct.ExitCode, + subTrace.MsgRct.ReturnCodec, + subTrace.MsgRct.Return, + ) } + trace.Result.Output = output + } else { + // Otherwise, if we failed before that, we have no revert + // message. + trace.Result.Output = nil } } else { - // 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 et.Msg.Method > 0 && - et.Msg.Method <= 1023 { - log.Debugf("Infof 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) - - if et.Msg.Method == builtin.MethodsEVM.GetBytecode { - // save the last bytecode trace to handle delegate calls - parent.LastByteCode = trace - } - - return nil - } + // We're _supposed_ to include the contracts bytecode here, but we + // can't do that reliably (e.g., if some part of the trace reverts). + // So we don't try and include a sentinel "impossible bytecode" + // value (the value specified by EIP-3541). + trace.Result.Output = []byte{0xFE} } + return trace, subTrace, nil } - } - // we are adding trace to the traces so update the parent subtraces count as it was originally set to zero - if parent != nil { - parent.Subtraces++ - } + ///////////////////////////////// + // Step 4: Handle DELEGATECALL // + ///////////////////////////////// - *traces = append(*traces, trace) + // EVM contracts cannot call methods in the range 1-1023, only the EVM itself can. So, if we + // see a call in this range, we know it's an implementation detail of the EVM and not an + // explicit call by the user. + // + // While the EVM calls several methods in this range (some we've already handled above with + // respect to the EAM), we only care about the ones relevant DELEGATECALL and can _ignore_ + // all the others. + if env.isEVM && et.Msg.Method > 0 && et.Msg.Method < 1024 { + // The EVM actor implements DELEGATECALL by: + // + // 1. Asking the callee for its bytecode by calling it on the GetBytecode method. + // 2. Recursively invoking the currently executing contract on the + // InvokeContractDelegate method. + // + // The code below "reconstructs" that delegate call by: + // + // 1. Remembering the last contract on which we called GetBytecode. + // 2. Treating the contract invoked in step 1 as the DELEGATECALL receiver. + // + // Note, however: GetBytecode will be called, e.g., if the user invokes the + // EXTCODECOPY instruction. It's not an error to see multiple GetBytecode calls + // before we see an InvokeContractDelegate. + switch et.Msg.Method { + case builtin.MethodsEVM.GetBytecode: + // NOTE: I'm not checking anything about the receiver here. The EVM won't + // DELEGATECALL any non-EVM actor, but there's no need to encode that fact + // here in case we decide to loosen this up in the future. + if et.MsgRct.ExitCode.IsSuccess() { + env.lastByteCode = &to + } else { + env.lastByteCode = nil + } + return nil, nil, nil + case builtin.MethodsEVM.InvokeContractDelegate: + // NOTE: We return errors in all the failure cases below instead of trying + // to continue because the caller is an EVM actor. If something goes wrong + // here, there's a bug in our EVM implementation. - for i, call := range et.Subcalls { - err := buildTraces(traces, trace, append(addr, i), call, height, st) - if err != nil { - return err + // 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 env.lastByteCode == nil { + return nil, nil, xerrors.Errorf("unknown bytecode for delegate call") + } + if env.caller != to { + return nil, nil, xerrors.Errorf("delegate-call not from & to self: %s != %s", env.caller, to) + } + + trace.SetCallType("delegatecall") + trace.Action.To = env.lastByteCode + + dp, err := decodeParams[evm.DelegateCallParams](&et.Msg) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode delegate-call params: %w", err) + } + trace.Action.Input = dp.Input + + trace.Result.Output, err = decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec) + if err != nil { + return nil, nil, xerrors.Errorf("failed to decode delegate-call return: %w", err) + } + + return trace, et, nil } + // We drop all other "private" calls from FEVM. We _forbid_ explicit calls between 0 and 1024 (exclusive), so any calls in this range must be implementation details. + return nil, nil, nil } - return nil + return trace, et, nil } diff --git a/node/impl/full/eth_utils.go b/node/impl/full/eth_utils.go index 90e6d054a9c..6186c2645f9 100644 --- a/node/impl/full/eth_utils.go +++ b/node/impl/full/eth_utils.go @@ -382,34 +382,35 @@ func parseEthRevert(ret []byte) string { // 3. Otherwise, we fall back to returning a masked ID Ethereum address. If the supplied address is an f0 address, we // use that ID to form the masked ID address. // 4. Otherwise, we fetch the actor's ID from the state tree and form the masked ID with it. +// +// If the actor doesn't exist in the state-tree but we have its ID, we use a masked ID address. It could have been deleted. func lookupEthAddress(addr address.Address, st *state.StateTree) (ethtypes.EthAddress, error) { - // BLOCK A: We are trying to get an actual Ethereum address from an f410 address. // Attempt to convert directly, if it's an f4 address. ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr) if err == nil && !ethAddr.IsMaskedID() { return ethAddr, nil } - // Lookup on the target actor and try to get an f410 address. - if actor, err := st.GetActor(addr); err != nil { + // Otherwise, resolve the ID addr. + idAddr, err := st.LookupID(addr) + if err != nil { return ethtypes.EthAddress{}, err - } else if actor.Address != nil { - if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() { - return ethAddr, nil - } } - // BLOCK B: We gave up on getting an actual Ethereum address and are falling back to a Masked ID address. - // Check if we already have an ID addr, and use it if possible. - if err == nil && ethAddr.IsMaskedID() { + // Lookup on the target actor and try to get an f410 address. + if actor, err := st.GetActor(idAddr); errors.Is(err, types.ErrActorNotFound) { + // Not found -> use a masked ID address + } else if err != nil { + // Any other error -> fail. + return ethtypes.EthAddress{}, err + } else if actor.Address == nil { + // No delegated address -> use masked ID address. + } else if ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(*actor.Address); err == nil && !ethAddr.IsMaskedID() { + // Conversable into an eth address, use it. return ethAddr, nil } - // Otherwise, resolve the ID addr. - idAddr, err := st.LookupID(addr) - if err != nil { - return ethtypes.EthAddress{}, err - } + // Otherwise, use the masked address. return ethtypes.EthAddressFromFilecoinAddress(idAddr) }