2020-08-13 20:37:09 +00:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"flag"
|
2020-08-18 15:27:00 +00:00
|
|
|
"fmt"
|
2020-08-13 20:37:09 +00:00
|
|
|
"os"
|
2020-09-01 14:33:44 +00:00
|
|
|
"regexp"
|
2020-08-13 20:37:09 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2020-09-03 16:03:42 +00:00
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
|
|
|
2020-08-13 20:37:09 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
|
|
|
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/verifreg"
|
|
|
|
|
|
|
|
"github.com/multiformats/go-multiaddr"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/events"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/api/apibstore"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
|
|
|
cbor "github.com/ipfs/go-ipld-cbor"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2020-09-07 12:52:45 +00:00
|
|
|
"github.com/filecoin-project/lotus/node/modules"
|
2020-08-13 20:37:09 +00:00
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/api/test"
|
|
|
|
"github.com/filecoin-project/lotus/chain/wallet"
|
|
|
|
builder "github.com/filecoin-project/lotus/node/test"
|
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
power.ConsensusMinerMinPower = big.NewInt(2048)
|
|
|
|
saminer.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
|
|
|
|
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
|
|
|
|
}
|
|
|
|
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
2020-09-07 12:52:45 +00:00
|
|
|
|
|
|
|
modules.PubsubSubscribeImmediately = true
|
2020-08-13 20:37:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TestPaymentChannels does a basic test to exercise the payment channel CLI
|
|
|
|
// commands
|
|
|
|
func TestPaymentChannels(t *testing.T) {
|
|
|
|
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
|
|
|
|
|
|
|
blocktime := 5 * time.Millisecond
|
|
|
|
ctx := context.Background()
|
2020-08-18 15:27:00 +00:00
|
|
|
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
|
|
|
paymentCreator := nodes[0]
|
2020-09-03 16:03:42 +00:00
|
|
|
paymentReceiver := nodes[1]
|
2020-08-18 15:27:00 +00:00
|
|
|
creatorAddr := addrs[0]
|
|
|
|
receiverAddr := addrs[1]
|
|
|
|
|
|
|
|
// Create mock CLI
|
|
|
|
mockCLI := newMockCLI(t)
|
|
|
|
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
|
|
|
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
|
|
|
|
|
2020-09-03 13:45:09 +00:00
|
|
|
// creator: paych add-funds <creator> <receiver> <amount>
|
2020-08-18 15:27:00 +00:00
|
|
|
channelAmt := "100000"
|
|
|
|
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
|
2020-09-03 13:45:09 +00:00
|
|
|
chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd)
|
2020-08-18 15:27:00 +00:00
|
|
|
|
|
|
|
chAddr, err := address.NewFromString(chstr)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// creator: paych voucher create <channel> <amount>
|
|
|
|
voucherAmt := 100
|
|
|
|
vamt := strconv.Itoa(voucherAmt)
|
|
|
|
cmd = []string{chAddr.String(), vamt}
|
|
|
|
voucher := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher add <channel> <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher}
|
|
|
|
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
|
|
|
|
|
|
|
// creator: paych settle <channel>
|
|
|
|
cmd = []string{chAddr.String()}
|
|
|
|
creatorCLI.runCmd(paychSettleCmd, cmd)
|
|
|
|
|
|
|
|
// Wait for the chain to reach the settle height
|
|
|
|
chState := getPaychState(ctx, t, paymentReceiver, chAddr)
|
|
|
|
waitForHeight(ctx, t, paymentReceiver, chState.SettlingAt)
|
|
|
|
|
|
|
|
// receiver: paych collect <channel>
|
|
|
|
cmd = []string{chAddr.String()}
|
|
|
|
receiverCLI.runCmd(paychCloseCmd, cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
type voucherSpec struct {
|
|
|
|
serialized string
|
|
|
|
amt int
|
|
|
|
lane int
|
|
|
|
}
|
|
|
|
|
2020-09-03 16:03:42 +00:00
|
|
|
// 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()}
|
2020-09-04 11:44:09 +00:00
|
|
|
out := creatorCLI.runCmd(paychStatusByFromToCmd, cmd)
|
2020-09-03 16:03:42 +00:00
|
|
|
fmt.Println(out)
|
|
|
|
noChannelState := "Channel does not exist"
|
|
|
|
require.Regexp(t, regexp.MustCompile(noChannelState), out)
|
|
|
|
|
|
|
|
channelAmt := uint64(100)
|
|
|
|
create := make(chan string)
|
|
|
|
go func() {
|
2020-09-04 11:44:09 +00:00
|
|
|
// creator: paych add-funds <creator> <receiver> <amount>
|
2020-09-03 16:03:42 +00:00
|
|
|
cmd = []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
|
2020-09-04 11:44:09 +00:00
|
|
|
create <- creatorCLI.runCmd(paychAddFundsCmd, cmd)
|
2020-09-03 16:03:42 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
// Wait for the output to stop being "Channel does not exist"
|
|
|
|
for regexp.MustCompile(noChannelState).MatchString(out) {
|
|
|
|
cmd = []string{creatorAddr.String(), receiverAddr.String()}
|
2020-09-04 11:44:09 +00:00
|
|
|
out = creatorCLI.runCmd(paychStatusByFromToCmd, cmd)
|
2020-09-03 16:03:42 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
|
2020-09-04 11:44:09 +00:00
|
|
|
cmd = []string{chstr}
|
2020-09-03 16:03:42 +00:00
|
|
|
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)
|
|
|
|
|
2020-09-04 11:44:09 +00:00
|
|
|
cmd = []string{chstr}
|
2020-09-03 16:03:42 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-08-18 15:27:00 +00:00
|
|
|
// TestPaymentChannelVouchers does a basic test to exercise some payment
|
|
|
|
// channel voucher commands
|
|
|
|
func TestPaymentChannelVouchers(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]
|
2020-08-20 16:09:52 +00:00
|
|
|
paymentReceiver := nodes[1]
|
2020-08-18 15:27:00 +00:00
|
|
|
creatorAddr := addrs[0]
|
|
|
|
receiverAddr := addrs[1]
|
|
|
|
|
|
|
|
// Create mock CLI
|
|
|
|
mockCLI := newMockCLI(t)
|
|
|
|
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
2020-08-20 16:09:52 +00:00
|
|
|
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
|
2020-08-18 15:27:00 +00:00
|
|
|
|
2020-09-03 13:45:09 +00:00
|
|
|
// creator: paych add-funds <creator> <receiver> <amount>
|
2020-08-18 15:27:00 +00:00
|
|
|
channelAmt := "100000"
|
|
|
|
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
|
2020-09-03 13:45:09 +00:00
|
|
|
chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd)
|
2020-08-18 15:27:00 +00:00
|
|
|
|
|
|
|
chAddr, err := address.NewFromString(chstr)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
var vouchers []voucherSpec
|
|
|
|
|
|
|
|
// creator: paych voucher create <channel> <amount>
|
|
|
|
// Note: implied --lane=0
|
|
|
|
voucherAmt1 := 100
|
2020-08-20 16:09:52 +00:00
|
|
|
cmd = []string{chAddr.String(), strconv.Itoa(voucherAmt1)}
|
2020-08-18 15:27:00 +00:00
|
|
|
voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
|
|
|
vouchers = append(vouchers, voucherSpec{serialized: voucher1, lane: 0, amt: voucherAmt1})
|
|
|
|
|
|
|
|
// creator: paych voucher create <channel> <amount> --lane=5
|
|
|
|
lane5 := "--lane=5"
|
|
|
|
voucherAmt2 := 50
|
2020-08-20 16:09:52 +00:00
|
|
|
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt2)}
|
2020-08-18 15:27:00 +00:00
|
|
|
voucher2 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
|
|
|
vouchers = append(vouchers, voucherSpec{serialized: voucher2, lane: 5, amt: voucherAmt2})
|
|
|
|
|
|
|
|
// creator: paych voucher create <channel> <amount> --lane=5
|
|
|
|
voucherAmt3 := 70
|
2020-08-20 16:09:52 +00:00
|
|
|
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt3)}
|
2020-08-18 15:27:00 +00:00
|
|
|
voucher3 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
|
|
|
vouchers = append(vouchers, voucherSpec{serialized: voucher3, lane: 5, amt: voucherAmt3})
|
|
|
|
|
2020-08-20 16:09:52 +00:00
|
|
|
// creator: paych voucher create <channel> <amount> --lane=5
|
|
|
|
voucherAmt4 := 80
|
|
|
|
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt4)}
|
|
|
|
voucher4 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
|
|
|
vouchers = append(vouchers, voucherSpec{serialized: voucher4, lane: 5, amt: voucherAmt4})
|
|
|
|
|
2020-08-18 15:27:00 +00:00
|
|
|
// creator: paych voucher list <channel> --export
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
list := creatorCLI.runCmd(paychVoucherListCmd, cmd)
|
|
|
|
|
2020-08-20 16:09:52 +00:00
|
|
|
// Check that voucher list output is correct on creator
|
2020-08-18 15:27:00 +00:00
|
|
|
checkVoucherOutput(t, list, vouchers)
|
|
|
|
|
|
|
|
// creator: paych voucher best-spendable <channel>
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
bestSpendable := creatorCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
|
|
|
|
2020-08-20 16:09:52 +00:00
|
|
|
// Check that best spendable output is correct on creator
|
2020-08-18 15:27:00 +00:00
|
|
|
bestVouchers := []voucherSpec{
|
|
|
|
{serialized: voucher1, lane: 0, amt: voucherAmt1},
|
2020-08-20 16:09:52 +00:00
|
|
|
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
2020-08-18 15:27:00 +00:00
|
|
|
}
|
|
|
|
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
2020-08-20 16:09:52 +00:00
|
|
|
|
|
|
|
// receiver: paych voucher add <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher1}
|
|
|
|
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher add <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher2}
|
|
|
|
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher add <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher3}
|
|
|
|
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher add <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher4}
|
|
|
|
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher list <channel> --export
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
list = receiverCLI.runCmd(paychVoucherListCmd, cmd)
|
|
|
|
|
|
|
|
// Check that voucher list output is correct on receiver
|
|
|
|
checkVoucherOutput(t, list, vouchers)
|
|
|
|
|
|
|
|
// receiver: paych voucher best-spendable <channel>
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
|
|
|
|
|
|
|
// Check that best spendable output is correct on receiver
|
|
|
|
bestVouchers = []voucherSpec{
|
|
|
|
{serialized: voucher1, lane: 0, amt: voucherAmt1},
|
|
|
|
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
|
|
|
}
|
|
|
|
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
|
|
|
|
|
|
|
// receiver: paych voucher submit <channel> <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher1}
|
|
|
|
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher best-spendable <channel>
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
|
|
|
|
|
|
|
// Check that best spendable output no longer includes submitted voucher
|
|
|
|
bestVouchers = []voucherSpec{
|
|
|
|
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
|
|
|
}
|
|
|
|
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
|
|
|
|
|
|
|
// There are three vouchers in lane 5: 50, 70, 80
|
|
|
|
// Submit the voucher for 50. Best spendable should still be 80.
|
|
|
|
// receiver: paych voucher submit <channel> <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher2}
|
|
|
|
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher best-spendable <channel>
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
|
|
|
|
|
|
|
// Check that best spendable output still includes the voucher for 80
|
|
|
|
bestVouchers = []voucherSpec{
|
|
|
|
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
|
|
|
}
|
|
|
|
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
|
|
|
|
|
|
|
// Submit the voucher for 80
|
|
|
|
// receiver: paych voucher submit <channel> <voucher>
|
|
|
|
cmd = []string{chAddr.String(), voucher4}
|
|
|
|
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
|
|
|
|
|
|
|
|
// receiver: paych voucher best-spendable <channel>
|
|
|
|
cmd = []string{"--export", chAddr.String()}
|
|
|
|
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
|
|
|
|
|
|
|
// Check that best spendable output no longer includes submitted voucher
|
|
|
|
bestVouchers = []voucherSpec{}
|
|
|
|
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
2020-08-18 15:27:00 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 14:33:44 +00:00
|
|
|
// TestPaymentChannelVoucherCreateShortfall verifies that if a voucher amount
|
|
|
|
// is greater than what's left in the channel, voucher create fails
|
|
|
|
func TestPaymentChannelVoucherCreateShortfall(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)
|
|
|
|
|
2020-09-04 11:44:09 +00:00
|
|
|
// creator: paych add-funds <creator> <receiver> <amount>
|
2020-09-01 14:33:44 +00:00
|
|
|
channelAmt := 100
|
|
|
|
cmd := []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
|
2020-09-04 11:44:09 +00:00
|
|
|
chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd)
|
2020-09-01 14:33:44 +00:00
|
|
|
|
|
|
|
chAddr, err := address.NewFromString(chstr)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// creator: paych voucher create <channel> <amount> --lane=1
|
|
|
|
voucherAmt1 := 60
|
|
|
|
lane1 := "--lane=1"
|
|
|
|
cmd = []string{lane1, chAddr.String(), strconv.Itoa(voucherAmt1)}
|
|
|
|
voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
|
|
|
fmt.Println(voucher1)
|
|
|
|
|
|
|
|
// creator: paych voucher create <channel> <amount> --lane=2
|
|
|
|
lane2 := "--lane=2"
|
|
|
|
voucherAmt2 := 70
|
|
|
|
cmd = []string{lane2, chAddr.String(), strconv.Itoa(voucherAmt2)}
|
|
|
|
_, err = creatorCLI.runCmdRaw(paychVoucherCreateCmd, cmd)
|
|
|
|
|
|
|
|
// Should fail because channel doesn't have required amount
|
|
|
|
require.Error(t, err)
|
|
|
|
|
|
|
|
shortfall := voucherAmt1 + voucherAmt2 - channelAmt
|
|
|
|
require.Regexp(t, regexp.MustCompile(fmt.Sprintf("shortfall: %d", shortfall)), err.Error())
|
|
|
|
}
|
|
|
|
|
2020-08-18 15:27:00 +00:00
|
|
|
func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
|
|
|
|
lines := strings.Split(list, "\n")
|
|
|
|
listVouchers := make(map[string]string)
|
|
|
|
for _, line := range lines {
|
|
|
|
parts := strings.Split(line, ";")
|
2020-08-20 16:09:52 +00:00
|
|
|
if len(parts) == 2 {
|
|
|
|
serialized := strings.TrimSpace(parts[1])
|
|
|
|
listVouchers[serialized] = strings.TrimSpace(parts[0])
|
|
|
|
}
|
2020-08-18 15:27:00 +00:00
|
|
|
}
|
|
|
|
for _, vchr := range vouchers {
|
|
|
|
res, ok := listVouchers[vchr.serialized]
|
|
|
|
require.True(t, ok)
|
|
|
|
require.Regexp(t, fmt.Sprintf("Lane %d", vchr.lane), res)
|
|
|
|
require.Regexp(t, fmt.Sprintf("%d", vchr.amt), res)
|
2020-08-20 16:09:52 +00:00
|
|
|
delete(listVouchers, vchr.serialized)
|
|
|
|
}
|
|
|
|
for _, vchr := range listVouchers {
|
|
|
|
require.Fail(t, "Extra voucher "+vchr)
|
2020-08-18 15:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) {
|
2020-08-13 20:37:09 +00:00
|
|
|
n, sn := builder.RPCMockSbBuilder(t, 2, test.OneMiner)
|
|
|
|
|
|
|
|
paymentCreator := n[0]
|
|
|
|
paymentReceiver := n[1]
|
|
|
|
miner := sn[0]
|
|
|
|
|
|
|
|
// Get everyone connected
|
|
|
|
addrs, err := paymentCreator.NetAddrsListen(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := paymentReceiver.NetConnect(ctx, addrs); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := miner.NetConnect(ctx, addrs); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start mining blocks
|
|
|
|
bm := test.NewBlockMiner(ctx, t, miner, blocktime)
|
|
|
|
bm.MineBlocks()
|
|
|
|
|
|
|
|
// Send some funds to register the receiver
|
|
|
|
receiverAddr, err := paymentReceiver.WalletNew(ctx, wallet.ActSigType("secp256k1"))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
test.SendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18))
|
|
|
|
|
|
|
|
// Get the creator's address
|
|
|
|
creatorAddr, err := paymentCreator.WalletDefaultAddress(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create mock CLI
|
2020-08-18 15:27:00 +00:00
|
|
|
return n, []address.Address{creatorAddr, receiverAddr}
|
2020-08-13 20:37:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type mockCLI struct {
|
|
|
|
t *testing.T
|
|
|
|
cctx *cli.Context
|
|
|
|
out *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMockCLI(t *testing.T) *mockCLI {
|
|
|
|
// Create a CLI App with an --api flag so that we can specify which node
|
|
|
|
// the command should be executed against
|
|
|
|
app := cli.NewApp()
|
|
|
|
app.Flags = []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "api",
|
|
|
|
Hidden: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
|
|
app.Writer = &out
|
|
|
|
app.Setup()
|
|
|
|
|
|
|
|
cctx := cli.NewContext(app, &flag.FlagSet{}, nil)
|
|
|
|
return &mockCLI{t: t, cctx: cctx, out: &out}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockCLI) client(addr multiaddr.Multiaddr) *mockCLIClient {
|
|
|
|
return &mockCLIClient{t: c.t, addr: addr, cctx: c.cctx, out: c.out}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mockCLIClient runs commands against a particular node
|
|
|
|
type mockCLIClient struct {
|
|
|
|
t *testing.T
|
|
|
|
addr multiaddr.Multiaddr
|
|
|
|
cctx *cli.Context
|
|
|
|
out *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockCLIClient) runCmd(cmd *cli.Command, input []string) string {
|
2020-09-01 14:33:44 +00:00
|
|
|
out, err := c.runCmdRaw(cmd, input)
|
|
|
|
require.NoError(c.t, err)
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockCLIClient) runCmdRaw(cmd *cli.Command, input []string) (string, error) {
|
2020-08-13 20:37:09 +00:00
|
|
|
// prepend --api=<node api listener address>
|
|
|
|
apiFlag := "--api=" + c.addr.String()
|
|
|
|
input = append([]string{apiFlag}, input...)
|
|
|
|
|
|
|
|
fs := c.flagSet(cmd)
|
|
|
|
err := fs.Parse(input)
|
|
|
|
require.NoError(c.t, err)
|
|
|
|
|
|
|
|
err = cmd.Action(cli.NewContext(c.cctx.App, fs, c.cctx))
|
|
|
|
|
|
|
|
// Get the output
|
|
|
|
str := strings.TrimSpace(c.out.String())
|
|
|
|
c.out.Reset()
|
2020-09-01 14:33:44 +00:00
|
|
|
return str, err
|
2020-08-13 20:37:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockCLIClient) flagSet(cmd *cli.Command) *flag.FlagSet {
|
|
|
|
// Apply app level flags (so we can process --api flag)
|
|
|
|
fs := &flag.FlagSet{}
|
|
|
|
for _, f := range c.cctx.App.Flags {
|
|
|
|
err := f.Apply(fs)
|
|
|
|
if err != nil {
|
|
|
|
c.t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Apply command level flags
|
|
|
|
for _, f := range cmd.Flags {
|
|
|
|
err := f.Apply(fs)
|
|
|
|
if err != nil {
|
|
|
|
c.t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fs
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitForHeight waits for the node to reach the given chain epoch
|
|
|
|
func waitForHeight(ctx context.Context, t *testing.T, node test.TestNode, height abi.ChainEpoch) {
|
|
|
|
atHeight := make(chan struct{})
|
|
|
|
chainEvents := events.NewEvents(ctx, node)
|
|
|
|
err := chainEvents.ChainAt(func(ctx context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
|
|
|
close(atHeight)
|
|
|
|
return nil
|
|
|
|
}, nil, 1, height)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-atHeight:
|
|
|
|
case <-ctx.Done():
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// getPaychState gets the state of the payment channel with the given address
|
|
|
|
func getPaychState(ctx context.Context, t *testing.T, node test.TestNode, chAddr address.Address) paych.State {
|
|
|
|
act, err := node.StateGetActor(ctx, chAddr, types.EmptyTSK)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
store := cbor.NewCborStore(apibstore.NewAPIBlockstore(node))
|
|
|
|
var chState paych.State
|
|
|
|
err = store.Get(ctx, act.Head, &chState)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
return chState
|
|
|
|
}
|