2019-08-07 23:22:35 +00:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
2020-07-23 02:30:51 +00:00
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
2019-08-07 23:22:35 +00:00
|
|
|
"fmt"
|
2020-07-23 02:30:51 +00:00
|
|
|
"reflect"
|
2019-10-03 18:12:30 +00:00
|
|
|
|
2020-08-12 17:06:08 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
cbg "github.com/whyrusleeping/cbor-gen"
|
|
|
|
|
2019-12-19 20:13:17 +00:00
|
|
|
"github.com/filecoin-project/go-address"
|
2020-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
2020-08-12 17:06:08 +00:00
|
|
|
|
2020-07-23 02:30:51 +00:00
|
|
|
"github.com/filecoin-project/lotus/api"
|
|
|
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2019-08-07 23:22:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var sendCmd = &cli.Command{
|
2019-10-09 20:02:08 +00:00
|
|
|
Name: "send",
|
|
|
|
Usage: "Send funds between accounts",
|
2020-03-04 21:46:00 +00:00
|
|
|
ArgsUsage: "[targetAddress] [amount]",
|
2019-08-07 23:22:35 +00:00
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
2020-07-16 00:55:27 +00:00
|
|
|
Name: "from",
|
2020-01-21 19:02:51 +00:00
|
|
|
Usage: "optionally specify the account to send funds from",
|
2019-08-07 23:22:35 +00:00
|
|
|
},
|
2020-04-01 01:34:23 +00:00
|
|
|
&cli.StringFlag{
|
2020-08-06 21:08:42 +00:00
|
|
|
Name: "gas-premium",
|
2020-04-01 01:34:23 +00:00
|
|
|
Usage: "specify gas price to use in AttoFIL",
|
|
|
|
Value: "0",
|
|
|
|
},
|
2020-08-06 21:08:42 +00:00
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "gas-feecap",
|
|
|
|
Usage: "specify gas fee cap to use in AttoFIL",
|
|
|
|
Value: "0",
|
|
|
|
},
|
2020-07-24 00:23:48 +00:00
|
|
|
&cli.Int64Flag{
|
|
|
|
Name: "gas-limit",
|
|
|
|
Usage: "specify gas limit",
|
|
|
|
Value: 0,
|
|
|
|
},
|
2020-12-03 17:36:34 +00:00
|
|
|
&cli.Uint64Flag{
|
2020-04-01 20:26:14 +00:00
|
|
|
Name: "nonce",
|
|
|
|
Usage: "specify the nonce to use",
|
2020-12-03 17:36:34 +00:00
|
|
|
Value: 0,
|
2020-04-01 20:26:14 +00:00
|
|
|
},
|
2020-07-23 02:30:51 +00:00
|
|
|
&cli.Uint64Flag{
|
|
|
|
Name: "method",
|
|
|
|
Usage: "specify method to invoke",
|
|
|
|
Value: 0,
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "params-json",
|
|
|
|
Usage: "specify invocation parameters in json",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "params-hex",
|
|
|
|
Usage: "specify invocation parameters in hex",
|
|
|
|
},
|
2019-08-07 23:22:35 +00:00
|
|
|
},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
2020-07-23 19:31:28 +00:00
|
|
|
if cctx.Args().Len() != 2 {
|
|
|
|
return ShowHelp(cctx, fmt.Errorf("'send' expects two arguments, target and amount"))
|
|
|
|
}
|
|
|
|
|
2019-10-03 18:12:30 +00:00
|
|
|
api, closer, err := GetFullNodeAPI(cctx)
|
2019-08-07 23:22:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-10-03 18:12:30 +00:00
|
|
|
defer closer()
|
2019-08-07 23:22:35 +00:00
|
|
|
|
|
|
|
ctx := ReqContext(cctx)
|
|
|
|
|
|
|
|
toAddr, err := address.NewFromString(cctx.Args().Get(0))
|
|
|
|
if err != nil {
|
2020-07-23 19:31:28 +00:00
|
|
|
return ShowHelp(cctx, fmt.Errorf("failed to parse target address: %w", err))
|
2019-08-07 23:22:35 +00:00
|
|
|
}
|
|
|
|
|
2019-10-18 12:31:45 +00:00
|
|
|
val, err := types.ParseFIL(cctx.Args().Get(1))
|
2019-08-07 23:22:35 +00:00
|
|
|
if err != nil {
|
2020-07-23 19:31:28 +00:00
|
|
|
return ShowHelp(cctx, fmt.Errorf("failed to parse amount: %w", err))
|
2019-08-07 23:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var fromAddr address.Address
|
2020-07-16 00:55:27 +00:00
|
|
|
if from := cctx.String("from"); from == "" {
|
2019-08-07 23:22:35 +00:00
|
|
|
defaddr, err := api.WalletDefaultAddress(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fromAddr = defaddr
|
|
|
|
} else {
|
|
|
|
addr, err := address.NewFromString(from)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fromAddr = addr
|
|
|
|
}
|
|
|
|
|
2020-08-07 06:37:08 +00:00
|
|
|
gp, err := types.BigFromString(cctx.String("gas-premium"))
|
2020-04-01 01:34:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-06 21:08:42 +00:00
|
|
|
gfc, err := types.BigFromString(cctx.String("gas-feecap"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-01 01:34:23 +00:00
|
|
|
|
2020-07-23 02:30:51 +00:00
|
|
|
method := abi.MethodNum(cctx.Uint64("method"))
|
|
|
|
|
|
|
|
var params []byte
|
|
|
|
if cctx.IsSet("params-json") {
|
|
|
|
decparams, err := decodeTypedParams(ctx, api, toAddr, method, cctx.String("params-json"))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to decode json params: %w", err)
|
|
|
|
}
|
|
|
|
params = decparams
|
|
|
|
}
|
|
|
|
if cctx.IsSet("params-hex") {
|
|
|
|
if params != nil {
|
|
|
|
return fmt.Errorf("can only specify one of 'params-json' and 'params-hex'")
|
|
|
|
}
|
|
|
|
decparams, err := hex.DecodeString(cctx.String("params-hex"))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to decode hex params: %w", err)
|
|
|
|
}
|
|
|
|
params = decparams
|
|
|
|
}
|
|
|
|
|
2019-08-07 23:22:35 +00:00
|
|
|
msg := &types.Message{
|
2020-08-06 21:08:42 +00:00
|
|
|
From: fromAddr,
|
|
|
|
To: toAddr,
|
|
|
|
Value: types.BigInt(val),
|
|
|
|
GasPremium: gp,
|
|
|
|
GasFeeCap: gfc,
|
|
|
|
GasLimit: cctx.Int64("gas-limit"),
|
|
|
|
Method: method,
|
|
|
|
Params: params,
|
2019-08-07 23:22:35 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 17:36:34 +00:00
|
|
|
if cctx.IsSet("nonce") {
|
|
|
|
msg.Nonce = cctx.Uint64("nonce")
|
2020-04-01 20:26:14 +00:00
|
|
|
sm, err := api.WalletSignMessage(ctx, fromAddr, msg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = api.MpoolPush(ctx, sm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-23 02:57:11 +00:00
|
|
|
fmt.Println(sm.Cid())
|
2020-04-01 20:26:14 +00:00
|
|
|
} else {
|
2020-08-12 20:17:21 +00:00
|
|
|
sm, err := api.MpoolPushMessage(ctx, msg, nil)
|
2020-04-01 20:26:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-23 02:57:11 +00:00
|
|
|
fmt.Println(sm.Cid())
|
2019-08-07 23:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
2020-07-23 02:30:51 +00:00
|
|
|
|
|
|
|
func decodeTypedParams(ctx context.Context, fapi api.FullNode, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) {
|
|
|
|
act, err := fapi.StateGetActor(ctx, to, types.EmptyTSK)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-04 03:28:42 +00:00
|
|
|
methodMeta, found := stmgr.MethodsMap[act.Code][method]
|
|
|
|
if !found {
|
|
|
|
return nil, fmt.Errorf("method %d not found on actor %s", method, act.Code)
|
|
|
|
}
|
|
|
|
|
|
|
|
p := reflect.New(methodMeta.Params.Elem()).Interface().(cbg.CBORMarshaler)
|
2020-07-23 02:30:51 +00:00
|
|
|
|
|
|
|
if err := json.Unmarshal([]byte(paramstr), p); err != nil {
|
|
|
|
return nil, fmt.Errorf("unmarshaling input into params type: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
if err := p.MarshalCBOR(buf); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|