Payment Channel Manager
Confidence Level:Draft/WIP
The Payments Channel Manager provides APIs that enable payment for retrieval via payment channels.
# What is a payment channel?
A payment channel is a direct payment connection between two participants in a cryptocurrency network. Through on-chain opening and closing transactions, payment channels leverage the security of the underlying blockchain to provide trustless payments between two parties without needing to submit each intermediate payment on chain.
Because we assume that retrievals need to happen fast, we further assume that we cannot block any step of retrieval for an on chain transaction. This must happen later. As such, we need some kind of payment channel system that operates as an L2 solution to the underlying L1 blockchain.
# What does the payment channel manager do?
The payment channel manager has two responsibilies: it communicates with the block chain to manage the on-chain component of payment channels, and it maintains state about payment channels that has not yet been submitted on on chain (such as outstanding vouchers).
# Dependencies
Name | Kind | APIs |
---|---|---|
Chain | required | default |
Wallet | required | default |
Note: a chain and a wallet are sufficient as dependencies to a payment channel manager, but there are a couple steps involved to hook up the current Lotus payment channel manager to those APIs. A good example the intermediate code required can be found here:
https://github.com/application-research/estuary/blob/master/filclient/msgpusher.go
and here:
https://github.com/filecoin-project/lotus/blob/master/chain/stmgr/rpc/rpcstatemanager.go
# Roadmap
V0: Existing or 3 months
A Payment Channel Channel manager is currently implemented in Lotus and used for retrievals. It works, but it does have one significant pain point: as written, it always creates a blocking chain message to deposit funds in a channel at the beginning of a retrieval. This prevents performant retrieval
V0.5: 6 months
The API specified below is designed to remove some of the ambiguity in the current payment channel manager, and to efficiently unlock paying for lots of retrievals ahead of time, so that they can proceed in batchs without being blocked by the chain. This fulfills the original purpose of a payment channel.
V1: 1 Year
It's fairly ineffecient to create a payment channel between every party that wants to transact on a network. A common technique for managing payment channels in a large scale crypto currency network is a "hub and spoke pattern". In this scenario, two parties who do not yet have a direct payment channel can go through an intermediary they've both already established a channel with. The mechanism to do this called is "Hash-Locked-Time-Codes", and the Filecoin blockchain has the machinery to do this. This is possibly something we need to implement to get payment channels to work at scale, even for an "MVP" retrieval market CDN. Both Bitcoin and Ethereum offer networks that have this functionality
V2: Future
There are several further innovations in the world of payment channel mechanisms (https://finalitylabs.io/static/media/SetPaymentChannels.8a29d449.pdf, https://eprint.iacr.org/2017/635.pdf). Third parties looking to innovate in this space can offer custom implementations of the payment channel manager as a way of becoming service providers for fast payments
# Preliminary API
The API below is extracted and modified from Lotus's Payment Channel Manager API
Notable changes include:
- removing the highly complicated and opaque method PaychGet (essentially a "get, create, and add" operation that is highly unpredicatble) and replacing it
with methods that perform simpler clearer operations - combining two seperate methods we offer for getting status on payment channels into one
- adding a PaychReserveFunds and PaychReleaseFunds that mirror the methods on the Lotus FundManager -- by allowing other processes to essentially "lock up" outstanding funds in a payment channel for an ongoing transaction, we have a sense of how much is actually available to spend. Not having this information has meant we always assume we need a deposit -- an create a blocking chain message to create a channel or add funds. This way, we'll know if there are free funds for a retrieval, and avoid a chain message if we do
- Removing the concept of a "wait sentinel" as a thin abstraction on a chain message. The waiting methods just wrapped the Chain StateWaitMsg call and seem redundant.
package paymentchannelmanager
import (
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/ipfs/go-cid"
)
type PCHDir int
const (
PCHUndef PCHDir = iota
PCHInbound
PCHOutbound
)
type PaychStatus struct {
// PCHDir indicates whether this channel is an inbound channel or an
// outbound channel
Direction PCHDir
// ControlAddr
ControlAddr address.Address
// Channel is the address of the channel
Channel address.Address
// From is the from address of the channel (channel creator)
From address.Address
// To is the to address of the channel
To address.Address
// ConfirmedAmt is the amount of funds that have been confirmed on-chain
// for the channel
ConfirmedAmt big.Int
// PendingAmt is the amount of funds that are pending confirmation on-chain
PendingAmt big.Int
// PendingChainMessage
PendingChainMessage *cid.Cid
// QueuedAmt is the amount that is queued up behind a pending request
QueuedAmt big.Int
// VoucherRedeemedAmt is the amount that is redeemed by vouchers on-chain
// and in the local datastore
VoucherReedeemedAmt big.Int
// ReservedAmt is the portion of the confirmed funds previously reserved by
// consumers of this payment channel
ReservedAmt big.Int
}
// VoucherCreateResult is the response to calling PaychVoucherCreate
type VoucherCreateResult struct {
// Voucher that was created, or nil if there was an error or if there
// were insufficient funds in the channel
Voucher *paych.SignedVoucher
// Shortfall is the additional amount that would be needed in the channel
// in order to be able to create the voucher
Shortfall big.Int
}
type PaymentChannelManagerAPI interface {
// PaychCreate creates a new payment channel and deposits funds in the
// channel
PaychCreate(
ctx context.Context,
from, to address.Address,
amt big.Int,
) (cid.Cid, error)
// PaychAddFunds deposits additional payment into a payment channel
// It returns the cid of the chain message submitted for the additional
// funds
PaychAddFunds(
ctx context.Context,
ch address.Address,
amt big.Int,
) (cid.Cid, error)
// PaychReserveFunds locks up the specified amount of funds in the given
// channel for
// a future transaction. If there are not enough available funds for
// the channel, submits a message on chain to top up available funds.
// Returns the cid of the message that was submitted on chain, or cid.Undef
// if
// the required funds were already available.
PaychReserveFunds(
ctx context.Context,
ch address.Address,
amt big.Int,
) (cid.Cid, error)
// PaychReleaseFunds frees channel funds so another transaction can use them
PaychReleaseFunds(
ctx context.Context,
addr address.Address,
amt big.Int,
) error
// PaychStatus looks up all available information about a payment channel
PaychStatus(ctx context.Context, ch address.Address) (*PaychStatus, error)
// PaychLookup searches for a channel matching the given from and to
// addresses that has available lanes
PaychLookup(
ctx context.Context,
from, to address.Address,
) (address.Address, error)
// PaychList lists the channels this manager is tracking
PaychList(context.Context) ([]address.Address, error)
// PaychSettle begins the settlement period for a payment channel
PaychSettle(context.Context, address.Address) (cid.Cid, error)
// PaychCollect attempts to collect payment on a payment channel at the end
// of a settlement period
PaychCollect(context.Context, address.Address) (cid.Cid, error)
// PaychAllocateLane identifies the next free lane for a payment channel
PaychAllocateLane(ctx context.Context, ch address.Address) (uint64, error)
// PaychVoucherCheckValid verifies if a voucher appears to meet requirements
// for submission
PaychVoucherCheckValid(
ctx context.Context,
ch address.Address,
sv *paych.SignedVoucher,
) error
// PaychVoucherCheckSpendable verifies if a voucher appears to meet
// requirements for submission and also verifies
// the given secret against the pre-image
PaychVoucherCheckSpendable(
ctx context.Context,
ch address.Address,
sv *paych.SignedVoucher,
secret []byte,
proof []byte,
) (bool, error)
// PaychVoucherCreate creates a new signed voucher on the given payment
// channel
// with the given lane and amount. The value passed in is exactly the value
// that will be used to create the voucher, so if previous vouchers exist,
// the
// actual additional value of this voucher will only be the difference
// between
// the two.
// If there are insufficient funds in the channel to create the voucher,
// returns a nil voucher and the shortfall.
PaychVoucherCreate(
ctx context.Context,
pch address.Address,
amt big.Int,
lane uint64,
) (*VoucherCreateResult, error)
// PaychVoucherAdd records a new inbound voucher but does not save it to the
// chain. In addition to the channel, voucher, and proof for the voucher
// pre-image
// if present, payment voucher add takes a minDelta, or minimum increase in
// lane value required to save the voucher.
// If successful, the function returns the delta in amound redeemed on the
// given lane in the payment channel
PaychVoucherAdd(
ctx context.Context,
ch address.Address,
sv *paych.SignedVoucher,
proof []byte,
minDelta big.Int,
) (big.Int, error)
// PaymentVoucherList returns all vouchers for a given payment channel
PaychVoucherList(
context.Context,
address.Address,
) ([]*paych.SignedVoucher, error)
// PaymentVoucherSubmit submits a given voucher to the blockchain
PaychVoucherSubmit(
ctx context.Context,
ch address.Address,
sv *paych.SignedVoucher,
secret []byte,
proof []byte,
) (cid.Cid, error)
}