package paychmgr

import (
	"context"
	"fmt"
	"sync"
	"testing"

	"github.com/filecoin-project/specs-actors/actors/builtin"
	"github.com/ipfs/go-cid"

	"github.com/filecoin-project/lotus/lib/sigs"
	"github.com/filecoin-project/specs-actors/actors/crypto"

	"github.com/stretchr/testify/require"

	"github.com/filecoin-project/specs-actors/actors/abi/big"

	"github.com/filecoin-project/specs-actors/actors/abi"
	tutils "github.com/filecoin-project/specs-actors/support/testing"

	"github.com/filecoin-project/specs-actors/actors/builtin/paych"

	"github.com/filecoin-project/specs-actors/actors/builtin/account"

	"github.com/filecoin-project/go-address"
	"github.com/filecoin-project/lotus/api"
	"github.com/filecoin-project/lotus/chain/types"

	ds "github.com/ipfs/go-datastore"
	ds_sync "github.com/ipfs/go-datastore/sync"
)

type testPchState struct {
	actor *types.Actor
	state paych.State
}

type mockStateManager struct {
	lk           sync.Mutex
	accountState map[address.Address]account.State
	paychState   map[address.Address]testPchState
	response     *api.InvocResult
}

func newMockStateManager() *mockStateManager {
	return &mockStateManager{
		accountState: make(map[address.Address]account.State),
		paychState:   make(map[address.Address]testPchState),
	}
}

func (sm *mockStateManager) setAccountState(a address.Address, state account.State) {
	sm.lk.Lock()
	defer sm.lk.Unlock()
	sm.accountState[a] = state
}

func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor, state paych.State) {
	sm.lk.Lock()
	defer sm.lk.Unlock()
	sm.paychState[a] = testPchState{actor, state}
}

func (sm *mockStateManager) LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) {
	sm.lk.Lock()
	defer sm.lk.Unlock()

	if outState, ok := out.(*account.State); ok {
		*outState = sm.accountState[a]
		return nil, nil
	}
	if outState, ok := out.(*paych.State); ok {
		info := sm.paychState[a]
		*outState = info.state
		return info.actor, nil
	}
	panic(fmt.Sprintf("unexpected state type %v", out))
}

func (sm *mockStateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
	return sm.response, nil
}

func TestPaychOutbound(t *testing.T) {
	ctx := context.Background()
	store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))

	ch := tutils.NewIDAddr(t, 100)
	from := tutils.NewIDAddr(t, 101)
	to := tutils.NewIDAddr(t, 102)
	fromAcct := tutils.NewIDAddr(t, 201)
	toAcct := tutils.NewIDAddr(t, 202)

	sm := newMockStateManager()
	sm.setAccountState(fromAcct, account.State{Address: from})
	sm.setAccountState(toAcct, account.State{Address: to})
	sm.setPaychState(ch, nil, paych.State{
		From:            fromAcct,
		To:              toAcct,
		ToSend:          big.NewInt(0),
		SettlingAt:      abi.ChainEpoch(0),
		MinSettleHeight: abi.ChainEpoch(0),
		LaneStates:      []*paych.LaneState{},
	})

	mgr := newManager(sm, store)
	err := mgr.TrackOutboundChannel(ctx, ch)
	require.NoError(t, err)

	ci, err := mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.Equal(t, ci.Channel, ch)
	require.Equal(t, ci.Control, from)
	require.Equal(t, ci.Target, to)
	require.EqualValues(t, ci.Direction, DirOutbound)
	require.EqualValues(t, ci.NextLane, 0)
	require.Len(t, ci.Vouchers, 0)
}

func TestPaychInbound(t *testing.T) {
	ctx := context.Background()
	store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))

	ch := tutils.NewIDAddr(t, 100)
	from := tutils.NewIDAddr(t, 101)
	to := tutils.NewIDAddr(t, 102)
	fromAcct := tutils.NewIDAddr(t, 201)
	toAcct := tutils.NewIDAddr(t, 202)

	sm := newMockStateManager()
	sm.setAccountState(fromAcct, account.State{Address: from})
	sm.setAccountState(toAcct, account.State{Address: to})
	sm.setPaychState(ch, nil, paych.State{
		From:            fromAcct,
		To:              toAcct,
		ToSend:          big.NewInt(0),
		SettlingAt:      abi.ChainEpoch(0),
		MinSettleHeight: abi.ChainEpoch(0),
		LaneStates:      []*paych.LaneState{},
	})

	mgr := newManager(sm, store)
	err := mgr.TrackInboundChannel(ctx, ch)
	require.NoError(t, err)

	ci, err := mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.Equal(t, ci.Channel, ch)
	require.Equal(t, ci.Control, to)
	require.Equal(t, ci.Target, from)
	require.EqualValues(t, ci.Direction, DirInbound)
	require.EqualValues(t, ci.NextLane, 0)
	require.Len(t, ci.Vouchers, 0)
}

func TestCheckVoucherValid(t *testing.T) {
	ctx := context.Background()

	fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
	randKeyPrivate, _ := testGenerateKeyPair(t)

	ch := tutils.NewIDAddr(t, 100)
	from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
	to := tutils.NewSECP256K1Addr(t, "secpTo")
	fromAcct := tutils.NewActorAddr(t, "fromAct")
	toAcct := tutils.NewActorAddr(t, "toAct")

	sm := newMockStateManager()
	sm.setAccountState(fromAcct, account.State{Address: from})
	sm.setAccountState(toAcct, account.State{Address: to})

	tcases := []struct {
		name          string
		expectError   bool
		key           []byte
		actorBalance  big.Int
		toSend        big.Int
		voucherAmount big.Int
		voucherLane   uint64
		voucherNonce  uint64
		laneStates    []*paych.LaneState
	}{{
		name:          "passes when voucher amount < balance",
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(5),
	}, {
		name:          "fails when funds too low",
		expectError:   true,
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(5),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(10),
	}, {
		name:          "fails when invalid signature",
		expectError:   true,
		key:           randKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(5),
	}, {
		name:          "fails when nonce too low",
		expectError:   true,
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(5),
		voucherLane:   1,
		voucherNonce:  2,
		laneStates: []*paych.LaneState{{
			ID:       1,
			Redeemed: big.NewInt(2),
			Nonce:    3,
		}},
	}, {
		name:          "passes when nonce higher",
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(5),
		voucherLane:   1,
		voucherNonce:  3,
		laneStates: []*paych.LaneState{{
			ID:       1,
			Redeemed: big.NewInt(2),
			Nonce:    2,
		}},
	}, {
		name:          "passes when nonce for different lane",
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(5),
		voucherLane:   2,
		voucherNonce:  2,
		laneStates: []*paych.LaneState{{
			ID:       1,
			Redeemed: big.NewInt(2),
			Nonce:    3,
		}},
	}, {
		name:          "fails when voucher has higher nonce but lower value than lane state",
		expectError:   true,
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(0),
		voucherAmount: big.NewInt(5),
		voucherLane:   1,
		voucherNonce:  3,
		laneStates: []*paych.LaneState{{
			ID:       1,
			Redeemed: big.NewInt(6),
			Nonce:    2,
		}},
	}, {
		name:          "fails when voucher + ToSend > balance",
		expectError:   true,
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(9),
		voucherAmount: big.NewInt(2),
	}, {
		// voucher supersedes lane 1 redeemed so
		// lane 1 effective redeemed = voucher amount
		//
		// required balance = toSend + total redeemed
		//                  = 1 + 6 (lane1)
		//                  = 7
		// So required balance: 7 < actor balance: 10
		name:          "passes when voucher + total redeemed <= balance",
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(1),
		voucherAmount: big.NewInt(6),
		voucherLane:   1,
		voucherNonce:  2,
		laneStates: []*paych.LaneState{{
			ID:       1, // Lane 1 (same as voucher lane 1)
			Redeemed: big.NewInt(4),
			Nonce:    1,
		}},
	}, {
		// required balance = toSend + total redeemed
		//                  = 1 + 4 (lane 2) + 6 (voucher lane 1)
		//                  = 11
		// So required balance: 11 > actor balance: 10
		name:          "fails when voucher + total redeemed > balance",
		expectError:   true,
		key:           fromKeyPrivate,
		actorBalance:  big.NewInt(10),
		toSend:        big.NewInt(1),
		voucherAmount: big.NewInt(6),
		voucherLane:   1,
		voucherNonce:  1,
		laneStates: []*paych.LaneState{{
			ID:       2, // Lane 2 (different from voucher lane 1)
			Redeemed: big.NewInt(4),
			Nonce:    1,
		}},
	}}

	for _, tcase := range tcases {
		tcase := tcase
		t.Run(tcase.name, func(t *testing.T) {
			store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))

			act := &types.Actor{
				Code:    builtin.AccountActorCodeID,
				Head:    cid.Cid{},
				Nonce:   0,
				Balance: tcase.actorBalance,
			}
			sm.setPaychState(ch, act, paych.State{
				From:            fromAcct,
				To:              toAcct,
				ToSend:          tcase.toSend,
				SettlingAt:      abi.ChainEpoch(0),
				MinSettleHeight: abi.ChainEpoch(0),
				LaneStates:      tcase.laneStates,
			})

			mgr := newManager(sm, store)
			err := mgr.TrackInboundChannel(ctx, ch)
			require.NoError(t, err)

			sv := testCreateVoucher(t, ch, tcase.voucherLane, tcase.voucherNonce, tcase.voucherAmount, tcase.key)

			err = mgr.CheckVoucherValid(ctx, ch, sv)
			if tcase.expectError {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
			}
		})
	}
}

func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
	ctx := context.Background()

	fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)

	ch := tutils.NewIDAddr(t, 100)
	from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
	to := tutils.NewSECP256K1Addr(t, "secpTo")
	fromAcct := tutils.NewActorAddr(t, "fromAct")
	toAcct := tutils.NewActorAddr(t, "toAct")
	minDelta := big.NewInt(0)

	sm := newMockStateManager()
	sm.setAccountState(fromAcct, account.State{Address: from})
	sm.setAccountState(toAcct, account.State{Address: to})

	store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))

	actorBalance := big.NewInt(10)
	toSend := big.NewInt(1)
	laneStates := []*paych.LaneState{{
		ID:       1,
		Nonce:    1,
		Redeemed: big.NewInt(3),
	}, {
		ID:       2,
		Nonce:    1,
		Redeemed: big.NewInt(4),
	}}

	act := &types.Actor{
		Code:    builtin.AccountActorCodeID,
		Head:    cid.Cid{},
		Nonce:   0,
		Balance: actorBalance,
	}
	sm.setPaychState(ch, act, paych.State{
		From:            fromAcct,
		To:              toAcct,
		ToSend:          toSend,
		SettlingAt:      abi.ChainEpoch(0),
		MinSettleHeight: abi.ChainEpoch(0),
		LaneStates:      laneStates,
	})

	mgr := newManager(sm, store)
	err := mgr.TrackInboundChannel(ctx, ch)
	require.NoError(t, err)

	//
	// Should not be possible to add a voucher with a value such that
	// <total lane Redeemed> + toSend > <actor balance>
	//
	// lane 1 redeemed:                   3
	// voucher amount (lane 1):           6
	// lane 1 redeemed (with voucher):    6
	//
	// Lane 1:             6
	// Lane 2:             4
	// toSend:             1
	//                     --
	// total:              11
	//
	// actor balance is 10 so total is too high.
	//
	voucherLane := uint64(1)
	voucherNonce := uint64(2)
	voucherAmount := big.NewInt(6)
	sv := testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
	err = mgr.CheckVoucherValid(ctx, ch, sv)
	require.Error(t, err)

	//
	// lane 1 redeemed:                   3
	// voucher amount (lane 1):           4
	// lane 1 redeemed (with voucher):    4
	//
	// Lane 1:             4
	// Lane 2:             4
	// toSend:             1
	//                     --
	// total:              9
	//
	// actor balance is 10 so total is ok.
	//
	voucherAmount = big.NewInt(4)
	sv = testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
	err = mgr.CheckVoucherValid(ctx, ch, sv)
	require.NoError(t, err)

	// Add voucher to lane 1, so Lane 1 effective redeemed
	// (with first voucher) is now 4
	_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)

	//
	// lane 1 redeemed:                   4
	// voucher amount (lane 1):           6
	// lane 1 redeemed (with voucher):    6
	//
	// Lane 1:             6
	// Lane 2:             4
	// toSend:             1
	//                     --
	// total:              11
	//
	// actor balance is 10 so total is too high.
	//
	voucherNonce++
	voucherAmount = big.NewInt(6)
	sv = testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
	err = mgr.CheckVoucherValid(ctx, ch, sv)
	require.Error(t, err)

	//
	// lane 1 redeemed:                   4
	// voucher amount (lane 1):           5
	// lane 1 redeemed (with voucher):    5
	//
	// Lane 1:             5
	// Lane 2:             4
	// toSend:             1
	//                     --
	// total:              10
	//
	// actor balance is 10 so total is ok.
	//
	voucherAmount = big.NewInt(5)
	sv = testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
	err = mgr.CheckVoucherValid(ctx, ch, sv)
	require.NoError(t, err)
}

func TestAddVoucherDelta(t *testing.T) {
	ctx := context.Background()

	// Set up a manager with a single payment channel
	mgr, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)

	voucherLane := uint64(1)

	// Expect error when adding a voucher whose amount is less than minDelta
	minDelta := big.NewInt(2)
	nonce := uint64(1)
	voucherAmount := big.NewInt(1)
	sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.Error(t, err)

	// Expect success when adding a voucher whose amount is equal to minDelta
	nonce++
	voucherAmount = big.NewInt(2)
	sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	delta, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)
	require.EqualValues(t, delta.Int64(), 2)

	// Check that delta is correct when there's an existing voucher
	nonce++
	voucherAmount = big.NewInt(5)
	sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	delta, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)
	require.EqualValues(t, delta.Int64(), 3)

	// Check that delta is correct when voucher added to a different lane
	nonce = uint64(1)
	voucherAmount = big.NewInt(6)
	voucherLane = uint64(2)
	sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	delta, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)
	require.EqualValues(t, delta.Int64(), 6)
}

func TestAddVoucherNextLane(t *testing.T) {
	ctx := context.Background()

	// Set up a manager with a single payment channel
	mgr, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)

	minDelta := big.NewInt(0)
	voucherAmount := big.NewInt(2)

	// Add a voucher in lane 2
	nonce := uint64(1)
	voucherLane := uint64(2)
	sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)

	ci, err := mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.EqualValues(t, ci.NextLane, 3)

	// Add a voucher in lane 1
	voucherLane = uint64(1)
	sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)

	ci, err = mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.EqualValues(t, ci.NextLane, 3)

	// Add a voucher in lane 5
	voucherLane = uint64(5)
	sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)

	ci, err = mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.EqualValues(t, ci.NextLane, 6)
}

func TestAddVoucherProof(t *testing.T) {
	ctx := context.Background()

	// Set up a manager with a single payment channel
	mgr, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)

	nonce := uint64(1)
	voucherAmount := big.NewInt(1)
	minDelta := big.NewInt(0)
	voucherAmount = big.NewInt(2)
	voucherLane := uint64(1)

	// Add a voucher with no proof
	var proof []byte
	sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
	_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)

	// Expect one voucher with no proof
	ci, err := mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.Len(t, ci.Vouchers, 1)
	require.Len(t, ci.Vouchers[0].Proof, 0)

	// Add same voucher with no proof
	voucherLane = uint64(1)
	_, err = mgr.AddVoucher(ctx, ch, sv, proof, minDelta)
	require.NoError(t, err)

	// Expect one voucher with no proof
	ci, err = mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.Len(t, ci.Vouchers, 1)
	require.Len(t, ci.Vouchers[0].Proof, 0)

	// Add same voucher with proof
	proof = []byte{1}
	_, err = mgr.AddVoucher(ctx, ch, sv, proof, minDelta)
	require.NoError(t, err)

	// Should add proof to existing voucher
	ci, err = mgr.GetChannelInfo(ch)
	require.NoError(t, err)
	require.Len(t, ci.Vouchers, 1)
	require.Len(t, ci.Vouchers[0].Proof, 1)
}

func TestAllocateLane(t *testing.T) {
	ctx := context.Background()

	// Set up a manager with a single payment channel
	mgr, ch, _ := testSetupMgrWithChannel(ctx, t)

	// First lane should be 0
	lane, err := mgr.AllocateLane(ch)
	require.NoError(t, err)
	require.EqualValues(t, lane, 0)

	// Next lane should be 1
	lane, err = mgr.AllocateLane(ch)
	require.NoError(t, err)
	require.EqualValues(t, lane, 1)
}

func TestNextNonceForLane(t *testing.T) {
	ctx := context.Background()

	// Set up a manager with a single payment channel
	mgr, ch, key := testSetupMgrWithChannel(ctx, t)

	// Expect next nonce for non-existent lane to be 1
	next, err := mgr.NextNonceForLane(ctx, ch, 1)
	require.NoError(t, err)
	require.EqualValues(t, next, 1)

	voucherAmount := big.NewInt(1)
	minDelta := big.NewInt(0)
	voucherAmount = big.NewInt(2)

	// Add vouchers such that we have
	// lane 1: nonce 2
	// lane 1: nonce 4
	// lane 2: nonce 7
	voucherLane := uint64(1)
	for _, nonce := range []uint64{2, 4} {
		voucherAmount = big.Add(voucherAmount, big.NewInt(1))
		sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, key)
		_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
		require.NoError(t, err)
	}

	voucherLane = uint64(2)
	nonce := uint64(7)
	sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, key)
	_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta)
	require.NoError(t, err)

	// Expect next nonce for lane 1 to be 5
	next, err = mgr.NextNonceForLane(ctx, ch, 1)
	require.NoError(t, err)
	require.EqualValues(t, next, 5)

	// Expect next nonce for lane 2 to be 8
	next, err = mgr.NextNonceForLane(ctx, ch, 2)
	require.NoError(t, err)
	require.EqualValues(t, next, 8)
}

func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, address.Address, []byte) {
	fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)

	ch := tutils.NewIDAddr(t, 100)
	from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
	to := tutils.NewSECP256K1Addr(t, "secpTo")
	fromAcct := tutils.NewActorAddr(t, "fromAct")
	toAcct := tutils.NewActorAddr(t, "toAct")

	sm := newMockStateManager()
	sm.setAccountState(fromAcct, account.State{Address: from})
	sm.setAccountState(toAcct, account.State{Address: to})

	act := &types.Actor{
		Code:    builtin.AccountActorCodeID,
		Head:    cid.Cid{},
		Nonce:   0,
		Balance: big.NewInt(20),
	}
	sm.setPaychState(ch, act, paych.State{
		From:            fromAcct,
		To:              toAcct,
		ToSend:          big.NewInt(0),
		SettlingAt:      abi.ChainEpoch(0),
		MinSettleHeight: abi.ChainEpoch(0),
		LaneStates:      []*paych.LaneState{},
	})

	store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
	mgr := newManager(sm, store)
	err := mgr.TrackInboundChannel(ctx, ch)
	require.NoError(t, err)
	return mgr, ch, fromKeyPrivate
}

func testGenerateKeyPair(t *testing.T) ([]byte, []byte) {
	priv, err := sigs.Generate(crypto.SigTypeSecp256k1)
	require.NoError(t, err)
	pub, err := sigs.ToPublic(crypto.SigTypeSecp256k1, priv)
	require.NoError(t, err)
	return priv, pub
}

func testCreateVoucher(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *paych.SignedVoucher {
	sv := &paych.SignedVoucher{
		ChannelAddr: ch,
		Lane:        voucherLane,
		Nonce:       nonce,
		Amount:      voucherAmount,
	}

	signingBytes, err := sv.SigningBytes()
	require.NoError(t, err)
	sig, err := sigs.Sign(crypto.SigTypeSecp256k1, key, signingBytes)
	require.NoError(t, err)
	sv.Signature = sig
	return sv
}