Merge pull request #3821 from filecoin-project/fix/paych-voucher-verify
paych: fix voucher amount verification
This commit is contained in:
commit
d3effe52a5
@ -67,7 +67,7 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
channelAmt := int64(100000)
|
||||
channelAmt := int64(7000)
|
||||
channelInfo, err := paymentCreator.PaychGet(ctx, createrAddr, receiverAddr, abi.NewTokenAmount(channelAmt))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -169,6 +169,51 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
t.Fatal("Timed out waiting for receiver to submit vouchers")
|
||||
}
|
||||
|
||||
// Create a new voucher now that some vouchers have already been submitted
|
||||
vouchRes, err := paymentCreator.PaychVoucherCreate(ctx, channel, abi.NewTokenAmount(1000), 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if vouchRes.Voucher == nil {
|
||||
t.Fatal(fmt.Errorf("Not enough funds to create voucher: missing %d", vouchRes.Shortfall))
|
||||
}
|
||||
vdelta, err := paymentReceiver.PaychVoucherAdd(ctx, channel, vouchRes.Voucher, nil, abi.NewTokenAmount(1000))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !vdelta.Equals(abi.NewTokenAmount(1000)) {
|
||||
t.Fatal("voucher didn't have the right amount")
|
||||
}
|
||||
|
||||
// Create a new voucher whose value would exceed the channel balance
|
||||
excessAmt := abi.NewTokenAmount(1000)
|
||||
vouchRes, err = paymentCreator.PaychVoucherCreate(ctx, channel, excessAmt, 4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if vouchRes.Voucher != nil {
|
||||
t.Fatal("Expected not to be able to create voucher whose value would exceed channel balance")
|
||||
}
|
||||
if !vouchRes.Shortfall.Equals(excessAmt) {
|
||||
t.Fatal(fmt.Errorf("Expected voucher shortfall of %d, got %d", excessAmt, vouchRes.Shortfall))
|
||||
}
|
||||
|
||||
// Add a voucher whose value would exceed the channel balance
|
||||
vouch := &paych.SignedVoucher{ChannelAddr: channel, Amount: excessAmt, Lane: 4, Nonce: 1}
|
||||
vb, err := vouch.SigningBytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sig, err := paymentCreator.WalletSign(ctx, createrAddr, vb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vouch.Signature = sig
|
||||
_, err = paymentReceiver.PaychVoucherAdd(ctx, channel, vouch, nil, abi.NewTokenAmount(1000))
|
||||
if err == nil {
|
||||
t.Fatal(fmt.Errorf("Expected shortfall error of %d", excessAmt))
|
||||
}
|
||||
|
||||
// wait for the settlement period to pass before collecting
|
||||
waitForBlocks(ctx, t, bm, paymentReceiver, receiverAddr, paych.SettleDelay)
|
||||
|
||||
|
@ -27,10 +27,9 @@ type mockLaneState struct {
|
||||
func NewMockPayChState(from address.Address,
|
||||
to address.Address,
|
||||
settlingAt abi.ChainEpoch,
|
||||
toSend abi.TokenAmount,
|
||||
lanes map[uint64]paych.LaneState,
|
||||
) paych.State {
|
||||
return &mockState{from, to, settlingAt, toSend, lanes}
|
||||
return &mockState{from: from, to: to, settlingAt: settlingAt, toSend: big.NewInt(0), lanes: lanes}
|
||||
}
|
||||
|
||||
// NewMockLaneState constructs a state for a payment channel lane with the set fixed values
|
||||
|
@ -205,7 +205,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
|
||||
}
|
||||
|
||||
// Check the voucher against the highest known voucher nonce / value
|
||||
laneStates, err := ca.laneState(ctx, pchState, ch)
|
||||
laneStates, err := ca.laneState(pchState, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -253,16 +253,9 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Total required balance = total redeemed + toSend
|
||||
// Must not exceed actor balance
|
||||
ts, err := pchState.ToSend()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newTotal := types.BigAdd(totalRedeemed, ts)
|
||||
if act.Balance.LessThan(newTotal) {
|
||||
return nil, newErrInsufficientFunds(types.BigSub(newTotal, act.Balance))
|
||||
// Total required balance must not exceed actor balance
|
||||
if act.Balance.LessThan(totalRedeemed) {
|
||||
return nil, newErrInsufficientFunds(types.BigSub(totalRedeemed, act.Balance))
|
||||
}
|
||||
|
||||
if len(sv.Merges) != 0 {
|
||||
@ -505,7 +498,6 @@ func (ca *channelAccessor) allocateLane(ch address.Address) (uint64, error) {
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
|
||||
// TODO: should this take into account lane state?
|
||||
return ca.store.AllocateLane(ch)
|
||||
}
|
||||
|
||||
@ -520,7 +512,7 @@ func (ca *channelAccessor) listVouchers(ctx context.Context, ch address.Address)
|
||||
|
||||
// laneState gets the LaneStates from chain, then applies all vouchers in
|
||||
// the data store over the chain state
|
||||
func (ca *channelAccessor) laneState(ctx context.Context, state paych.State, ch address.Address) (map[uint64]paych.LaneState, error) {
|
||||
func (ca *channelAccessor) laneState(state paych.State, ch address.Address) (map[uint64]paych.LaneState, error) {
|
||||
// TODO: we probably want to call UpdateChannelState with all vouchers to be fully correct
|
||||
// (but technically dont't need to)
|
||||
|
||||
@ -552,9 +544,12 @@ func (ca *channelAccessor) laneState(ctx context.Context, state paych.State, ch
|
||||
return nil, xerrors.Errorf("paych merges not handled yet")
|
||||
}
|
||||
|
||||
// If there's a voucher for a lane that isn't in chain state just
|
||||
// create it
|
||||
// Check if there is an existing laneState in the payment channel
|
||||
// for this voucher's lane
|
||||
ls, ok := laneStates[v.Voucher.Lane]
|
||||
|
||||
// If the voucher does not have a higher nonce than the existing
|
||||
// laneState for this lane, ignore it
|
||||
if ok {
|
||||
n, err := ls.Nonce()
|
||||
if err != nil {
|
||||
@ -565,6 +560,7 @@ func (ca *channelAccessor) laneState(ctx context.Context, state paych.State, ch
|
||||
}
|
||||
}
|
||||
|
||||
// Voucher has a higher nonce, so replace laneState with this voucher
|
||||
laneStates[v.Voucher.Lane] = laneState{v.Voucher.Amount, v.Voucher.Nonce}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,6 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
expectError bool
|
||||
key []byte
|
||||
actorBalance big.Int
|
||||
toSend big.Int
|
||||
voucherAmount big.Int
|
||||
voucherLane uint64
|
||||
voucherNonce uint64
|
||||
@ -56,35 +55,30 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
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 signed by channel To account (instead of From account)",
|
||||
expectError: true,
|
||||
key: toKeyPrivate,
|
||||
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,
|
||||
@ -95,7 +89,6 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
name: "passes when nonce higher",
|
||||
key: fromKeyPrivate,
|
||||
actorBalance: big.NewInt(10),
|
||||
toSend: big.NewInt(0),
|
||||
voucherAmount: big.NewInt(5),
|
||||
voucherLane: 1,
|
||||
voucherNonce: 3,
|
||||
@ -106,7 +99,6 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
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,
|
||||
@ -118,32 +110,22 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
expectError: true,
|
||||
key: fromKeyPrivate,
|
||||
actorBalance: big.NewInt(10),
|
||||
toSend: big.NewInt(0),
|
||||
voucherAmount: big.NewInt(5),
|
||||
voucherLane: 1,
|
||||
voucherNonce: 3,
|
||||
laneStates: map[uint64]paych.LaneState{
|
||||
1: paychmock.NewMockLaneState(big.NewInt(6), 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)
|
||||
// required balance = voucher amt
|
||||
// = 7
|
||||
// So required balance: 7 < actor balance: 10
|
||||
name: "passes when voucher + total redeemed <= balance",
|
||||
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,
|
||||
@ -152,29 +134,68 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
1: paychmock.NewMockLaneState(big.NewInt(4), 1),
|
||||
},
|
||||
}, {
|
||||
// required balance = toSend + total redeemed
|
||||
// = 1 + 4 (lane 2) + 6 (voucher lane 1)
|
||||
// required balance = total redeemed
|
||||
// = 6 (voucher lane 1) + 5 (lane 2)
|
||||
// = 11
|
||||
// So required balance: 11 > actor balance: 10
|
||||
name: "fails when voucher + total redeemed > balance",
|
||||
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: map[uint64]paych.LaneState{
|
||||
// Lane 2 (different from voucher lane 1)
|
||||
2: paychmock.NewMockLaneState(big.NewInt(4), 1),
|
||||
2: paychmock.NewMockLaneState(big.NewInt(5), 1),
|
||||
},
|
||||
}, {
|
||||
// voucher supersedes lane 1 redeemed so
|
||||
// lane 1 effective redeemed = voucher amount
|
||||
//
|
||||
// required balance = total redeemed
|
||||
// = 6 (new voucher lane 1) + 5 (lane 2)
|
||||
// = 11
|
||||
// So required balance: 11 > actor balance: 10
|
||||
name: "fails when voucher total redeemed > balance",
|
||||
expectError: true,
|
||||
key: fromKeyPrivate,
|
||||
actorBalance: big.NewInt(10),
|
||||
voucherAmount: big.NewInt(6),
|
||||
voucherLane: 1,
|
||||
voucherNonce: 2,
|
||||
laneStates: map[uint64]paych.LaneState{
|
||||
// Lane 1 (superseded by new voucher in voucher lane 1)
|
||||
1: paychmock.NewMockLaneState(big.NewInt(5), 1),
|
||||
// Lane 2 (different from voucher lane 1)
|
||||
2: paychmock.NewMockLaneState(big.NewInt(5), 1),
|
||||
},
|
||||
}, {
|
||||
// voucher supersedes lane 1 redeemed so
|
||||
// lane 1 effective redeemed = voucher amount
|
||||
//
|
||||
// required balance = total redeemed
|
||||
// = 5 (new voucher lane 1) + 5 (lane 2)
|
||||
// = 10
|
||||
// So required balance: 10 <= actor balance: 10
|
||||
name: "passes when voucher total redeemed <= balance",
|
||||
expectError: false,
|
||||
key: fromKeyPrivate,
|
||||
actorBalance: big.NewInt(10),
|
||||
voucherAmount: big.NewInt(5),
|
||||
voucherLane: 1,
|
||||
voucherNonce: 2,
|
||||
laneStates: map[uint64]paych.LaneState{
|
||||
// Lane 1 (superseded by new voucher in voucher lane 1)
|
||||
1: paychmock.NewMockLaneState(big.NewInt(4), 1),
|
||||
// Lane 2 (different from voucher lane 1)
|
||||
2: paychmock.NewMockLaneState(big.NewInt(5), 1),
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tcase := range tcases {
|
||||
tcase := tcase
|
||||
t.Run(tcase.name, func(t *testing.T) {
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
|
||||
// Create an actor for the channel with the test case balance
|
||||
act := &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
@ -184,16 +205,17 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
}
|
||||
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(
|
||||
fromAcct, toAcct, abi.ChainEpoch(0), tcase.toSend, tcase.laneStates))
|
||||
fromAcct, toAcct, abi.ChainEpoch(0), tcase.laneStates))
|
||||
|
||||
// Create a manager
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
mgr, err := newManager(store, mock)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add channel To address to wallet
|
||||
mock.addWalletAddress(to)
|
||||
|
||||
// Create a signed voucher
|
||||
// Create the test case signed voucher
|
||||
sv := createTestVoucher(t, ch, tcase.voucherLane, tcase.voucherNonce, tcase.voucherAmount, tcase.key)
|
||||
|
||||
// Check the voucher's validity
|
||||
@ -207,135 +229,11 @@ func TestCheckVoucherValid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
mock := newMockManagerAPI()
|
||||
mock.setAccountAddress(fromAcct, from)
|
||||
mock.setAccountAddress(toAcct, to)
|
||||
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
|
||||
actorBalance := big.NewInt(10)
|
||||
toSend := big.NewInt(1)
|
||||
laneStates := map[uint64]paych.LaneState{
|
||||
1: paychmock.NewMockLaneState(big.NewInt(3), 1),
|
||||
2: paychmock.NewMockLaneState(big.NewInt(4), 1),
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Head: cid.Cid{},
|
||||
Nonce: 0,
|
||||
Balance: actorBalance,
|
||||
}
|
||||
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), toSend, laneStates))
|
||||
|
||||
mgr, err := newManager(store, mock)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add channel To address to wallet
|
||||
mock.addWalletAddress(to)
|
||||
|
||||
//
|
||||
// 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 := createTestVoucher(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 = createTestVoucher(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.AddVoucherOutbound(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 = createTestVoucher(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 = createTestVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
|
||||
err = mgr.CheckVoucherValid(ctx, ch, sv)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCreateVoucher(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
// Create a voucher in lane 1
|
||||
voucherLane1Amt := big.NewInt(5)
|
||||
@ -400,7 +298,7 @@ func TestAddVoucherDelta(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
voucherLane := uint64(1)
|
||||
|
||||
@ -442,7 +340,7 @@ func TestAddVoucherNextLane(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
minDelta := big.NewInt(0)
|
||||
voucherAmount := big.NewInt(2)
|
||||
@ -489,10 +387,8 @@ func TestAddVoucherNextLane(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAllocateLane(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
// First lane should be 0
|
||||
lane, err := s.mgr.AllocateLane(s.ch)
|
||||
@ -525,7 +421,6 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
|
||||
|
||||
// Create a channel that will be retrieved from state
|
||||
actorBalance := big.NewInt(10)
|
||||
toSend := big.NewInt(1)
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
@ -534,7 +429,7 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
|
||||
Balance: actorBalance,
|
||||
}
|
||||
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), toSend, make(map[uint64]paych.LaneState)))
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), make(map[uint64]paych.LaneState)))
|
||||
|
||||
mgr, err := newManager(store, mock)
|
||||
require.NoError(t, err)
|
||||
@ -559,7 +454,7 @@ func TestAddVoucherProof(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
nonce := uint64(1)
|
||||
voucherAmount := big.NewInt(1)
|
||||
@ -622,10 +517,11 @@ func TestAddVoucherInboundWalletKey(t *testing.T) {
|
||||
}
|
||||
|
||||
mock := newMockManagerAPI()
|
||||
|
||||
mock.setAccountAddress(fromAcct, from)
|
||||
mock.setAccountAddress(toAcct, to)
|
||||
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), types.NewInt(0), make(map[uint64]paych.LaneState)))
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), make(map[uint64]paych.LaneState)))
|
||||
|
||||
// Create a manager
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
@ -660,7 +556,7 @@ func TestBestSpendable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
// Add vouchers to lane 1 with amounts: [1, 2, 3]
|
||||
voucherLane := uint64(1)
|
||||
@ -740,7 +636,7 @@ func TestCheckSpendable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
// Create voucher with Extra
|
||||
voucherLane := uint64(1)
|
||||
@ -821,7 +717,7 @@ func TestSubmitVoucher(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(t)
|
||||
|
||||
// Create voucher with Extra
|
||||
voucherLane := uint64(1)
|
||||
@ -908,7 +804,7 @@ type testScaffold struct {
|
||||
fromKeyPrivate []byte
|
||||
}
|
||||
|
||||
func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold {
|
||||
func testSetupMgrWithChannel(t *testing.T) *testScaffold {
|
||||
fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
|
||||
|
||||
ch := tutils.NewIDAddr(t, 100)
|
||||
@ -929,7 +825,7 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold {
|
||||
Nonce: 0,
|
||||
Balance: balance,
|
||||
}
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), big.NewInt(0), make(map[uint64]paych.LaneState)))
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), make(map[uint64]paych.LaneState)))
|
||||
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
mgr, err := newManager(store, mock)
|
||||
|
@ -978,7 +978,7 @@ func TestPaychAvailableFunds(t *testing.T) {
|
||||
Nonce: 0,
|
||||
Balance: createAmt,
|
||||
}
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), big.NewInt(0), make(map[uint64]paych.LaneState)))
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), make(map[uint64]paych.LaneState)))
|
||||
// Send create channel response
|
||||
response := testChannelResponse(t, ch)
|
||||
mock.receiveMsgResponse(createMsgCid, response)
|
||||
|
103
paychmgr/paychvoucherfunds_test.go
Normal file
103
paychmgr/paychvoucherfunds_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
package paychmgr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
|
||||
paychmock "github.com/filecoin-project/lotus/chain/actors/builtin/paych/mock"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
tutils "github.com/filecoin-project/specs-actors/support/testing"
|
||||
"github.com/ipfs/go-cid"
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
ds_sync "github.com/ipfs/go-datastore/sync"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestPaychAddVoucherAfterAddFunds tests adding a voucher to a channel with
|
||||
// insufficient funds, then adding funds to the channel, then adding the
|
||||
// voucher again
|
||||
func TestPaychAddVoucherAfterAddFunds(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
|
||||
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")
|
||||
|
||||
mock := newMockManagerAPI()
|
||||
defer mock.close()
|
||||
|
||||
// Add the from signing key to the wallet
|
||||
mock.setAccountAddress(fromAcct, from)
|
||||
mock.setAccountAddress(toAcct, to)
|
||||
mock.addSigningKey(fromKeyPrivate)
|
||||
|
||||
mgr, err := newManager(store, mock)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Send create message for a channel with value 10
|
||||
createAmt := big.NewInt(10)
|
||||
_, createMsgCid, err := mgr.GetPaych(ctx, from, to, createAmt)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Send create channel response
|
||||
response := testChannelResponse(t, ch)
|
||||
mock.receiveMsgResponse(createMsgCid, response)
|
||||
|
||||
// Create an actor in state for the channel with the initial channel balance
|
||||
act := &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Head: cid.Cid{},
|
||||
Nonce: 0,
|
||||
Balance: createAmt,
|
||||
}
|
||||
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), make(map[uint64]paych.LaneState)))
|
||||
|
||||
// Wait for create response to be processed by manager
|
||||
_, err = mgr.GetPaychWaitReady(ctx, createMsgCid)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a voucher with a value equal to the channel balance
|
||||
voucher := paych.SignedVoucher{Amount: createAmt, Lane: 1}
|
||||
res, err := mgr.CreateVoucher(ctx, ch, voucher)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.Voucher)
|
||||
|
||||
// Create a voucher in a different lane with an amount that exceeds the
|
||||
// channel balance
|
||||
excessAmt := types.NewInt(5)
|
||||
voucher = paych.SignedVoucher{Amount: excessAmt, Lane: 2}
|
||||
res, err = mgr.CreateVoucher(ctx, ch, voucher)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, res.Voucher)
|
||||
require.Equal(t, res.Shortfall, excessAmt)
|
||||
|
||||
// Add funds so as to cover the voucher shortfall
|
||||
_, addFundsMsgCid, err := mgr.GetPaych(ctx, from, to, excessAmt)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Trigger add funds confirmation
|
||||
mock.receiveMsgResponse(addFundsMsgCid, types.MessageReceipt{ExitCode: 0})
|
||||
|
||||
// Update actor test case balance to reflect added funds
|
||||
act.Balance = types.BigAdd(createAmt, excessAmt)
|
||||
|
||||
// Wait for add funds confirmation to be processed by manager
|
||||
_, err = mgr.GetPaychWaitReady(ctx, addFundsMsgCid)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Adding same voucher that previously exceeded channel balance
|
||||
// should succeed now that the channel balance has been increased
|
||||
res, err = mgr.CreateVoucher(ctx, ch, voucher)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.Voucher)
|
||||
}
|
@ -312,7 +312,7 @@ func (ca *channelAccessor) currentAvailableFunds(channelID string, queuedAmt typ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
laneStates, err := ca.laneState(ca.chctx, pchState, ch)
|
||||
laneStates, err := ca.laneState(pchState, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user