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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
@ -22,7 +24,6 @@ import (
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/multiformats/go-multihash"
|
||||
"github.com/urfave/cli/v2"
|
||||
@ -31,7 +32,6 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"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/lotus/api"
|
||||
@ -1521,7 +1521,7 @@ func printMsg(ctx context.Context, api v0api.FullNode, msg cid.Cid, mw *lapi.Msg
|
||||
var StateCallCmd = &cli.Command{
|
||||
Name: "call",
|
||||
Usage: "Invoke a method on an actor locally",
|
||||
ArgsUsage: "[toAddress methodId <param1 param2 ...> (optional)]",
|
||||
ArgsUsage: "[toAddress methodId params (optional)]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
@ -1535,8 +1535,13 @@ var StateCallCmd = &cli.Command{
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ret",
|
||||
Usage: "specify how to parse output (auto, raw, addr, big)",
|
||||
Value: "auto",
|
||||
Usage: "specify how to parse output (raw, decoded, base64, hex)",
|
||||
Value: "decoded",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "encoding",
|
||||
Value: "base64",
|
||||
Usage: "specify params encoding to parse (base64, hex)",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
@ -1577,14 +1582,23 @@ var StateCallCmd = &cli.Command{
|
||||
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 {
|
||||
return fmt.Errorf("failed to lookup target actor: %s", err)
|
||||
return xerrors.Errorf("decoding base64 value: %w", err)
|
||||
}
|
||||
|
||||
params, err := parseParamsForMethod(act.Code, method, cctx.Args().Slice()[2:])
|
||||
case "hex":
|
||||
params, err = hex.DecodeString(cctx.Args().Get(2))
|
||||
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{
|
||||
@ -1595,137 +1609,42 @@ var StateCallCmd = &cli.Command{
|
||||
Params: params,
|
||||
}, ts.Key())
|
||||
if err != nil {
|
||||
return fmt.Errorf("state call failed: %s", err)
|
||||
return fmt.Errorf("state call failed: %w", err)
|
||||
}
|
||||
|
||||
if ret.MsgRct.ExitCode != 0 {
|
||||
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 {
|
||||
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)
|
||||
fmt.Printf("return: %s\n", s)
|
||||
retStr, err := jsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return)
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
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{
|
||||
Name: "circulating-supply",
|
||||
Usage: "Get the exact current circulating supply of Filecoin",
|
||||
|
Loading…
Reference in New Issue
Block a user