Small refactor and cleanup
This commit is contained in:
parent
fd69f8bbd8
commit
7f99d15071
@ -84,9 +84,8 @@ type Action struct {
|
|||||||
Input string `json:"input"`
|
Input string `json:"input"`
|
||||||
Value ethtypes.EthBigInt `json:"value"`
|
Value ethtypes.EthBigInt `json:"value"`
|
||||||
|
|
||||||
// TODO: remove these fields from json output
|
method abi.MethodNum `json:"-"`
|
||||||
Method abi.MethodNum `json:"method"`
|
codeCid cid.Cid `json:"-"`
|
||||||
CodeCid cid.Cid `json:"codeCid"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
@ -225,17 +224,17 @@ func (e *EthTrace) TraceReplayBlockTransactions(ctx context.Context, blkNum stri
|
|||||||
return allTraces, nil
|
return allTraces, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func write_padded[T any](w io.Writer, data T, size int) error {
|
func writePadded[T any](w io.Writer, data T, size int) error {
|
||||||
tmp := &bytes.Buffer{}
|
tmp := &bytes.Buffer{}
|
||||||
|
|
||||||
// first write data to tmp buffer to get the size
|
// first write data to tmp buffer to get the size
|
||||||
err := binary.Write(tmp, binary.BigEndian, data)
|
err := binary.Write(tmp, binary.BigEndian, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write_padded: failed writing tmp data to buffer: %w", err)
|
return fmt.Errorf("writePadded: failed writing tmp data to buffer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tmp.Len() > size {
|
if tmp.Len() > size {
|
||||||
return fmt.Errorf("write_padded: data is larger than size")
|
return fmt.Errorf("writePadded: data is larger than size")
|
||||||
}
|
}
|
||||||
|
|
||||||
// write tailing zeros to pad up to size
|
// write tailing zeros to pad up to size
|
||||||
@ -243,81 +242,19 @@ func write_padded[T any](w io.Writer, data T, size int) error {
|
|||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
err = binary.Write(w, binary.BigEndian, uint8(0))
|
err = binary.Write(w, binary.BigEndian, uint8(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write_padded: failed writing tailing zeros to buffer: %w", err)
|
return fmt.Errorf("writePadded: failed writing tailing zeros to buffer: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally write the actual value
|
// finally write the actual value
|
||||||
err = binary.Write(w, binary.BigEndian, tmp.Bytes())
|
err = binary.Write(w, binary.BigEndian, tmp.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("write_padded: failed writing data to buffer: %w", err)
|
return fmt.Errorf("writePadded: failed writing data to buffer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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
|
// 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 {
|
func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.ExecutionTrace, height int64) error {
|
||||||
trace := &Trace{
|
trace := &Trace{
|
||||||
@ -327,8 +264,8 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution
|
|||||||
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
|
Gas: ethtypes.EthUint64(et.Msg.GasLimit),
|
||||||
Input: hex.EncodeToString(et.Msg.Params),
|
Input: hex.EncodeToString(et.Msg.Params),
|
||||||
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: Result{
|
Result: Result{
|
||||||
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
|
GasUsed: ethtypes.EthUint64(et.SumGas().TotalGas),
|
||||||
@ -354,14 +291,16 @@ 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)
|
// 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
|
// then we need to format native calls in a way that makes sense to Ethereum tooling (convert
|
||||||
// the input & output to solidity ABI format).
|
// 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)
|
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)
|
input, err := handleFilecoinMethodInput(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trace.Action.Input = hex.EncodeToString(input)
|
trace.Action.Input = hex.EncodeToString(input)
|
||||||
output, err := handle_filecoin_method_output(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
|
||||||
}
|
}
|
||||||
@ -371,7 +310,9 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution
|
|||||||
// 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
|
||||||
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)
|
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()
|
||||||
@ -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).
|
// 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.
|
// 3) The init actor creates the target actor B then calls it on method 1.
|
||||||
if parent.parent != nil {
|
if parent.parent != nil {
|
||||||
calledCreateOnEAM := parent.parent.Action.To == builtin.EthereumAddressManagerActorAddr.String() && (parent.parent.Action.Method == builtin.MethodsEAM.Create ||
|
calledCreateOnEAM := parent.parent.Action.To == builtin.EthereumAddressManagerActorAddr.String() &&
|
||||||
parent.parent.Action.Method == builtin.MethodsEAM.Create2)
|
(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
|
eamCalledInitOnExec4 := parent.Action.To == builtin.InitActorAddr.String() &&
|
||||||
initCreatedActor := trace.Action.Method == builtin.MethodsInit.Constructor
|
parent.Action.method == builtin.MethodsInit.Exec4
|
||||||
|
initCreatedActor := trace.Action.method == builtin.MethodConstructor
|
||||||
|
|
||||||
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("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")
|
parent.parent.setCallType("create")
|
||||||
} else {
|
} else {
|
||||||
parent.parent.setCallType("create2")
|
parent.parent.setCallType("create2")
|
||||||
@ -419,22 +361,26 @@ 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
|
// Any outbound call from an EVM actor on methods 1-1023 are side-effects from EVM instructions
|
||||||
// and should be dropped from the trace.
|
// and should be dropped from the trace.
|
||||||
if builtinactors.IsEvmActor(parent.Action.CodeCid) && et.Msg.Method > 0 && et.Msg.Method <= 1023 {
|
if builtinactors.IsEvmActor(parent.Action.codeCid) &&
|
||||||
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)
|
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
|
// 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
|
// EVM -> EVM calls
|
||||||
//
|
//
|
||||||
// Check for normal EVM to EVM calls and decode the params and return values
|
// 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)
|
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)
|
input, err := handleFilecoinMethodInput(et.Msg.Method, et.Msg.ParamsCodec, et.Msg.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trace.Action.Input = hex.EncodeToString(input)
|
trace.Action.Input = hex.EncodeToString(input)
|
||||||
output, err := handle_filecoin_method_output(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
|
||||||
}
|
}
|
||||||
@ -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.
|
// 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)
|
// 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.
|
// 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)
|
log.Debugf("found delegate call, height: %d", height)
|
||||||
prev := (*traces)[len(*traces)-1]
|
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.setCallType("delegatecall")
|
||||||
trace.Action.To = prev.Action.To
|
trace.Action.To = prev.Action.To
|
||||||
}
|
}
|
||||||
@ -468,6 +416,68 @@ func buildTraces(traces *[]*Trace, parent *Trace, addr []int, et types.Execution
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleFilecoinMethodInput(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("handleFilecoinMethodInput: failed writing method selector: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range staticArgs {
|
||||||
|
err := writePadded(w, arg, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("handleFilecoinMethodInput: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = binary.Write(w, binary.BigEndian, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("handleFilecoinMethodInput: 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("handleFilecoinMethodInput: failed writing tailing zeros: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleFilecoinMethodOutput(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 := writePadded(w, v, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("handleFilecoinMethodOutput: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := binary.Write(w, binary.BigEndian, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("handleFilecoinMethodOutput: failed writing data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: refactor this to be shared code
|
// TODO: refactor this to be shared code
|
||||||
func (e *EthTrace) getTipsetByBlockNr(ctx context.Context, blkParam string, strict bool) (*types.TipSet, error) {
|
func (e *EthTrace) getTipsetByBlockNr(ctx context.Context, blkParam string, strict bool) (*types.TipSet, error) {
|
||||||
if blkParam == "earliest" {
|
if blkParam == "earliest" {
|
||||||
|
Loading…
Reference in New Issue
Block a user