-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
eth_trace.go
605 lines (548 loc) · 21.5 KB
/
eth_trace.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
package full
import (
"bytes"
"fmt"
"github.com/multiformats/go-multicodec"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin"
eam12 "github.com/filecoin-project/go-state-types/builtin/v12/eam"
evm12 "github.com/filecoin-project/go-state-types/builtin/v12/evm"
init12 "github.com/filecoin-project/go-state-types/builtin/v12/init"
"github.com/filecoin-project/go-state-types/exitcode"
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/evm"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/types"
"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) {
switch multicodec.Code(codec) {
case multicodec.Identity:
return 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)
}
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 decodeReturn[R any, T interface {
*R
cbg.CBORUnmarshaler
}](ret *types.ReturnTrace) (T, error) {
var retval T = new(R)
switch ret.ReturnCodec {
case uint64(multicodec.DagCbor), uint64(multicodec.Cbor):
default:
return nil, xerrors.Errorf("Method returned an unexpected codec %d", ret.ReturnCodec)
}
if err := retval.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil {
return nil, xerrors.Errorf("failed to decode return value: %w", err)
}
return retval, 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 {
return nil, xerrors.Errorf("top-level message sender %s s could not be found: %w", from, err)
}
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)
}
// traceIsEVMOrEAM returns true if the trace is a call to an EVM or EAM actor.
func traceIsEVMOrEAM(et *types.ExecutionTrace) bool {
if et.InvokedActor == nil {
return false
}
return builtinactors.IsEvmActor(et.InvokedActor.State.Code) ||
et.InvokedActor.Id != abi.ActorID(builtin.EthereumAddressManagerActorID)
}
func traceErrMsg(et *types.ExecutionTrace) string {
code := et.MsgRct.ExitCode
if code.IsSuccess() {
return ""
}
// EVM tools often expect this literal string.
if code == exitcode.SysErrOutOfGas {
return "out of gas"
}
// indicate when we have a "system" error.
if code < exitcode.FirstActorErrorCode {
return fmt.Sprintf("vm error: %s", code)
}
// handle special exit codes from the EVM/EAM.
if traceIsEVMOrEAM(et) {
switch code {
case evm.ErrReverted:
return "Reverted" // capitalized for compatibility
case evm.ErrInvalidInstruction:
return "invalid instruction"
case evm.ErrUndefinedInstruction:
return "undefined instruction"
case evm.ErrStackUnderflow:
return "stack underflow"
case evm.ErrStackOverflow:
return "stack overflow"
case evm.ErrIllegalMemoryAccess:
return "illegal memory access"
case evm.ErrBadJumpdest:
return "invalid jump destination"
case evm.ErrSelfdestructFailed:
return "self destruct failed"
}
}
// everything else...
return fmt.Sprintf("actor error: %s", code.Error())
}
// 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 {
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
}
subEnv := &environment{
caller: traceToAddress(recurseInto.InvokedActor),
isEVM: builtinactors.IsEvmActor(recurseInto.InvokedActor.State.Code),
traces: env.traces,
}
// Set capacity to the length so each `append` below creates a new slice. Otherwise, we'll
// end up repeatedly mutating previous paths.
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
}
// 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, nil, nil
}
// 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 {
return traceEVMCall(env, addr, et)
}
// 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:
return traceNativeCreate(env, addr, et)
}
case builtin.EthereumAddressManagerActorAddr:
switch et.Msg.Method {
case builtin.MethodsEAM.Create, builtin.MethodsEAM.Create2, builtin.MethodsEAM.CreateExternal:
return traceEthCreate(env, addr, et)
}
}
// Step 4: Handle DELEGATECALL
//
// 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 {
return traceEVMPrivate(env, addr, et)
}
return traceNativeCall(env, addr, et), et, nil
}
// Build an EthTrace for a "call" with the given input & output.
func traceCall(env *environment, addr []int, et *types.ExecutionTrace, input, output ethtypes.EthBytes) *ethtypes.EthTrace {
to := traceToAddress(et.InvokedActor)
callType := "call"
if et.Msg.ReadOnly {
callType = "staticcall"
}
return ðtypes.EthTrace{
Type: "call",
Action: ðtypes.EthCallTraceAction{
CallType: callType,
From: env.caller,
To: to,
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
Value: ethtypes.EthBigInt(et.Msg.Value),
Input: input,
},
Result: ðtypes.EthCallTraceResult{
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
Output: output,
},
TraceAddress: addr,
Error: traceErrMsg(et),
}
}
// Build an EthTrace for a "call", parsing the inputs & outputs as a "native" FVM call.
func traceNativeCall(env *environment, addr []int, et *types.ExecutionTrace) *ethtypes.EthTrace {
return traceCall(env, addr, et,
encodeFilecoinParamsAsABI(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params),
encodeFilecoinReturnAsABI(et.MsgRct.ExitCode, et.MsgRct.ReturnCodec, et.MsgRct.Return),
)
}
// Build an EthTrace for a "call", parsing the inputs & outputs as an EVM call (falling back on
// treating it as a native call).
func traceEVMCall(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) {
input, err := decodePayload(et.Msg.Params, et.Msg.ParamsCodec)
if err != nil {
log.Debugf("failed to decode contract invocation payload: %w", err)
return traceNativeCall(env, addr, et), et, nil
}
output, err := decodePayload(et.MsgRct.Return, et.MsgRct.ReturnCodec)
if err != nil {
log.Debugf("failed to decode contract invocation return: %w", err)
return traceNativeCall(env, addr, et), et, nil
}
return traceCall(env, addr, et, input, output), et, nil
}
// Build an EthTrace for a native "create" operation. This should only be called with an
// ExecutionTrace is an Exec or Exec4 method invocation on the Init actor.
func traceNativeCreate(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) {
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
}
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
}
// 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!")
}
var output ethtypes.EthBytes
var createdAddr *ethtypes.EthAddress
if et.MsgRct.ExitCode.IsSuccess() {
// 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).
output = []byte{0xFE}
// Extract the address of the created actor from the return value.
initReturn, err := decodeReturn[init12.ExecReturn](&et.MsgRct)
if err != nil {
return nil, nil, xerrors.Errorf("failed to decode init params after a successful Init.Exec call: %w", err)
}
actorId, err := address.IDFromAddress(initReturn.IDAddress)
if err != nil {
return nil, nil, xerrors.Errorf("failed to extract created actor ID from address: %w", err)
}
ethAddr := ethtypes.EthAddressFromActorID(abi.ActorID(actorId))
createdAddr = ðAddr
}
return ðtypes.EthTrace{
Type: "create",
Action: ðtypes.EthCreateTraceAction{
From: env.caller,
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
Value: ethtypes.EthBigInt(et.Msg.Value),
// 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.
Init: []byte{0xFE},
},
Result: ðtypes.EthCreateTraceResult{
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
Address: createdAddr,
Code: output,
},
TraceAddress: addr,
Error: traceErrMsg(et),
}, subTrace, nil
}
// Assert that these are all identical so we can simplify the below code and decode once.
var _ *eam12.Return = (*eam12.Return)((*eam12.CreateReturn)(nil))
var _ *eam12.Return = (*eam12.Return)((*eam12.Create2Return)(nil))
var _ *eam12.Return = (*eam12.Return)((*eam12.CreateExternalReturn)(nil))
// Decode the parameters and return value of an EVM smart contract creation through the EAM. This
// should only be called with an ExecutionTrace for a Create, Create2, or CreateExternal method
// invocation on the EAM.
func decodeCreateViaEAM(et *types.ExecutionTrace) (initcode []byte, addr *ethtypes.EthAddress, err error) {
switch et.Msg.Method {
case builtin.MethodsEAM.Create:
params, err := decodeParams[eam12.CreateParams](&et.Msg)
if err != nil {
return nil, nil, err
}
initcode = params.Initcode
case builtin.MethodsEAM.Create2:
params, err := decodeParams[eam12.Create2Params](&et.Msg)
if err != nil {
return nil, nil, err
}
initcode = params.Initcode
case builtin.MethodsEAM.CreateExternal:
input, err := decodePayload(et.Msg.Params, et.Msg.ParamsCodec)
if err != nil {
return nil, nil, err
}
initcode = input
default:
return nil, nil, xerrors.Errorf("unexpected CREATE method %d", et.Msg.Method)
}
ret, err := decodeReturn[eam12.CreateReturn](&et.MsgRct)
if err != nil {
return nil, (*ethtypes.EthAddress)(&ret.EthAddress), err
}
return initcode, (*ethtypes.EthAddress)(&ret.EthAddress), nil
}
// Build an EthTrace for an EVM "create" operation. This should only be called with an
// ExecutionTrace for a Create, Create2, or CreateExternal method invocation on the EAM.
func traceEthCreate(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) {
// Same as the Init actor case above, see the comment there.
if et.Msg.ReadOnly {
return nil, nil, nil
}
// 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
}
// Decode inputs & determine create type.
initcode, createdAddr, err := decodeCreateViaEAM(et)
if err != nil {
return nil, nil, xerrors.Errorf("EAM called with invalid params or returned an invalid result, but it still tried to construct the contract: %w", err)
}
var output ethtypes.EthBytes
// Handle the output.
switch et.MsgRct.ExitCode {
case 0: // success
// 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).
output = []byte{0xFE}
case 33: // Reverted, parse the revert message.
// If we managed to call the constructor, parse/return its revert message. If we
// fail, we just return no output.
output, _ = decodePayload(subTrace.MsgRct.Return, subTrace.MsgRct.ReturnCodec)
}
return ðtypes.EthTrace{
Type: "create",
Action: ðtypes.EthCreateTraceAction{
From: env.caller,
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
Value: ethtypes.EthBigInt(et.Msg.Value),
Init: initcode,
},
Result: ðtypes.EthCreateTraceResult{
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
Address: createdAddr,
Code: output,
},
TraceAddress: addr,
Error: traceErrMsg(et),
}, subTrace, nil
}
// Build an EthTrace for a "private" method invocation from the EVM. This should only be called with
// an ExecutionTrace from an EVM instance and on a method between 1 and 1023 inclusive.
func traceEVMPrivate(env *environment, addr []int, et *types.ExecutionTrace) (*ethtypes.EthTrace, *types.ExecutionTrace, error) {
// 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() {
to := traceToAddress(et.InvokedActor)
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.
// 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 to := traceToAddress(et.InvokedActor); env.caller != to {
return nil, nil, xerrors.Errorf("delegate-call not from & to self: %s != %s", env.caller, to)
}
dp, err := decodeParams[evm12.DelegateCallParams](&et.Msg)
if err != nil {
return nil, nil, xerrors.Errorf("failed to decode delegate-call params: %w", err)
}
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 ðtypes.EthTrace{
Type: "call",
Action: ðtypes.EthCallTraceAction{
CallType: "delegatecall",
From: env.caller,
To: *env.lastByteCode,
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
Value: ethtypes.EthBigInt(et.Msg.Value),
Input: dp.Input,
},
Result: ðtypes.EthCallTraceResult{
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
Output: output,
},
TraceAddress: addr,
Error: traceErrMsg(et),
}, 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
}