lotus/cli/eth.go

227 lines
4.9 KiB
Go
Raw Normal View History

package cli
import (
"context"
"encoding/hex"
"fmt"
"os"
2022-12-16 18:03:27 +00:00
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
2022-12-16 18:03:27 +00:00
"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{
2022-12-19 18:19:52 +00:00
EthGetInfoCmd,
EthCallSimulateCmd,
EthGetContractAddress,
},
}
2022-12-19 18:19:52 +00:00
var EthGetInfoCmd = &cli.Command{
2022-12-19 18:05:11 +00:00
Name: "stat",
Usage: "Print eth/filecoin addrs and code cid",
Flags: []cli.Flag{
&cli.StringFlag{
2022-12-19 18:05:11 +00:00
Name: "filAddr",
Usage: "Filecoin address",
},
&cli.StringFlag{
2022-12-19 18:05:11 +00:00
Name: "ethAddr",
Usage: "Ethereum address",
},
},
Action: func(cctx *cli.Context) error {
2022-12-19 18:05:11 +00:00
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)
2022-12-19 18:05:11 +00:00
if filAddr != "" {
addr, err := address.NewFromString(filAddr)
if err != nil {
return err
}
eaddr, faddr, err = ethAddrFromFilecoinAddress(ctx, addr, api)
2022-12-16 18:03:27 +00:00
if err != nil {
return err
}
2022-12-19 18:05:11 +00:00
} else if ethAddr != "" {
2022-12-19 19:41:46 +00:00
eaddr, err = ethtypes.EthAddressFromHex(ethAddr)
if err != nil {
return err
}
faddr, err = eaddr.ToFilecoinAddress()
if err != nil {
return err
}
} else {
2022-12-19 18:05:11 +00:00
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 {
2022-12-19 18:19:52 +00:00
if cctx.NArg() != 3 {
return IncorrectNumArgs(cctx)
}
fromEthAddr, err := ethtypes.EthAddressFromHex(cctx.Args().Get(0))
if err != nil {
return err
}
2022-12-19 18:19:52 +00:00
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
},
}
2022-12-19 18:05:11 +00:00
var EthGetContractAddress = &cli.Command{
Name: "contract-address",
Usage: "Generate contract address from smart contract code",
ArgsUsage: "[senderEthAddr] [salt] [contractHexPath]",
Action: func(cctx *cli.Context) error {
if cctx.NArg() != 3 {
return IncorrectNumArgs(cctx)
}
sender, err := ethtypes.EthAddressFromHex(cctx.Args().Get(0))
if err != nil {
return err
}
salt, err := hex.DecodeString(cctx.Args().Get(1))
if err != nil {
return xerrors.Errorf("Could not decode salt: %w", err)
}
if len(salt) > 32 {
return xerrors.Errorf("Len of salt bytes greater than 32")
}
var fsalt [32]byte
copy(fsalt[:], salt[:])
contractBin := cctx.Args().Get(2)
if err != nil {
return err
}
contractHex, err := os.ReadFile(contractBin)
if err != nil {
return err
}
contract, err := hex.DecodeString(string(contractHex))
if err != nil {
return xerrors.Errorf("Could not decode contract file: %w", err)
}
contractAddr, err := ethtypes.GetContractEthAddressFromCode(sender, fsalt, contract)
if err != nil {
return err
}
fmt.Println("Contract Eth address: ", contractAddr)
return nil
},
}
2022-12-19 18:05:11 +00:00
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
}