Improve the cli state call command to accept base64/hex params, and decode result according to method return type
This commit is contained in:
parent
d86c8195dd
commit
2782ea31d3
173
cli/state.go
173
cli/state.go
@ -3,6 +3,8 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -22,7 +24,6 @@ import (
|
|||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
cbor "github.com/ipfs/go-ipld-cbor"
|
cbor "github.com/ipfs/go-ipld-cbor"
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
|
||||||
"github.com/multiformats/go-multiaddr"
|
"github.com/multiformats/go-multiaddr"
|
||||||
"github.com/multiformats/go-multihash"
|
"github.com/multiformats/go-multihash"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -31,7 +32,6 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/big"
|
|
||||||
"github.com/filecoin-project/go-state-types/exitcode"
|
"github.com/filecoin-project/go-state-types/exitcode"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
@ -1521,7 +1521,7 @@ func printMsg(ctx context.Context, api v0api.FullNode, msg cid.Cid, mw *lapi.Msg
|
|||||||
var StateCallCmd = &cli.Command{
|
var StateCallCmd = &cli.Command{
|
||||||
Name: "call",
|
Name: "call",
|
||||||
Usage: "Invoke a method on an actor locally",
|
Usage: "Invoke a method on an actor locally",
|
||||||
ArgsUsage: "[toAddress methodId <param1 param2 ...> (optional)]",
|
ArgsUsage: "[toAddress methodId params (optional)]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "from",
|
Name: "from",
|
||||||
@ -1535,8 +1535,13 @@ var StateCallCmd = &cli.Command{
|
|||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "ret",
|
Name: "ret",
|
||||||
Usage: "specify how to parse output (auto, raw, addr, big)",
|
Usage: "specify how to parse output (raw, decoded, base64, hex)",
|
||||||
Value: "auto",
|
Value: "decoded",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "encoding",
|
||||||
|
Value: "base64",
|
||||||
|
Usage: "specify params encoding to parse (base64, hex)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: func(cctx *cli.Context) error {
|
Action: func(cctx *cli.Context) error {
|
||||||
@ -1577,14 +1582,23 @@ var StateCallCmd = &cli.Command{
|
|||||||
return fmt.Errorf("failed to parse 'value': %s", err)
|
return fmt.Errorf("failed to parse 'value': %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
act, err := api.StateGetActor(ctx, toa, ts.Key())
|
var params []byte
|
||||||
|
// If params were passed in, decode them
|
||||||
|
if cctx.Args().Len() > 2 {
|
||||||
|
switch cctx.String("encoding") {
|
||||||
|
case "base64":
|
||||||
|
params, err = base64.StdEncoding.DecodeString(cctx.Args().Get(2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to lookup target actor: %s", err)
|
return xerrors.Errorf("decoding base64 value: %w", err)
|
||||||
}
|
}
|
||||||
|
case "hex":
|
||||||
params, err := parseParamsForMethod(act.Code, method, cctx.Args().Slice()[2:])
|
params, err = hex.DecodeString(cctx.Args().Get(2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse params: %s", err)
|
return xerrors.Errorf("decoding hex value: %w", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return xerrors.Errorf("unrecognized encoding: %s", cctx.String("encoding"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := api.StateCall(ctx, &types.Message{
|
ret, err := api.StateCall(ctx, &types.Message{
|
||||||
@ -1595,137 +1609,42 @@ var StateCallCmd = &cli.Command{
|
|||||||
Params: params,
|
Params: params,
|
||||||
}, ts.Key())
|
}, ts.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("state call failed: %s", err)
|
return fmt.Errorf("state call failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ret.MsgRct.ExitCode != 0 {
|
if ret.MsgRct.ExitCode != 0 {
|
||||||
return fmt.Errorf("invocation failed (exit: %d, gasUsed: %d): %s", ret.MsgRct.ExitCode, ret.MsgRct.GasUsed, ret.Error)
|
return fmt.Errorf("invocation failed (exit: %d, gasUsed: %d): %s", ret.MsgRct.ExitCode, ret.MsgRct.GasUsed, ret.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := formatOutput(cctx.String("ret"), ret.MsgRct.Return)
|
fmt.Println("Call receipt:")
|
||||||
|
fmt.Printf("Exit code: %d\n", ret.MsgRct.ExitCode)
|
||||||
|
fmt.Printf("Gas Used: %d\n", ret.MsgRct.GasUsed)
|
||||||
|
|
||||||
|
switch cctx.String("ret") {
|
||||||
|
case "decoded":
|
||||||
|
act, err := api.StateGetActor(ctx, toa, ts.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to format output: %s", err)
|
return xerrors.Errorf("getting actor: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("gas used: %d\n", ret.MsgRct.GasUsed)
|
retStr, err := jsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return)
|
||||||
fmt.Printf("return: %s\n", s)
|
if err != nil {
|
||||||
|
return xerrors.Errorf("decoding return: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Return:\n%s\n", retStr)
|
||||||
|
case "raw":
|
||||||
|
fmt.Printf("Return: \n%s\n", ret.MsgRct.Return)
|
||||||
|
case "hex":
|
||||||
|
fmt.Printf("Return: \n%x\n", ret.MsgRct.Return)
|
||||||
|
case "base64":
|
||||||
|
fmt.Printf("Return: \n%s\n", base64.StdEncoding.EncodeToString(ret.MsgRct.Return))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatOutput(t string, val []byte) (string, error) {
|
|
||||||
switch t {
|
|
||||||
case "raw", "hex":
|
|
||||||
return fmt.Sprintf("%x", val), nil
|
|
||||||
case "address", "addr", "a":
|
|
||||||
a, err := address.NewFromBytes(val)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return a.String(), nil
|
|
||||||
case "big", "int", "bigint":
|
|
||||||
bi := types.BigFromBytes(val)
|
|
||||||
return bi.String(), nil
|
|
||||||
case "fil":
|
|
||||||
bi := types.FIL(types.BigFromBytes(val))
|
|
||||||
return bi.String(), nil
|
|
||||||
case "pid", "peerid", "peer":
|
|
||||||
pid, err := peer.IDFromBytes(val)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pid.Pretty(), nil
|
|
||||||
case "auto":
|
|
||||||
if len(val) == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
a, err := address.NewFromBytes(val)
|
|
||||||
if err == nil {
|
|
||||||
return "address: " + a.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pid, err := peer.IDFromBytes(val)
|
|
||||||
if err == nil {
|
|
||||||
return "peerID: " + pid.Pretty(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bi := types.BigFromBytes(val)
|
|
||||||
return "bigint: " + bi.String(), nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("unrecognized output type: %q", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, error) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: consider moving this to a dedicated helper
|
|
||||||
actMeta, ok := stmgr.MethodsMap[act]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown actor %s", act)
|
|
||||||
}
|
|
||||||
|
|
||||||
methodMeta, ok := actMeta[abi.MethodNum(method)]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown method %d for actor %s", method, act)
|
|
||||||
}
|
|
||||||
|
|
||||||
paramObj := methodMeta.Params.Elem()
|
|
||||||
if paramObj.NumField() != len(args) {
|
|
||||||
return nil, fmt.Errorf("not enough arguments given to call that method (expecting %d)", paramObj.NumField())
|
|
||||||
}
|
|
||||||
|
|
||||||
p := reflect.New(paramObj)
|
|
||||||
for i := 0; i < len(args); i++ {
|
|
||||||
switch paramObj.Field(i).Type {
|
|
||||||
case reflect.TypeOf(address.Address{}):
|
|
||||||
a, err := address.NewFromString(args[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse address: %s", err)
|
|
||||||
}
|
|
||||||
p.Elem().Field(i).Set(reflect.ValueOf(a))
|
|
||||||
case reflect.TypeOf(uint64(0)):
|
|
||||||
val, err := strconv.ParseUint(args[i], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p.Elem().Field(i).Set(reflect.ValueOf(val))
|
|
||||||
case reflect.TypeOf(abi.ChainEpoch(0)):
|
|
||||||
val, err := strconv.ParseInt(args[i], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p.Elem().Field(i).Set(reflect.ValueOf(abi.ChainEpoch(val)))
|
|
||||||
case reflect.TypeOf(big.Int{}):
|
|
||||||
val, err := big.FromString(args[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p.Elem().Field(i).Set(reflect.ValueOf(val))
|
|
||||||
case reflect.TypeOf(peer.ID("")):
|
|
||||||
pid, err := peer.Decode(args[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse peer ID: %s", err)
|
|
||||||
}
|
|
||||||
p.Elem().Field(i).Set(reflect.ValueOf(pid))
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported type for call (TODO): %s", paramObj.Field(i).Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m := p.Interface().(cbg.CBORMarshaler)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := m.MarshalCBOR(buf); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to marshal param object: %s", err)
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var StateCircSupplyCmd = &cli.Command{
|
var StateCircSupplyCmd = &cli.Command{
|
||||||
Name: "circulating-supply",
|
Name: "circulating-supply",
|
||||||
Usage: "Get the exact current circulating supply of Filecoin",
|
Usage: "Get the exact current circulating supply of Filecoin",
|
||||||
|
Loading…
Reference in New Issue
Block a user