package paychmgr import ( "context" "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" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" paychtypes "github.com/filecoin-project/go-state-types/builtin/v8/paych" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" tutils2 "github.com/filecoin-project/specs-actors/v2/support/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" ) // 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) { //stm: @TOKEN_PAYCH_WAIT_READY_001 ctx := context.Background() store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore())) fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t) ch := tutils2.NewIDAddr(t, 100) from := tutils2.NewSECP256K1Addr(t, string(fromKeyPublic)) to := tutils2.NewSECP256K1Addr(t, "secpTo") fromAcct := tutils2.NewActorAddr(t, "fromAct") toAcct := tutils2.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, onChainReserve) 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: builtin2.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 := paychtypes.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 = paychtypes.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, onChainReserve) 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) }