Merge pull request #3821 from filecoin-project/fix/paych-voucher-verify

paych: fix voucher amount verification
This commit is contained in:
Łukasz Magiera 2020-10-02 23:27:18 +02:00 committed by GitHub
commit d3effe52a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 227 additions and 188 deletions

View File

@ -67,7 +67,7 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
t.Fatal(err) t.Fatal(err)
} }
channelAmt := int64(100000) channelAmt := int64(7000)
channelInfo, err := paymentCreator.PaychGet(ctx, createrAddr, receiverAddr, abi.NewTokenAmount(channelAmt)) channelInfo, err := paymentCreator.PaychGet(ctx, createrAddr, receiverAddr, abi.NewTokenAmount(channelAmt))
if err != nil { if err != nil {
t.Fatal(err) 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") 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 // wait for the settlement period to pass before collecting
waitForBlocks(ctx, t, bm, paymentReceiver, receiverAddr, paych.SettleDelay) waitForBlocks(ctx, t, bm, paymentReceiver, receiverAddr, paych.SettleDelay)

View File

@ -27,10 +27,9 @@ type mockLaneState struct {
func NewMockPayChState(from address.Address, func NewMockPayChState(from address.Address,
to address.Address, to address.Address,
settlingAt abi.ChainEpoch, settlingAt abi.ChainEpoch,
toSend abi.TokenAmount,
lanes map[uint64]paych.LaneState, lanes map[uint64]paych.LaneState,
) paych.State { ) 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 // NewMockLaneState constructs a state for a payment channel lane with the set fixed values

View File

@ -205,7 +205,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
} }
// Check the voucher against the highest known voucher nonce / value // 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 { if err != nil {
return nil, err return nil, err
} }
@ -253,16 +253,9 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
return nil, err return nil, err
} }
// Total required balance = total redeemed + toSend // Total required balance must not exceed actor balance
// Must not exceed actor balance if act.Balance.LessThan(totalRedeemed) {
ts, err := pchState.ToSend() return nil, newErrInsufficientFunds(types.BigSub(totalRedeemed, act.Balance))
if err != nil {
return nil, err
}
newTotal := types.BigAdd(totalRedeemed, ts)
if act.Balance.LessThan(newTotal) {
return nil, newErrInsufficientFunds(types.BigSub(newTotal, act.Balance))
} }
if len(sv.Merges) != 0 { if len(sv.Merges) != 0 {
@ -505,7 +498,6 @@ func (ca *channelAccessor) allocateLane(ch address.Address) (uint64, error) {
ca.lk.Lock() ca.lk.Lock()
defer ca.lk.Unlock() defer ca.lk.Unlock()
// TODO: should this take into account lane state?
return ca.store.AllocateLane(ch) 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 // laneState gets the LaneStates from chain, then applies all vouchers in
// the data store over the chain state // 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 // TODO: we probably want to call UpdateChannelState with all vouchers to be fully correct
// (but technically dont't need to) // (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") return nil, xerrors.Errorf("paych merges not handled yet")
} }
// If there's a voucher for a lane that isn't in chain state just // Check if there is an existing laneState in the payment channel
// create it // for this voucher's lane
ls, ok := laneStates[v.Voucher.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 { if ok {
n, err := ls.Nonce() n, err := ls.Nonce()
if err != nil { 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} laneStates[v.Voucher.Lane] = laneState{v.Voucher.Amount, v.Voucher.Nonce}
} }

View File

@ -47,7 +47,6 @@ func TestCheckVoucherValid(t *testing.T) {
expectError bool expectError bool
key []byte key []byte
actorBalance big.Int actorBalance big.Int
toSend big.Int
voucherAmount big.Int voucherAmount big.Int
voucherLane uint64 voucherLane uint64
voucherNonce uint64 voucherNonce uint64
@ -56,35 +55,30 @@ func TestCheckVoucherValid(t *testing.T) {
name: "passes when voucher amount < balance", name: "passes when voucher amount < balance",
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
}, { }, {
name: "fails when funds too low", name: "fails when funds too low",
expectError: true, expectError: true,
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(5), actorBalance: big.NewInt(5),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(10), voucherAmount: big.NewInt(10),
}, { }, {
name: "fails when invalid signature", name: "fails when invalid signature",
expectError: true, expectError: true,
key: randKeyPrivate, key: randKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
}, { }, {
name: "fails when signed by channel To account (instead of From account)", name: "fails when signed by channel To account (instead of From account)",
expectError: true, expectError: true,
key: toKeyPrivate, key: toKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
}, { }, {
name: "fails when nonce too low", name: "fails when nonce too low",
expectError: true, expectError: true,
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
voucherLane: 1, voucherLane: 1,
voucherNonce: 2, voucherNonce: 2,
@ -95,7 +89,6 @@ func TestCheckVoucherValid(t *testing.T) {
name: "passes when nonce higher", name: "passes when nonce higher",
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
voucherLane: 1, voucherLane: 1,
voucherNonce: 3, voucherNonce: 3,
@ -106,7 +99,6 @@ func TestCheckVoucherValid(t *testing.T) {
name: "passes when nonce for different lane", name: "passes when nonce for different lane",
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
voucherLane: 2, voucherLane: 2,
voucherNonce: 2, voucherNonce: 2,
@ -118,32 +110,22 @@ func TestCheckVoucherValid(t *testing.T) {
expectError: true, expectError: true,
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
voucherLane: 1, voucherLane: 1,
voucherNonce: 3, voucherNonce: 3,
laneStates: map[uint64]paych.LaneState{ laneStates: map[uint64]paych.LaneState{
1: paychmock.NewMockLaneState(big.NewInt(6), 2), 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 // voucher supersedes lane 1 redeemed so
// lane 1 effective redeemed = voucher amount // lane 1 effective redeemed = voucher amount
// //
// required balance = toSend + total redeemed // required balance = voucher amt
// = 1 + 6 (lane1)
// = 7 // = 7
// So required balance: 7 < actor balance: 10 // So required balance: 7 < actor balance: 10
name: "passes when voucher + total redeemed <= balance", name: "passes when voucher total redeemed <= balance",
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(1),
voucherAmount: big.NewInt(6), voucherAmount: big.NewInt(6),
voucherLane: 1, voucherLane: 1,
voucherNonce: 2, voucherNonce: 2,
@ -152,29 +134,68 @@ func TestCheckVoucherValid(t *testing.T) {
1: paychmock.NewMockLaneState(big.NewInt(4), 1), 1: paychmock.NewMockLaneState(big.NewInt(4), 1),
}, },
}, { }, {
// required balance = toSend + total redeemed // required balance = total redeemed
// = 1 + 4 (lane 2) + 6 (voucher lane 1) // = 6 (voucher lane 1) + 5 (lane 2)
// = 11 // = 11
// So required balance: 11 > actor balance: 10 // So required balance: 11 > actor balance: 10
name: "fails when voucher + total redeemed > balance", name: "fails when voucher total redeemed > balance",
expectError: true, expectError: true,
key: fromKeyPrivate, key: fromKeyPrivate,
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(1),
voucherAmount: big.NewInt(6), voucherAmount: big.NewInt(6),
voucherLane: 1, voucherLane: 1,
voucherNonce: 1, voucherNonce: 1,
laneStates: map[uint64]paych.LaneState{ laneStates: map[uint64]paych.LaneState{
// Lane 2 (different from voucher lane 1) // 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 { for _, tcase := range tcases {
tcase := tcase tcase := tcase
t.Run(tcase.name, func(t *testing.T) { 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 // Create an actor for the channel with the test case balance
act := &types.Actor{ act := &types.Actor{
Code: builtin.AccountActorCodeID, Code: builtin.AccountActorCodeID,
@ -184,16 +205,17 @@ func TestCheckVoucherValid(t *testing.T) {
} }
mock.setPaychState(ch, act, paychmock.NewMockPayChState( 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 // Create a manager
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
require.NoError(t, err) require.NoError(t, err)
// Add channel To address to wallet // Add channel To address to wallet
mock.addWalletAddress(to) 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) sv := createTestVoucher(t, ch, tcase.voucherLane, tcase.voucherNonce, tcase.voucherAmount, tcase.key)
// Check the voucher's validity // 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) { func TestCreateVoucher(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
// Create a voucher in lane 1 // Create a voucher in lane 1
voucherLane1Amt := big.NewInt(5) voucherLane1Amt := big.NewInt(5)
@ -400,7 +298,7 @@ func TestAddVoucherDelta(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
voucherLane := uint64(1) voucherLane := uint64(1)
@ -442,7 +340,7 @@ func TestAddVoucherNextLane(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
minDelta := big.NewInt(0) minDelta := big.NewInt(0)
voucherAmount := big.NewInt(2) voucherAmount := big.NewInt(2)
@ -489,10 +387,8 @@ func TestAddVoucherNextLane(t *testing.T) {
} }
func TestAllocateLane(t *testing.T) { func TestAllocateLane(t *testing.T) {
ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
// First lane should be 0 // First lane should be 0
lane, err := s.mgr.AllocateLane(s.ch) 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 // Create a channel that will be retrieved from state
actorBalance := big.NewInt(10) actorBalance := big.NewInt(10)
toSend := big.NewInt(1)
act := &types.Actor{ act := &types.Actor{
Code: builtin.AccountActorCodeID, Code: builtin.AccountActorCodeID,
@ -534,7 +429,7 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
Balance: actorBalance, 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) mgr, err := newManager(store, mock)
require.NoError(t, err) require.NoError(t, err)
@ -559,7 +454,7 @@ func TestAddVoucherProof(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
nonce := uint64(1) nonce := uint64(1)
voucherAmount := big.NewInt(1) voucherAmount := big.NewInt(1)
@ -622,10 +517,11 @@ func TestAddVoucherInboundWalletKey(t *testing.T) {
} }
mock := newMockManagerAPI() mock := newMockManagerAPI()
mock.setAccountAddress(fromAcct, from) mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to) 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 // Create a manager
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore())) store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
@ -660,7 +556,7 @@ func TestBestSpendable(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // 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] // Add vouchers to lane 1 with amounts: [1, 2, 3]
voucherLane := uint64(1) voucherLane := uint64(1)
@ -740,7 +636,7 @@ func TestCheckSpendable(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
// Create voucher with Extra // Create voucher with Extra
voucherLane := uint64(1) voucherLane := uint64(1)
@ -821,7 +717,7 @@ func TestSubmitVoucher(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Set up a manager with a single payment channel // Set up a manager with a single payment channel
s := testSetupMgrWithChannel(ctx, t) s := testSetupMgrWithChannel(t)
// Create voucher with Extra // Create voucher with Extra
voucherLane := uint64(1) voucherLane := uint64(1)
@ -908,7 +804,7 @@ type testScaffold struct {
fromKeyPrivate []byte fromKeyPrivate []byte
} }
func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold { func testSetupMgrWithChannel(t *testing.T) *testScaffold {
fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t) fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
ch := tutils.NewIDAddr(t, 100) ch := tutils.NewIDAddr(t, 100)
@ -929,7 +825,7 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold {
Nonce: 0, Nonce: 0,
Balance: balance, 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())) store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)

View File

@ -978,7 +978,7 @@ func TestPaychAvailableFunds(t *testing.T) {
Nonce: 0, Nonce: 0,
Balance: createAmt, 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 // Send create channel response
response := testChannelResponse(t, ch) response := testChannelResponse(t, ch)
mock.receiveMsgResponse(createMsgCid, response) mock.receiveMsgResponse(createMsgCid, response)

View 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)
}

View File

@ -312,7 +312,7 @@ func (ca *channelAccessor) currentAvailableFunds(channelID string, queuedAmt typ
return nil, err return nil, err
} }
laneStates, err := ca.laneState(ca.chctx, pchState, ch) laneStates, err := ca.laneState(pchState, ch)
if err != nil { if err != nil {
return nil, err return nil, err
} }