feat: paych CLI - lotus paych status <from> <to>
This commit is contained in:
parent
8423325a6e
commit
930be3d0e5
87
cli/paych.go
87
cli/paych.go
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/paychmgr"
|
"github.com/filecoin-project/lotus/paychmgr"
|
||||||
|
|
||||||
@ -79,6 +80,92 @@ var paychGetCmd = &cli.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var paychStatusCmd = &cli.Command{
|
||||||
|
Name: "status",
|
||||||
|
Usage: "Show the status of an outbound payment channel between fromAddress and toAddress",
|
||||||
|
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>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
from, err := address.NewFromString(cctx.Args().Get(0))
|
||||||
|
if err != nil {
|
||||||
|
return ShowHelp(cctx, fmt.Errorf("failed to parse from address: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
to, err := address.NewFromString(cctx.Args().Get(1))
|
||||||
|
if err != nil {
|
||||||
|
return ShowHelp(cctx, fmt.Errorf("failed to parse to address: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
api, closer, err := GetFullNodeAPI(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
avail, err := api.PaychAvailableFunds(from, to)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if avail.Channel == nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if avail.PendingWaitSentinel != nil {
|
||||||
|
fmt.Fprint(cctx.App.Writer, "Adding Funds to channel\n")
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(cctx.App.Writer, "Channel exists\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)},
|
||||||
|
}
|
||||||
|
if avail.PendingWaitSentinel != nil {
|
||||||
|
nameValues = append(nameValues, []string{
|
||||||
|
"Add Funds Wait Sentinel",
|
||||||
|
avail.PendingWaitSentinel.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fmt.Fprint(cctx.App.Writer, formatNameValues(nameValues))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatNameValues(nameValues [][]string) string {
|
||||||
|
maxLen := 0
|
||||||
|
for _, nv := range nameValues {
|
||||||
|
if len(nv[0]) > maxLen {
|
||||||
|
maxLen = len(nv[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := make([]string, len(nameValues))
|
||||||
|
for i, nv := range nameValues {
|
||||||
|
namePad := strings.Repeat(" ", maxLen-len(nv[0]))
|
||||||
|
out[i] = " " + nv[0] + ": " + namePad + nv[1]
|
||||||
|
}
|
||||||
|
return strings.Join(out, "\n") + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
var paychListCmd = &cli.Command{
|
var paychListCmd = &cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List all locally registered payment channels",
|
Usage: "List all locally registered payment channels",
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
|
||||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||||
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||||
@ -53,7 +55,7 @@ func TestPaymentChannels(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
||||||
paymentCreator := nodes[0]
|
paymentCreator := nodes[0]
|
||||||
paymentReceiver := nodes[0]
|
paymentReceiver := nodes[1]
|
||||||
creatorAddr := addrs[0]
|
creatorAddr := addrs[0]
|
||||||
receiverAddr := addrs[1]
|
receiverAddr := addrs[1]
|
||||||
|
|
||||||
@ -99,6 +101,83 @@ type voucherSpec struct {
|
|||||||
lane int
|
lane int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPaymentChannelStatus tests the payment channel status CLI command
|
||||||
|
func TestPaymentChannelStatus(t *testing.T) {
|
||||||
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||||
|
|
||||||
|
blocktime := 5 * time.Millisecond
|
||||||
|
ctx := context.Background()
|
||||||
|
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
||||||
|
paymentCreator := nodes[0]
|
||||||
|
creatorAddr := addrs[0]
|
||||||
|
receiverAddr := addrs[1]
|
||||||
|
|
||||||
|
// Create mock CLI
|
||||||
|
mockCLI := newMockCLI(t)
|
||||||
|
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||||
|
|
||||||
|
cmd := []string{creatorAddr.String(), receiverAddr.String()}
|
||||||
|
out := creatorCLI.runCmd(paychStatusCmd, 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>
|
||||||
|
cmd = []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
|
||||||
|
create <- creatorCLI.runCmd(paychGetCmd, 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)
|
||||||
|
}
|
||||||
|
fmt.Println(out)
|
||||||
|
|
||||||
|
// The next state should be creating channel or channel created, depending
|
||||||
|
// on timing
|
||||||
|
stateCreating := regexp.MustCompile("Creating channel").MatchString(out)
|
||||||
|
stateCreated := regexp.MustCompile("Channel exists").MatchString(out)
|
||||||
|
require.True(t, stateCreating || stateCreated)
|
||||||
|
|
||||||
|
channelAmtAtto := types.BigMul(types.NewInt(channelAmt), types.NewInt(build.FilecoinPrecision))
|
||||||
|
channelAmtStr := fmt.Sprintf("%d", channelAmtAtto)
|
||||||
|
if stateCreating {
|
||||||
|
// If we're in the creating state (most likely) the amount should be pending
|
||||||
|
require.Regexp(t, regexp.MustCompile("Pending.*"+channelAmtStr), out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for create channel to complete
|
||||||
|
chstr := <-create
|
||||||
|
|
||||||
|
cmd = []string{creatorAddr.String(), receiverAddr.String()}
|
||||||
|
out = creatorCLI.runCmd(paychStatusCmd, cmd)
|
||||||
|
fmt.Println(out)
|
||||||
|
// Output should have the channel address
|
||||||
|
require.Regexp(t, regexp.MustCompile("Channel.*"+chstr), out)
|
||||||
|
// Output should have confirmed amount
|
||||||
|
require.Regexp(t, regexp.MustCompile("Confirmed.*"+channelAmtStr), out)
|
||||||
|
|
||||||
|
chAddr, err := address.NewFromString(chstr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// creator: paych voucher create <channel> <amount>
|
||||||
|
voucherAmt := uint64(10)
|
||||||
|
cmd = []string{chAddr.String(), fmt.Sprintf("%d", voucherAmt)}
|
||||||
|
creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||||
|
|
||||||
|
cmd = []string{creatorAddr.String(), receiverAddr.String()}
|
||||||
|
out = creatorCLI.runCmd(paychStatusCmd, cmd)
|
||||||
|
fmt.Println(out)
|
||||||
|
voucherAmtAtto := types.BigMul(types.NewInt(voucherAmt), types.NewInt(build.FilecoinPrecision))
|
||||||
|
voucherAmtStr := fmt.Sprintf("%d", voucherAmtAtto)
|
||||||
|
// Output should include voucher amount
|
||||||
|
require.Regexp(t, regexp.MustCompile("Voucher.*"+voucherAmtStr), out)
|
||||||
|
}
|
||||||
|
|
||||||
// TestPaymentChannelVouchers does a basic test to exercise some payment
|
// TestPaymentChannelVouchers does a basic test to exercise some payment
|
||||||
// channel voucher commands
|
// channel voucher commands
|
||||||
func TestPaymentChannelVouchers(t *testing.T) {
|
func TestPaymentChannelVouchers(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user