Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

paych: get available funds by address or by from/to #3547

Merged
merged 2 commits into from
Sep 5, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ type FullNode interface {

PaychGet(ctx context.Context, from, to address.Address, amt types.BigInt) (*ChannelInfo, error)
PaychGetWaitReady(context.Context, cid.Cid) (address.Address, error)
PaychAvailableFunds(from, to address.Address) (*ChannelAvailableFunds, error)
PaychAvailableFunds(ch address.Address) (*ChannelAvailableFunds, error)
PaychAvailableFundsByFromTo(from, to address.Address) (*ChannelAvailableFunds, error)
PaychList(context.Context) ([]address.Address, error)
PaychStatus(context.Context, address.Address) (*PaychStatus, error)
PaychSettle(context.Context, address.Address) (cid.Cid, error)
Expand Down Expand Up @@ -540,7 +541,12 @@ type ChannelInfo struct {
}

type ChannelAvailableFunds struct {
// Channel is the address of the channel
Channel *address.Address
// From is the from address of the channel (channel creator)
From address.Address
// To is the to address of the channel
To address.Address
// ConfirmedAmt is the amount of funds that have been confirmed on-chain
// for the channel
ConfirmedAmt types.BigInt
Expand Down
41 changes: 23 additions & 18 deletions api/apistruct/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,22 +207,23 @@ type FullNodeStruct struct {

MarketEnsureAvailable func(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`

PaychGet func(ctx context.Context, from, to address.Address, amt types.BigInt) (*api.ChannelInfo, error) `perm:"sign"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an indentation change

PaychGetWaitReady func(context.Context, cid.Cid) (address.Address, error) `perm:"sign"`
PaychAvailableFunds func(address.Address, address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"`
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
PaychStatus func(context.Context, address.Address) (*api.PaychStatus, error) `perm:"read"`
PaychSettle func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
PaychCollect func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
PaychAllocateLane func(context.Context, address.Address) (uint64, error) `perm:"sign"`
PaychNewPayment func(ctx context.Context, from, to address.Address, vouchers []api.VoucherSpec) (*api.PaymentInfo, error) `perm:"sign"`
PaychVoucherCheck func(context.Context, *paych.SignedVoucher) error `perm:"read"`
PaychVoucherCheckValid func(context.Context, address.Address, *paych.SignedVoucher) error `perm:"read"`
PaychVoucherCheckSpendable func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*api.VoucherCreateResult, error) `perm:"sign"`
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
PaychGet func(ctx context.Context, from, to address.Address, amt types.BigInt) (*api.ChannelInfo, error) `perm:"sign"`
PaychGetWaitReady func(context.Context, cid.Cid) (address.Address, error) `perm:"sign"`
PaychAvailableFunds func(address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"`
PaychAvailableFundsByFromTo func(address.Address, address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"`
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
PaychStatus func(context.Context, address.Address) (*api.PaychStatus, error) `perm:"read"`
PaychSettle func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
PaychCollect func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
PaychAllocateLane func(context.Context, address.Address) (uint64, error) `perm:"sign"`
PaychNewPayment func(ctx context.Context, from, to address.Address, vouchers []api.VoucherSpec) (*api.PaymentInfo, error) `perm:"sign"`
PaychVoucherCheck func(context.Context, *paych.SignedVoucher) error `perm:"read"`
PaychVoucherCheckValid func(context.Context, address.Address, *paych.SignedVoucher) error `perm:"read"`
PaychVoucherCheckSpendable func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*api.VoucherCreateResult, error) `perm:"sign"`
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
}
}

Expand Down Expand Up @@ -905,8 +906,12 @@ func (c *FullNodeStruct) PaychGetWaitReady(ctx context.Context, sentinel cid.Cid
return c.Internal.PaychGetWaitReady(ctx, sentinel)
}

func (c *FullNodeStruct) PaychAvailableFunds(from address.Address, to address.Address) (*api.ChannelAvailableFunds, error) {
return c.Internal.PaychAvailableFunds(from, to)
func (c *FullNodeStruct) PaychAvailableFunds(ch address.Address) (*api.ChannelAvailableFunds, error) {
return c.Internal.PaychAvailableFunds(ch)
}

func (c *FullNodeStruct) PaychAvailableFundsByFromTo(from, to address.Address) (*api.ChannelAvailableFunds, error) {
return c.Internal.PaychAvailableFundsByFromTo(from, to)
}

func (c *FullNodeStruct) PaychList(ctx context.Context) ([]address.Address, error) {
Expand Down
108 changes: 72 additions & 36 deletions cli/paych.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"sort"
"strings"

"github.com/filecoin-project/lotus/api"

"github.com/filecoin-project/lotus/paychmgr"

"github.com/filecoin-project/go-address"
Expand Down Expand Up @@ -80,13 +82,13 @@ var paychAddFundsCmd = &cli.Command{
},
}

var paychStatusCmd = &cli.Command{
Name: "status",
Usage: "Show the status of an outbound payment channel between fromAddress and toAddress",
var paychStatusByFromToCmd = &cli.Command{
Name: "status-by-from-to",
Usage: "Show the status of an active outbound payment channel by from/to addresses",
ArgsUsage: "[fromAddress toAddress]",
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 2 {
return ShowHelp(cctx, fmt.Errorf("must pass two arguments: <from> <to>"))
return ShowHelp(cctx, fmt.Errorf("must pass two arguments: <from address> <to address>"))
}

from, err := address.NewFromString(cctx.Args().Get(0))
Expand All @@ -105,52 +107,86 @@ var paychStatusCmd = &cli.Command{
}
defer closer()

avail, err := api.PaychAvailableFunds(from, to)
avail, err := api.PaychAvailableFundsByFromTo(from, to)
if err != nil {
return err
}

if avail.Channel == nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic of formatting the channel status output is the same, it's just been extracted into a method that's called by both paych status and paych status-by-from-to

if avail.PendingWaitSentinel != nil {
fmt.Fprint(cctx.App.Writer, "Creating channel\n")
fmt.Fprintf(cctx.App.Writer, " From: %s\n", from)
fmt.Fprintf(cctx.App.Writer, " To: %s\n", to)
fmt.Fprintf(cctx.App.Writer, " Pending Amt: %d\n", avail.PendingAmt)
fmt.Fprintf(cctx.App.Writer, " Wait Sentinel: %s\n", avail.PendingWaitSentinel)
return nil
}
fmt.Fprint(cctx.App.Writer, "Channel does not exist\n")
fmt.Fprintf(cctx.App.Writer, " From: %s\n", from)
fmt.Fprintf(cctx.App.Writer, " To: %s\n", to)
return nil
paychStatus(cctx.App.Writer, avail)
return nil
},
}

var paychStatusCmd = &cli.Command{
Name: "status",
Usage: "Show the status of an outbound payment channel",
ArgsUsage: "[channelAddress]",
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 1 {
return ShowHelp(cctx, fmt.Errorf("must pass an argument: <channel address>"))
}

if avail.PendingWaitSentinel != nil {
fmt.Fprint(cctx.App.Writer, "Adding Funds to channel\n")
} else {
fmt.Fprint(cctx.App.Writer, "Channel exists\n")
ch, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return ShowHelp(cctx, fmt.Errorf("failed to parse channel address: %s", err))
}

nameValues := [][]string{
{"Channel", avail.Channel.String()},
{"From", from.String()},
{"To", to.String()},
{"Confirmed Amt", fmt.Sprintf("%d", avail.ConfirmedAmt)},
{"Pending Amt", fmt.Sprintf("%d", avail.PendingAmt)},
{"Queued Amt", fmt.Sprintf("%d", avail.QueuedAmt)},
{"Voucher Redeemed Amt", fmt.Sprintf("%d", avail.VoucherReedeemedAmt)},
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
if avail.PendingWaitSentinel != nil {
nameValues = append(nameValues, []string{
"Add Funds Wait Sentinel",
avail.PendingWaitSentinel.String(),
})
defer closer()

avail, err := api.PaychAvailableFunds(ch)
if err != nil {
return err
}
fmt.Fprint(cctx.App.Writer, formatNameValues(nameValues))

paychStatus(cctx.App.Writer, avail)
return nil
},
}

func paychStatus(writer io.Writer, avail *api.ChannelAvailableFunds) {
if avail.Channel == nil {
if avail.PendingWaitSentinel != nil {
fmt.Fprint(writer, "Creating channel\n")
fmt.Fprintf(writer, " From: %s\n", avail.From)
fmt.Fprintf(writer, " To: %s\n", avail.To)
fmt.Fprintf(writer, " Pending Amt: %d\n", avail.PendingAmt)
fmt.Fprintf(writer, " Wait Sentinel: %s\n", avail.PendingWaitSentinel)
return
}
fmt.Fprint(writer, "Channel does not exist\n")
fmt.Fprintf(writer, " From: %s\n", avail.From)
fmt.Fprintf(writer, " To: %s\n", avail.To)
return
}

if avail.PendingWaitSentinel != nil {
fmt.Fprint(writer, "Adding Funds to channel\n")
} else {
fmt.Fprint(writer, "Channel exists\n")
}

nameValues := [][]string{
{"Channel", avail.Channel.String()},
{"From", avail.From.String()},
{"To", avail.To.String()},
{"Confirmed Amt", fmt.Sprintf("%d", avail.ConfirmedAmt)},
{"Pending Amt", fmt.Sprintf("%d", avail.PendingAmt)},
{"Queued Amt", fmt.Sprintf("%d", avail.QueuedAmt)},
{"Voucher Redeemed Amt", fmt.Sprintf("%d", avail.VoucherReedeemedAmt)},
}
if avail.PendingWaitSentinel != nil {
nameValues = append(nameValues, []string{
"Add Funds Wait Sentinel",
avail.PendingWaitSentinel.String(),
})
}
fmt.Fprint(writer, formatNameValues(nameValues))
}

func formatNameValues(nameValues [][]string) string {
maxLen := 0
for _, nv := range nameValues {
Expand Down
16 changes: 8 additions & 8 deletions cli/paych_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,23 @@ func TestPaymentChannelStatus(t *testing.T) {
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)

cmd := []string{creatorAddr.String(), receiverAddr.String()}
out := creatorCLI.runCmd(paychStatusCmd, cmd)
out := creatorCLI.runCmd(paychStatusByFromToCmd, cmd)
fmt.Println(out)
noChannelState := "Channel does not exist"
require.Regexp(t, regexp.MustCompile(noChannelState), out)

channelAmt := uint64(100)
create := make(chan string)
go func() {
// creator: paych get <creator> <receiver> <amount>
// creator: paych add-funds <creator> <receiver> <amount>
cmd = []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
create <- creatorCLI.runCmd(paychGetCmd, cmd)
create <- creatorCLI.runCmd(paychAddFundsCmd, cmd)
}()

// Wait for the output to stop being "Channel does not exist"
for regexp.MustCompile(noChannelState).MatchString(out) {
cmd = []string{creatorAddr.String(), receiverAddr.String()}
out = creatorCLI.runCmd(paychStatusCmd, cmd)
out = creatorCLI.runCmd(paychStatusByFromToCmd, cmd)
}
fmt.Println(out)

Expand All @@ -153,7 +153,7 @@ func TestPaymentChannelStatus(t *testing.T) {
// Wait for create channel to complete
chstr := <-create

cmd = []string{creatorAddr.String(), receiverAddr.String()}
cmd = []string{chstr}
out = creatorCLI.runCmd(paychStatusCmd, cmd)
fmt.Println(out)
// Output should have the channel address
Expand All @@ -169,7 +169,7 @@ func TestPaymentChannelStatus(t *testing.T) {
cmd = []string{chAddr.String(), fmt.Sprintf("%d", voucherAmt)}
creatorCLI.runCmd(paychVoucherCreateCmd, cmd)

cmd = []string{creatorAddr.String(), receiverAddr.String()}
cmd = []string{chstr}
out = creatorCLI.runCmd(paychStatusCmd, cmd)
fmt.Println(out)
voucherAmtAtto := types.BigMul(types.NewInt(voucherAmt), types.NewInt(build.FilecoinPrecision))
Expand Down Expand Up @@ -344,10 +344,10 @@ func TestPaymentChannelVoucherCreateShortfall(t *testing.T) {
mockCLI := newMockCLI(t)
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)

// creator: paych get <creator> <receiver> <amount>
// creator: paych add-funds <creator> <receiver> <amount>
channelAmt := 100
cmd := []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd)

chAddr, err := address.NewFromString(chstr)
require.NoError(t, err)
Expand Down
8 changes: 6 additions & 2 deletions node/impl/paych/paych.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ func (a *PaychAPI) PaychGet(ctx context.Context, from, to address.Address, amt t
}, nil
}

func (a *PaychAPI) PaychAvailableFunds(from, to address.Address) (*api.ChannelAvailableFunds, error) {
return a.PaychMgr.AvailableFunds(from, to)
func (a *PaychAPI) PaychAvailableFunds(ch address.Address) (*api.ChannelAvailableFunds, error) {
return a.PaychMgr.AvailableFunds(ch)
}

func (a *PaychAPI) PaychAvailableFundsByFromTo(from, to address.Address) (*api.ChannelAvailableFunds, error) {
return a.PaychMgr.AvailableFundsByFromTo(from, to)
}

func (a *PaychAPI) PaychGetWaitReady(ctx context.Context, sentinel cid.Cid) (address.Address, error) {
Expand Down
41 changes: 38 additions & 3 deletions paychmgr/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,48 @@ func (pm *Manager) GetPaych(ctx context.Context, from, to address.Address, amt t
return chanAccessor.getPaych(ctx, amt)
}

func (pm *Manager) AvailableFunds(from address.Address, to address.Address) (*api.ChannelAvailableFunds, error) {
chanAccessor, err := pm.accessorByFromTo(from, to)
func (pm *Manager) AvailableFunds(ch address.Address) (*api.ChannelAvailableFunds, error) {
ca, err := pm.accessorByAddress(ch)
if err != nil {
return nil, err
}

ci, err := ca.getChannelInfo(ch)
if err != nil {
return nil, err
}

return ca.availableFunds(ci.ChannelID)
}

func (pm *Manager) AvailableFundsByFromTo(from address.Address, to address.Address) (*api.ChannelAvailableFunds, error) {
ca, err := pm.accessorByFromTo(from, to)
if err != nil {
return nil, err
}

ci, err := ca.outboundActiveByFromTo(from, to)
if err == ErrChannelNotTracked {
// If there is no active channel between from / to we still want to
// return an empty ChannelAvailableFunds, so that clients can check
// for the existence of a channel between from / to without getting
// an error.
return &api.ChannelAvailableFunds{
Channel: nil,
From: from,
To: to,
ConfirmedAmt: types.NewInt(0),
PendingAmt: types.NewInt(0),
PendingWaitSentinel: nil,
QueuedAmt: types.NewInt(0),
VoucherReedeemedAmt: types.NewInt(0),
}, nil
}
if err != nil {
return nil, err
}

return chanAccessor.availableFunds()
return ca.availableFunds(ci.ChannelID)
}

// GetPaychWaitReady waits until the create channel / add funds message with the
Expand Down
9 changes: 8 additions & 1 deletion paychmgr/paych.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
xerrors "golang.org/x/xerrors"
"golang.org/x/xerrors"
)

// insufficientFundsErr indicates that there are not enough funds in the
Expand Down Expand Up @@ -81,6 +81,13 @@ func (ca *channelAccessor) getChannelInfo(addr address.Address) (*ChannelInfo, e
return ca.store.ByAddress(addr)
}

func (ca *channelAccessor) outboundActiveByFromTo(from, to address.Address) (*ChannelInfo, error) {
ca.lk.Lock()
defer ca.lk.Unlock()

return ca.store.OutboundActiveByFromTo(from, to)
}

// createVoucher creates a voucher with the given specification, setting its
// nonce, signing the voucher and storing it in the local datastore.
// If there are not enough funds in the channel to create the voucher, returns
Expand Down
Loading