2020-02-13 00:28:23 +00:00
|
|
|
package paychmgr
|
2019-08-09 21:42:56 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
2022-04-20 21:34:28 +00:00
|
|
|
lpaych "github.com/filecoin-project/lotus/chain/actors/builtin/paych"
|
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
2020-09-16 04:06:04 +00:00
|
|
|
"golang.org/x/xerrors"
|
2019-09-09 13:59:07 +00:00
|
|
|
|
2019-12-19 20:13:17 +00:00
|
|
|
"github.com/filecoin-project/go-address"
|
2020-07-28 23:16:47 +00:00
|
|
|
cborutil "github.com/filecoin-project/go-cbor-util"
|
2020-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/big"
|
2022-04-20 21:34:28 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/builtin/v8/paych"
|
2020-09-16 04:06:04 +00:00
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/api"
|
2019-10-18 04:47:41 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2020-01-30 23:48:25 +00:00
|
|
|
"github.com/filecoin-project/lotus/lib/sigs"
|
2019-08-09 21:42:56 +00:00
|
|
|
)
|
|
|
|
|
2020-09-01 14:33:44 +00:00
|
|
|
// insufficientFundsErr indicates that there are not enough funds in the
|
|
|
|
// channel to create a voucher
|
|
|
|
type insufficientFundsErr interface {
|
|
|
|
Shortfall() types.BigInt
|
|
|
|
}
|
|
|
|
|
|
|
|
type ErrInsufficientFunds struct {
|
|
|
|
shortfall types.BigInt
|
|
|
|
}
|
|
|
|
|
|
|
|
func newErrInsufficientFunds(shortfall types.BigInt) *ErrInsufficientFunds {
|
|
|
|
return &ErrInsufficientFunds{shortfall: shortfall}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ErrInsufficientFunds) Error() string {
|
|
|
|
return fmt.Sprintf("not enough funds in channel to cover voucher - shortfall: %d", e.shortfall)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ErrInsufficientFunds) Shortfall() types.BigInt {
|
|
|
|
return e.shortfall
|
|
|
|
}
|
|
|
|
|
2020-09-16 04:06:04 +00:00
|
|
|
type laneState struct {
|
|
|
|
redeemed big.Int
|
|
|
|
nonce uint64
|
|
|
|
}
|
|
|
|
|
2020-09-23 05:19:43 +00:00
|
|
|
func (ls laneState) Redeemed() (big.Int, error) {
|
|
|
|
return ls.redeemed, nil
|
2020-09-16 04:06:04 +00:00
|
|
|
}
|
|
|
|
|
2020-09-23 05:19:43 +00:00
|
|
|
func (ls laneState) Nonce() (uint64, error) {
|
|
|
|
return ls.nonce, nil
|
2020-09-16 04:06:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
// channelAccessor is used to simplify locking when accessing a channel
|
|
|
|
type channelAccessor struct {
|
2020-09-01 14:33:44 +00:00
|
|
|
from address.Address
|
|
|
|
to address.Address
|
|
|
|
|
|
|
|
// chctx is used by background processes (eg when waiting for things to be
|
|
|
|
// confirmed on chain)
|
|
|
|
chctx context.Context
|
2020-07-28 23:16:47 +00:00
|
|
|
sa *stateAccessor
|
2020-08-11 14:20:05 +00:00
|
|
|
api managerAPI
|
2020-07-28 23:16:47 +00:00
|
|
|
store *Store
|
|
|
|
lk *channelLock
|
|
|
|
fundsReqQueue []*fundsReq
|
|
|
|
msgListeners msgListeners
|
2019-09-16 16:40:26 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 14:33:44 +00:00
|
|
|
func newChannelAccessor(pm *Manager, from address.Address, to address.Address) *channelAccessor {
|
2020-07-28 23:16:47 +00:00
|
|
|
return &channelAccessor{
|
2020-09-01 14:33:44 +00:00
|
|
|
from: from,
|
|
|
|
to: to,
|
|
|
|
chctx: pm.ctx,
|
2020-08-11 14:20:05 +00:00
|
|
|
sa: pm.sa,
|
2020-08-10 21:52:59 +00:00
|
|
|
api: pm.pchapi,
|
|
|
|
store: pm.store,
|
2020-09-01 14:33:44 +00:00
|
|
|
lk: &channelLock{globalLock: &pm.lk},
|
2020-08-10 21:52:59 +00:00
|
|
|
msgListeners: newMsgListeners(),
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 21:34:28 +00:00
|
|
|
func (ca *channelAccessor) messageBuilder(ctx context.Context, from address.Address) (lpaych.MessageBuilder, error) {
|
2020-09-30 17:04:10 +00:00
|
|
|
nwVersion, err := ca.api.StateNetworkVersion(ctx, types.EmptyTSK)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-10 17:07:30 +00:00
|
|
|
av, err := actors.VersionForNetwork(nwVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-04-20 21:34:28 +00:00
|
|
|
return lpaych.Message(av, from), nil
|
2020-09-30 17:04:10 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
func (ca *channelAccessor) getChannelInfo(ctx context.Context, addr address.Address) (*ChannelInfo, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
2019-09-10 13:43:01 +00:00
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
return ca.store.ByAddress(ctx, addr)
|
2019-09-16 17:23:48 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
func (ca *channelAccessor) outboundActiveByFromTo(ctx context.Context, from, to address.Address) (*ChannelInfo, error) {
|
2020-09-04 11:44:09 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
2022-01-06 18:51:26 +00:00
|
|
|
return ca.store.OutboundActiveByFromTo(ctx, ca.api, from, to)
|
2020-09-04 11:44:09 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 14:33:44 +00:00
|
|
|
// createVoucher creates a voucher with the given specification, setting its
|
|
|
|
// nonce, signing the voucher and storing it in the local datastore.
|
|
|
|
// If there are not enough funds in the channel to create the voucher, returns
|
|
|
|
// the shortfall in funds.
|
2020-09-28 21:25:58 +00:00
|
|
|
func (ca *channelAccessor) createVoucher(ctx context.Context, ch address.Address, voucher paych.SignedVoucher) (*api.VoucherCreateResult, error) {
|
2020-09-01 14:33:44 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
|
|
|
// Find the channel for the voucher
|
2021-12-11 21:03:00 +00:00
|
|
|
ci, err := ca.store.ByAddress(ctx, ch)
|
2020-09-01 14:33:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to get channel info by address: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the voucher channel
|
|
|
|
sv := &voucher
|
|
|
|
sv.ChannelAddr = ch
|
|
|
|
|
|
|
|
// Get the next nonce on the given lane
|
|
|
|
sv.Nonce = ca.nextNonceForLane(ci, voucher.Lane)
|
|
|
|
|
|
|
|
// Sign the voucher
|
|
|
|
vb, err := sv.SigningBytes()
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to get voucher signing bytes: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sig, err := ca.api.WalletSign(ctx, ci.Control, vb)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to sign voucher: %w", err)
|
|
|
|
}
|
|
|
|
sv.Signature = sig
|
|
|
|
|
|
|
|
// Store the voucher
|
2020-09-30 16:35:42 +00:00
|
|
|
if _, err := ca.addVoucherUnlocked(ctx, ch, sv, types.NewInt(0)); err != nil {
|
2020-09-01 14:33:44 +00:00
|
|
|
// If there are not enough funds in the channel to cover the voucher,
|
|
|
|
// return a voucher create result with the shortfall
|
|
|
|
var ife insufficientFundsErr
|
|
|
|
if xerrors.As(err, &ife) {
|
|
|
|
return &api.VoucherCreateResult{
|
|
|
|
Shortfall: ife.Shortfall(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, xerrors.Errorf("failed to persist voucher: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &api.VoucherCreateResult{Voucher: sv, Shortfall: types.NewInt(0)}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ca *channelAccessor) nextNonceForLane(ci *ChannelInfo, lane uint64) uint64 {
|
|
|
|
var maxnonce uint64
|
|
|
|
for _, v := range ci.Vouchers {
|
|
|
|
if v.Voucher.Lane == lane {
|
|
|
|
if v.Voucher.Nonce > maxnonce {
|
|
|
|
maxnonce = v.Voucher.Nonce
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return maxnonce + 1
|
|
|
|
}
|
|
|
|
|
2022-04-20 21:34:28 +00:00
|
|
|
func (ca *channelAccessor) checkVoucherValid(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (map[uint64]lpaych.LaneState, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
2019-09-16 17:23:48 +00:00
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
return ca.checkVoucherValidUnlocked(ctx, ch, sv)
|
2019-08-12 17:09:56 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 21:34:28 +00:00
|
|
|
func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (map[uint64]lpaych.LaneState, error) {
|
2020-07-15 09:12:03 +00:00
|
|
|
if sv.ChannelAddr != ch {
|
|
|
|
return nil, xerrors.Errorf("voucher ChannelAddr doesn't match channel address, got %s, expected %s", sv.ChannelAddr, ch)
|
|
|
|
}
|
|
|
|
|
2021-02-16 12:29:29 +00:00
|
|
|
// check voucher is unlocked
|
|
|
|
if sv.Extra != nil {
|
|
|
|
return nil, xerrors.Errorf("voucher is Message Locked")
|
|
|
|
}
|
|
|
|
if sv.TimeLockMax != 0 {
|
|
|
|
return nil, xerrors.Errorf("voucher is Max Time Locked")
|
|
|
|
}
|
|
|
|
if sv.TimeLockMin != 0 {
|
|
|
|
return nil, xerrors.Errorf("voucher is Min Time Locked")
|
|
|
|
}
|
2022-04-20 21:34:28 +00:00
|
|
|
if len(sv.SecretHash) != 0 {
|
2021-02-16 12:29:29 +00:00
|
|
|
return nil, xerrors.Errorf("voucher is Hash Locked")
|
|
|
|
}
|
|
|
|
|
2020-08-17 15:13:13 +00:00
|
|
|
// Load payment channel actor state
|
|
|
|
act, pchState, err := ca.sa.loadPaychActorState(ctx, ch)
|
2019-08-09 21:42:56 +00:00
|
|
|
if err != nil {
|
2020-07-09 22:27:39 +00:00
|
|
|
return nil, err
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
2020-08-17 15:13:13 +00:00
|
|
|
// Load channel "From" account actor state
|
2020-09-23 05:19:43 +00:00
|
|
|
f, err := pchState.From()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
from, err := ca.api.ResolveToKeyAddress(ctx, f, nil)
|
2020-03-03 02:12:36 +00:00
|
|
|
if err != nil {
|
2020-07-09 22:27:39 +00:00
|
|
|
return nil, err
|
2020-03-03 02:12:36 +00:00
|
|
|
}
|
|
|
|
|
2020-08-17 15:13:13 +00:00
|
|
|
// verify voucher signature
|
2019-08-09 21:42:56 +00:00
|
|
|
vb, err := sv.SigningBytes()
|
|
|
|
if err != nil {
|
2020-07-09 22:27:39 +00:00
|
|
|
return nil, err
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
2019-08-13 18:47:40 +00:00
|
|
|
|
|
|
|
// TODO: technically, either party may create and sign a voucher.
|
|
|
|
// However, for now, we only accept them from the channel creator.
|
|
|
|
// More complex handling logic can be added later
|
2020-03-03 02:12:36 +00:00
|
|
|
if err := sigs.Verify(sv.Signature, from, vb); err != nil {
|
2020-07-09 22:27:39 +00:00
|
|
|
return nil, err
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 22:27:39 +00:00
|
|
|
// Check the voucher against the highest known voucher nonce / value
|
2021-12-11 21:03:00 +00:00
|
|
|
laneStates, err := ca.laneState(ctx, pchState, ch)
|
2020-07-09 22:27:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-10 18:06:52 +00:00
|
|
|
|
|
|
|
// If the new voucher nonce value is less than the highest known
|
|
|
|
// nonce for the lane
|
|
|
|
ls, lsExists := laneStates[sv.Lane]
|
2020-09-23 05:19:43 +00:00
|
|
|
if lsExists {
|
|
|
|
n, err := ls.Nonce()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-10 18:06:52 +00:00
|
|
|
|
2020-09-23 05:19:43 +00:00
|
|
|
if sv.Nonce <= n {
|
|
|
|
return nil, fmt.Errorf("nonce too low")
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the voucher amount is less than the highest known voucher amount
|
|
|
|
r, err := ls.Redeemed()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if sv.Amount.LessThanEqual(r) {
|
|
|
|
return nil, fmt.Errorf("voucher amount is lower than amount for voucher with lower nonce")
|
|
|
|
}
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 18:06:52 +00:00
|
|
|
// Total redeemed is the total redeemed amount for all lanes, including
|
|
|
|
// the new voucher
|
|
|
|
// eg
|
|
|
|
//
|
|
|
|
// lane 1 redeemed: 3
|
|
|
|
// lane 2 redeemed: 2
|
|
|
|
// voucher for lane 1: 5
|
|
|
|
//
|
|
|
|
// Voucher supersedes lane 1 redeemed, therefore
|
|
|
|
// effective lane 1 redeemed: 5
|
|
|
|
//
|
|
|
|
// lane 1: 5
|
|
|
|
// lane 2: 2
|
|
|
|
// -
|
|
|
|
// total: 7
|
2020-07-28 23:16:47 +00:00
|
|
|
totalRedeemed, err := ca.totalRedeemedWithVoucher(laneStates, sv)
|
2020-07-10 18:06:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-09 22:27:39 +00:00
|
|
|
|
2020-09-14 08:11:11 +00:00
|
|
|
// Total required balance must not exceed actor balance
|
|
|
|
if act.Balance.LessThan(totalRedeemed) {
|
|
|
|
return nil, newErrInsufficientFunds(types.BigSub(totalRedeemed, act.Balance))
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(sv.Merges) != 0 {
|
2020-07-09 22:27:39 +00:00
|
|
|
return nil, fmt.Errorf("dont currently support paych lane merges")
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 18:06:52 +00:00
|
|
|
return laneStates, nil
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
2020-09-30 16:35:42 +00:00
|
|
|
func (ca *channelAccessor) checkVoucherSpendable(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte) (bool, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
|
|
|
recipient, err := ca.getPaychRecipient(ctx, ch)
|
2019-08-09 21:42:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
ci, err := ca.store.ByAddress(ctx, ch)
|
2020-08-20 16:09:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if voucher has already been submitted
|
|
|
|
submitted, err := ci.wasVoucherSubmitted(sv)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if submitted {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
mb, err := ca.messageBuilder(ctx, recipient)
|
2019-08-09 21:42:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
mes, err := mb.Update(ch, sv, secret)
|
2020-09-30 17:04:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ret, err := ca.api.Call(ctx, mes, nil)
|
2019-08-09 21:42:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2020-03-03 23:32:17 +00:00
|
|
|
if ret.MsgRct.ExitCode != 0 {
|
2019-08-09 21:42:56 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
func (ca *channelAccessor) getPaychRecipient(ctx context.Context, ch address.Address) (address.Address, error) {
|
2020-09-16 04:06:04 +00:00
|
|
|
_, state, err := ca.api.GetPaychState(ctx, ch, nil)
|
|
|
|
if err != nil {
|
2020-02-12 23:52:19 +00:00
|
|
|
return address.Address{}, err
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
|
|
|
|
2020-09-23 05:19:43 +00:00
|
|
|
return state.To()
|
2019-08-09 21:42:56 +00:00
|
|
|
}
|
2019-08-12 17:09:56 +00:00
|
|
|
|
2020-09-30 16:35:42 +00:00
|
|
|
func (ca *channelAccessor) addVoucher(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, minDelta types.BigInt) (types.BigInt, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
2019-09-25 12:56:04 +00:00
|
|
|
|
2020-09-30 16:35:42 +00:00
|
|
|
return ca.addVoucherUnlocked(ctx, ch, sv, minDelta)
|
2020-09-01 14:33:44 +00:00
|
|
|
}
|
|
|
|
|
2020-09-30 16:35:42 +00:00
|
|
|
func (ca *channelAccessor) addVoucherUnlocked(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, minDelta types.BigInt) (types.BigInt, error) {
|
2021-12-11 21:03:00 +00:00
|
|
|
ci, err := ca.store.ByAddress(ctx, ch)
|
2019-09-25 12:56:04 +00:00
|
|
|
if err != nil {
|
2020-07-28 23:16:47 +00:00
|
|
|
return types.BigInt{}, err
|
2019-09-25 12:56:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 22:27:39 +00:00
|
|
|
// Check if the voucher has already been added
|
2020-09-30 16:35:42 +00:00
|
|
|
for _, v := range ci.Vouchers {
|
2020-03-03 02:12:36 +00:00
|
|
|
eq, err := cborutil.Equals(sv, v.Voucher)
|
2020-02-12 23:52:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return types.BigInt{}, err
|
|
|
|
}
|
2020-09-30 16:35:42 +00:00
|
|
|
if eq {
|
|
|
|
// Ignore the duplicate voucher.
|
|
|
|
log.Warnf("AddVoucher: voucher re-added")
|
|
|
|
return types.NewInt(0), nil
|
2019-09-25 12:56:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 22:27:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check voucher validity
|
2020-07-28 23:16:47 +00:00
|
|
|
laneStates, err := ca.checkVoucherValidUnlocked(ctx, ch, sv)
|
2020-07-09 22:27:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return types.NewInt(0), err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The change in value is the delta between the voucher amount and
|
2020-07-10 18:06:52 +00:00
|
|
|
// the highest previous voucher amount for the lane
|
|
|
|
laneState, exists := laneStates[sv.Lane]
|
|
|
|
redeemed := big.NewInt(0)
|
|
|
|
if exists {
|
2020-09-23 05:19:43 +00:00
|
|
|
redeemed, err = laneState.Redeemed()
|
|
|
|
if err != nil {
|
|
|
|
return types.NewInt(0), err
|
|
|
|
}
|
2019-09-25 12:56:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 18:06:52 +00:00
|
|
|
delta := types.BigSub(sv.Amount, redeemed)
|
2019-09-25 12:56:04 +00:00
|
|
|
if minDelta.GreaterThan(delta) {
|
2020-07-10 18:06:52 +00:00
|
|
|
return delta, xerrors.Errorf("addVoucher: supplied token amount too low; minD=%s, D=%s; laneAmt=%s; v.Amt=%s", minDelta, delta, redeemed, sv.Amount)
|
2019-09-25 12:56:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ci.Vouchers = append(ci.Vouchers, &VoucherInfo{
|
|
|
|
Voucher: sv,
|
|
|
|
})
|
|
|
|
|
2020-07-09 21:20:17 +00:00
|
|
|
if ci.NextLane <= sv.Lane {
|
2020-02-21 17:26:44 +00:00
|
|
|
ci.NextLane = sv.Lane + 1
|
2019-09-25 12:56:04 +00:00
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
return delta, ca.store.putChannelInfo(ctx, ci)
|
2019-08-12 17:09:56 +00:00
|
|
|
}
|
2019-08-12 19:51:01 +00:00
|
|
|
|
2020-09-30 16:35:42 +00:00
|
|
|
func (ca *channelAccessor) submitVoucher(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte) (cid.Cid, error) {
|
2020-08-20 16:09:52 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
ci, err := ca.store.ByAddress(ctx, ch)
|
2020-08-20 16:09:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
has, err := ci.hasVoucher(sv)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the channel has the voucher
|
|
|
|
if has {
|
|
|
|
// Check that the voucher hasn't already been submitted
|
|
|
|
submitted, err := ci.wasVoucherSubmitted(sv)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
if submitted {
|
|
|
|
return cid.Undef, xerrors.Errorf("cannot submit voucher that has already been submitted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
mb, err := ca.messageBuilder(ctx, ci.Control)
|
2020-08-20 16:09:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
msg, err := mb.Update(ch, sv, secret)
|
2020-09-30 17:04:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
2020-08-20 16:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
smsg, err := ca.api.MpoolPushMessage(ctx, msg, nil)
|
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the channel didn't already have the voucher
|
|
|
|
if !has {
|
|
|
|
// Add the voucher to the channel
|
|
|
|
ci.Vouchers = append(ci.Vouchers, &VoucherInfo{
|
|
|
|
Voucher: sv,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark the voucher and any lower-nonce vouchers as having been submitted
|
2021-12-11 21:03:00 +00:00
|
|
|
err = ca.store.MarkVoucherSubmitted(ctx, ci, sv)
|
2020-08-20 16:09:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return smsg.Cid(), nil
|
|
|
|
}
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
func (ca *channelAccessor) allocateLane(ctx context.Context, ch address.Address) (uint64, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
return ca.store.AllocateLane(ctx, ch)
|
2019-09-10 13:43:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
func (ca *channelAccessor) listVouchers(ctx context.Context, ch address.Address) ([]*VoucherInfo, error) {
|
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
2019-08-12 19:51:01 +00:00
|
|
|
// TODO: just having a passthrough method like this feels odd. Seems like
|
|
|
|
// there should be some filtering we're doing here
|
2021-12-11 21:03:00 +00:00
|
|
|
return ca.store.VouchersForPaych(ctx, ch)
|
2019-08-12 19:51:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
// laneState gets the LaneStates from chain, then applies all vouchers in
|
|
|
|
// the data store over the chain state
|
2022-04-20 21:34:28 +00:00
|
|
|
func (ca *channelAccessor) laneState(ctx context.Context, state lpaych.State, ch address.Address) (map[uint64]lpaych.LaneState, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
// TODO: we probably want to call UpdateChannelState with all vouchers to be fully correct
|
|
|
|
// (but technically dont't need to)
|
|
|
|
|
2020-09-16 04:06:04 +00:00
|
|
|
laneCount, err := state.LaneCount()
|
2020-08-11 21:01:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-07-28 23:16:47 +00:00
|
|
|
}
|
|
|
|
|
2020-08-11 21:01:16 +00:00
|
|
|
// Note: we use a map instead of an array to store laneStates because the
|
|
|
|
// client sets the lane ID (the index) and potentially they could use a
|
|
|
|
// very large index.
|
2022-04-20 21:34:28 +00:00
|
|
|
laneStates := make(map[uint64]lpaych.LaneState, laneCount)
|
|
|
|
err = state.ForEachLaneState(func(idx uint64, ls lpaych.LaneState) error {
|
2020-09-16 04:06:04 +00:00
|
|
|
laneStates[idx] = ls
|
2020-08-11 21:01:16 +00:00
|
|
|
return nil
|
|
|
|
})
|
2020-08-13 00:27:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-08-11 21:01:16 +00:00
|
|
|
|
2020-07-28 23:16:47 +00:00
|
|
|
// Apply locally stored vouchers
|
2021-12-11 21:03:00 +00:00
|
|
|
vouchers, err := ca.store.VouchersForPaych(ctx, ch)
|
2020-07-28 23:16:47 +00:00
|
|
|
if err != nil && err != ErrChannelNotTracked {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range vouchers {
|
|
|
|
for range v.Voucher.Merges {
|
|
|
|
return nil, xerrors.Errorf("paych merges not handled yet")
|
|
|
|
}
|
|
|
|
|
2020-09-14 08:11:11 +00:00
|
|
|
// Check if there is an existing laneState in the payment channel
|
|
|
|
// for this voucher's lane
|
2020-07-28 23:16:47 +00:00
|
|
|
ls, ok := laneStates[v.Voucher.Lane]
|
2020-09-14 08:11:11 +00:00
|
|
|
|
|
|
|
// If the voucher does not have a higher nonce than the existing
|
|
|
|
// laneState for this lane, ignore it
|
2020-09-23 05:19:43 +00:00
|
|
|
if ok {
|
|
|
|
n, err := ls.Nonce()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if v.Voucher.Nonce < n {
|
|
|
|
continue
|
|
|
|
}
|
2020-07-28 23:16:47 +00:00
|
|
|
}
|
|
|
|
|
2020-09-14 08:11:11 +00:00
|
|
|
// Voucher has a higher nonce, so replace laneState with this voucher
|
2020-09-16 04:06:04 +00:00
|
|
|
laneStates[v.Voucher.Lane] = laneState{v.Voucher.Amount, v.Voucher.Nonce}
|
2020-07-28 23:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return laneStates, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the total redeemed amount across all lanes, after applying the voucher
|
2022-04-20 21:34:28 +00:00
|
|
|
func (ca *channelAccessor) totalRedeemedWithVoucher(laneStates map[uint64]lpaych.LaneState, sv *paych.SignedVoucher) (big.Int, error) {
|
2020-07-28 23:16:47 +00:00
|
|
|
// TODO: merges
|
|
|
|
if len(sv.Merges) != 0 {
|
|
|
|
return big.Int{}, xerrors.Errorf("dont currently support paych lane merges")
|
|
|
|
}
|
|
|
|
|
|
|
|
total := big.NewInt(0)
|
|
|
|
for _, ls := range laneStates {
|
2020-09-23 05:19:43 +00:00
|
|
|
r, err := ls.Redeemed()
|
|
|
|
if err != nil {
|
|
|
|
return big.Int{}, err
|
|
|
|
}
|
|
|
|
total = big.Add(total, r)
|
2020-07-28 23:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lane, ok := laneStates[sv.Lane]
|
|
|
|
if ok {
|
|
|
|
// If the voucher is for an existing lane, and the voucher nonce
|
2020-09-01 14:33:44 +00:00
|
|
|
// is higher than the lane nonce
|
2020-09-23 05:19:43 +00:00
|
|
|
n, err := lane.Nonce()
|
|
|
|
if err != nil {
|
|
|
|
return big.Int{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if sv.Nonce > n {
|
2020-07-28 23:16:47 +00:00
|
|
|
// Add the delta between the redeemed amount and the voucher
|
|
|
|
// amount to the total
|
2020-09-23 05:19:43 +00:00
|
|
|
r, err := lane.Redeemed()
|
|
|
|
if err != nil {
|
|
|
|
return big.Int{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
delta := big.Sub(sv.Amount, r)
|
2020-07-28 23:16:47 +00:00
|
|
|
total = big.Add(total, delta)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If the voucher is *not* for an existing lane, just add its
|
|
|
|
// value (implicitly a new lane will be created for the voucher)
|
|
|
|
total = big.Add(total, sv.Amount)
|
|
|
|
}
|
|
|
|
|
|
|
|
return total, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ca *channelAccessor) settle(ctx context.Context, ch address.Address) (cid.Cid, error) {
|
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
ci, err := ca.store.ByAddress(ctx, ch)
|
2020-07-28 23:16:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
mb, err := ca.messageBuilder(ctx, ci.Control)
|
2020-09-30 17:04:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
2020-09-30 17:58:34 +00:00
|
|
|
msg, err := mb.Settle(ch)
|
2020-09-30 17:04:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
2020-07-28 23:16:47 +00:00
|
|
|
}
|
2020-08-12 20:17:21 +00:00
|
|
|
smgs, err := ca.api.MpoolPushMessage(ctx, msg, nil)
|
2020-07-28 23:16:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ci.Settling = true
|
2021-12-11 21:03:00 +00:00
|
|
|
err = ca.store.putChannelInfo(ctx, ci)
|
2020-07-28 23:16:47 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("Error marking channel as settled: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return smgs.Cid(), err
|
|
|
|
}
|
2020-08-10 17:21:25 +00:00
|
|
|
|
|
|
|
func (ca *channelAccessor) collect(ctx context.Context, ch address.Address) (cid.Cid, error) {
|
|
|
|
ca.lk.Lock()
|
|
|
|
defer ca.lk.Unlock()
|
|
|
|
|
2021-12-11 21:03:00 +00:00
|
|
|
ci, err := ca.store.ByAddress(ctx, ch)
|
2020-08-10 17:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
mb, err := ca.messageBuilder(ctx, ci.Control)
|
2020-09-30 17:04:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
2020-09-30 17:58:34 +00:00
|
|
|
msg, err := mb.Collect(ch)
|
2020-09-30 17:04:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
2020-08-10 17:21:25 +00:00
|
|
|
}
|
|
|
|
|
2020-08-12 20:17:21 +00:00
|
|
|
smsg, err := ca.api.MpoolPushMessage(ctx, msg, nil)
|
2020-08-10 17:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return cid.Undef, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return smsg.Cid(), nil
|
|
|
|
}
|