diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index b5ab878ba..cb9535615 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -266,6 +266,8 @@ func (ea EthAddress) ToFilecoinAddress() (address.Address, error) { return addr, nil } +// This API assumes that if an ID address is passed in, it doesn't have an equivalent +// delegated address func TryEthAddressFromFilecoinAddress(addr address.Address, allowId bool) (EthAddress, bool, error) { switch addr.Protocol() { case address.ID: diff --git a/cli/cmd.go b/cli/cmd.go index 79023917b..6184d71ca 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -81,6 +81,7 @@ var Commands = []*cli.Command{ WithCategory("developer", LogCmd), WithCategory("developer", WaitApiCmd), WithCategory("developer", FetchParamCmd), + WithCategory("developer", EthCmd), WithCategory("network", NetCmd), WithCategory("network", SyncCmd), WithCategory("status", StatusCmd), diff --git a/cli/eth.go b/cli/eth.go new file mode 100644 index 000000000..e977e29f6 --- /dev/null +++ b/cli/eth.go @@ -0,0 +1,174 @@ +package cli + +import ( + "context" + "encoding/hex" + "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + + "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" +) + +var EthCmd = &cli.Command{ + Name: "eth", + Usage: "Query eth contract state", + Subcommands: []*cli.Command{ + EthGetInfoCmd, + EthCallSimulateCmd, + }, +} + +var EthGetInfoCmd = &cli.Command{ + Name: "stat", + Usage: "Print eth/filecoin addrs and code cid", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "filAddr", + Usage: "Filecoin address", + }, + &cli.StringFlag{ + Name: "ethAddr", + Usage: "Ethereum address", + }, + }, + Action: func(cctx *cli.Context) error { + + filAddr := cctx.String("filAddr") + ethAddr := cctx.String("ethAddr") + + var faddr address.Address + var eaddr ethtypes.EthAddress + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + if filAddr != "" { + addr, err := address.NewFromString(filAddr) + if err != nil { + return err + } + eaddr, faddr, err = ethAddrFromFilecoinAddress(ctx, addr, api) + if err != nil { + return err + } + } else if ethAddr != "" { + eaddr, err = ethtypes.EthAddressFromHex(ethAddr) + if err != nil { + return err + } + faddr, err = eaddr.ToFilecoinAddress() + if err != nil { + return err + } + } else { + return xerrors.Errorf("Neither filAddr nor ethAddr specified") + } + + actor, err := api.StateGetActor(ctx, faddr, types.EmptyTSK) + if err != nil { + return err + } + + fmt.Println("Filecoin address: ", faddr) + fmt.Println("Eth address: ", eaddr) + fmt.Println("Code cid: ", actor.Code.String()) + + return nil + + }, +} + +var EthCallSimulateCmd = &cli.Command{ + Name: "call", + Usage: "Simulate an eth contract call", + ArgsUsage: "[from] [to] [params]", + Action: func(cctx *cli.Context) error { + + if cctx.NArg() != 3 { + return IncorrectNumArgs(cctx) + } + + fromEthAddr, err := ethtypes.EthAddressFromHex(cctx.Args().Get(0)) + if err != nil { + return err + } + + toEthAddr, err := ethtypes.EthAddressFromHex(cctx.Args().Get(1)) + if err != nil { + return err + } + + params, err := hex.DecodeString(cctx.Args().Get(2)) + if err != nil { + return err + } + + api, closer, err := GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + res, err := api.EthCall(ctx, ethtypes.EthCall{ + From: &fromEthAddr, + To: &toEthAddr, + Data: params, + }, "") + if err != nil { + fmt.Println("Eth call fails, return val: ", res) + return err + } + + fmt.Println("Result: ", res) + + return nil + + }, +} + +func ethAddrFromFilecoinAddress(ctx context.Context, addr address.Address, fnapi v0api.FullNode) (ethtypes.EthAddress, address.Address, error) { + var faddr address.Address + var err error + + switch addr.Protocol() { + case address.BLS, address.SECP256K1: + faddr, err = fnapi.StateLookupID(ctx, addr, types.EmptyTSK) + if err != nil { + return ethtypes.EthAddress{}, addr, err + } + case address.Actor, address.ID: + faddr, err = fnapi.StateLookupID(ctx, addr, types.EmptyTSK) + if err != nil { + return ethtypes.EthAddress{}, addr, err + } + fAct, err := fnapi.StateGetActor(ctx, faddr, types.EmptyTSK) + if err != nil { + return ethtypes.EthAddress{}, addr, err + } + if fAct.Address != nil && (*fAct.Address).Protocol() == address.Delegated { + faddr = *fAct.Address + } + case address.Delegated: + faddr = addr + default: + return ethtypes.EthAddress{}, addr, xerrors.Errorf("Filecoin address doesn't match known protocols") + } + + ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(faddr) + if err != nil { + return ethtypes.EthAddress{}, addr, err + } + + return ethAddr, faddr, nil +} diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 35bc11760..e5c27f9b2 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -31,6 +31,7 @@ COMMANDS: log Manage logging wait-api Wait for lotus api to come online fetch-params Fetch proving parameters + eth Query eth contract state NETWORK: net Manage P2P Network sync Inspect or interact with the chain syncer @@ -2569,6 +2570,51 @@ OPTIONS: ``` +## lotus eth +``` +NAME: + lotus eth - Query eth contract state + +USAGE: + lotus eth command [command options] [arguments...] + +COMMANDS: + stat Print eth/filecoin addrs and code cid + call Simulate an eth contract call + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus eth stat +``` +NAME: + lotus eth stat - Print eth/filecoin addrs and code cid + +USAGE: + lotus eth stat [command options] [arguments...] + +OPTIONS: + --ethAddr value Ethereum address + --filAddr value Filecoin address + +``` + +### lotus eth call +``` +NAME: + lotus eth call - Simulate an eth contract call + +USAGE: + lotus eth call [command options] [from] [to] [params] + +OPTIONS: + --help, -h show help (default: false) + +``` + ## lotus net ``` NAME: diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 13660c4b3..2f374641c 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -706,7 +706,7 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty return nil, xerrors.Errorf("CallWithGas failed: %w", err) } if res.MsgRct.ExitCode.IsError() { - return nil, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error) + return nil, xerrors.Errorf("message execution failed: exit %s, msg receipt: %s, reason: %s", res.MsgRct.ExitCode, res.MsgRct.Return, res.Error) } return res, nil }