Implement services skeleton
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
parent
b13428bf11
commit
9553b32d52
13
cli/cmd.go
13
cli/cmd.go
@ -207,6 +207,19 @@ func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error
|
||||
return client.NewFullNodeRPC(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetFullNodeServices(ctx *cli.Context) (ServicesAPI, error) {
|
||||
if tn, ok := ctx.App.Metadata["test-services"]; ok {
|
||||
return tn.(ServicesAPI), nil
|
||||
}
|
||||
|
||||
api, c, err := GetFullNodeAPI(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ServicesImpl{api: api, closer: c}, nil
|
||||
}
|
||||
|
||||
type GetStorageMinerOptions struct {
|
||||
PreferHttp bool
|
||||
}
|
||||
|
118
cli/send.go
118
cli/send.go
@ -1,16 +1,12 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/urfave/cli/v2"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@ -18,7 +14,6 @@ import (
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@ -74,14 +69,14 @@ var sendCmd = &cli.Command{
|
||||
return ShowHelp(cctx, fmt.Errorf("'send' expects two arguments, target and amount"))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
srv, err := GetFullNodeServices(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
defer srv.Close()
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
var params sendParams
|
||||
var params SendParams
|
||||
|
||||
params.To, err = address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
@ -119,7 +114,7 @@ var sendCmd = &cli.Command{
|
||||
params.Method = abi.MethodNum(cctx.Uint64("method"))
|
||||
|
||||
if cctx.IsSet("params-json") {
|
||||
decparams, err := decodeTypedParams(ctx, api, params.To, params.Method, cctx.String("params-json"))
|
||||
decparams, err := srv.DecodeTypedParamsFromJSON(ctx, params.To, params.Method, cctx.String("params-json"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode json params: %w", err)
|
||||
}
|
||||
@ -135,6 +130,7 @@ var sendCmd = &cli.Command{
|
||||
}
|
||||
params.Params = decparams
|
||||
}
|
||||
|
||||
params.Force = cctx.Bool("force")
|
||||
|
||||
if cctx.IsSet("nonce") {
|
||||
@ -142,7 +138,7 @@ var sendCmd = &cli.Command{
|
||||
params.Nonce.N = cctx.Uint64("nonce")
|
||||
}
|
||||
|
||||
msgCid, err := send(ctx, api, params)
|
||||
msgCid, err := srv.Send(ctx, params)
|
||||
|
||||
if err != nil {
|
||||
return xerrors.Errorf("executing send: %w", err)
|
||||
@ -161,105 +157,3 @@ type sendAPIs interface {
|
||||
WalletDefaultAddress(context.Context) (address.Address, error)
|
||||
WalletSignMessage(context.Context, address.Address, *types.Message) (*types.SignedMessage, error)
|
||||
}
|
||||
|
||||
type sendParams struct {
|
||||
To address.Address
|
||||
From address.Address
|
||||
Val abi.TokenAmount
|
||||
|
||||
GasPremium abi.TokenAmount
|
||||
GasFeeCap abi.TokenAmount
|
||||
GasLimit int64
|
||||
|
||||
Nonce struct {
|
||||
N uint64
|
||||
Set bool
|
||||
}
|
||||
Method abi.MethodNum
|
||||
Params []byte
|
||||
|
||||
Force bool
|
||||
}
|
||||
|
||||
func send(ctx context.Context, api sendAPIs, params sendParams) (cid.Cid, error) {
|
||||
if params.From == address.Undef {
|
||||
defaddr, err := api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
params.From = defaddr
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
From: params.From,
|
||||
To: params.To,
|
||||
Value: params.Val,
|
||||
|
||||
GasPremium: params.GasPremium,
|
||||
GasFeeCap: params.GasFeeCap,
|
||||
GasLimit: params.GasLimit,
|
||||
|
||||
Method: params.Method,
|
||||
Params: params.Params,
|
||||
}
|
||||
|
||||
if !params.Force {
|
||||
// Funds insufficient check
|
||||
fromBalance, err := api.WalletBalance(ctx, msg.From)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
totalCost := types.BigAdd(types.BigMul(msg.GasFeeCap, types.NewInt(uint64(msg.GasLimit))), msg.Value)
|
||||
|
||||
if fromBalance.LessThan(totalCost) {
|
||||
fmt.Printf("WARNING: From balance %s less than total cost %s\n", types.FIL(fromBalance), types.FIL(totalCost))
|
||||
return cid.Undef, fmt.Errorf("--force must be specified for this action to have an effect; you have been warned")
|
||||
}
|
||||
}
|
||||
|
||||
if params.Nonce.Set {
|
||||
msg.Nonce = params.Nonce.N
|
||||
sm, err := api.WalletSignMessage(ctx, params.From, msg)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
_, err = api.MpoolPush(ctx, sm)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return sm.Cid(), nil
|
||||
}
|
||||
|
||||
sm, err := api.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return sm.Cid(), nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
}
|
||||
|
152
cli/services.go
Normal file
152
cli/services.go
Normal file
@ -0,0 +1,152 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ServicesAPI interface {
|
||||
// Sends executes a send given SendParams
|
||||
Send(ctx context.Context, params SendParams) (cid.Cid, error)
|
||||
// DecodeTypedParamsFromJSON takes in information needed to identify a method and converts JSON
|
||||
// paramaters to bytes of their CBOR encoding
|
||||
DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error)
|
||||
|
||||
// Close ends the session of services and disconnects from RPC, using Services after Close is called
|
||||
// most likely will result in an error
|
||||
// Should not be called concurrently
|
||||
Close() error
|
||||
}
|
||||
|
||||
type ServicesImpl struct {
|
||||
api api.FullNode
|
||||
closer jsonrpc.ClientCloser
|
||||
}
|
||||
|
||||
func (s *ServicesImpl) Close() error {
|
||||
if s.closer == nil {
|
||||
return xerrors.Errorf("Services already closed")
|
||||
}
|
||||
s.closer()
|
||||
s.closer = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) {
|
||||
act, err := s.api.StateGetActor(ctx, to, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type SendParams struct {
|
||||
To address.Address
|
||||
From address.Address
|
||||
Val abi.TokenAmount
|
||||
|
||||
GasPremium abi.TokenAmount
|
||||
GasFeeCap abi.TokenAmount
|
||||
GasLimit int64
|
||||
|
||||
Nonce struct {
|
||||
N uint64
|
||||
Set bool
|
||||
}
|
||||
Method abi.MethodNum
|
||||
Params []byte
|
||||
|
||||
Force bool
|
||||
}
|
||||
|
||||
// This is specialised Send for Send command
|
||||
// There might be room for generic Send that other commands can use to send their messages
|
||||
// We will see
|
||||
|
||||
func (s *ServicesImpl) Send(ctx context.Context, params SendParams) (cid.Cid, error) {
|
||||
if params.From == address.Undef {
|
||||
defaddr, err := s.api.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
params.From = defaddr
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
From: params.From,
|
||||
To: params.To,
|
||||
Value: params.Val,
|
||||
|
||||
GasPremium: params.GasPremium,
|
||||
GasFeeCap: params.GasFeeCap,
|
||||
GasLimit: params.GasLimit,
|
||||
|
||||
Method: params.Method,
|
||||
Params: params.Params,
|
||||
}
|
||||
|
||||
if !params.Force {
|
||||
// Funds insufficient check
|
||||
fromBalance, err := s.api.WalletBalance(ctx, msg.From)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
totalCost := types.BigAdd(types.BigMul(msg.GasFeeCap, types.NewInt(uint64(msg.GasLimit))), msg.Value)
|
||||
|
||||
if fromBalance.LessThan(totalCost) {
|
||||
fmt.Printf("WARNING: From balance %s less than total cost %s\n", types.FIL(fromBalance), types.FIL(totalCost))
|
||||
return cid.Undef, fmt.Errorf("--force must be specified for this action to have an effect; you have been warned")
|
||||
}
|
||||
}
|
||||
|
||||
if params.Nonce.Set {
|
||||
msg.Nonce = params.Nonce.N
|
||||
sm, err := s.api.WalletSignMessage(ctx, params.From, msg)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
_, err = s.api.MpoolPush(ctx, sm)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return sm.Cid(), nil
|
||||
}
|
||||
|
||||
sm, err := s.api.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return sm.Cid(), nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user