Filecoin支付通道研究
本文主要阐述Filecoin中Payment Channel,即支付通道的具体实现。
概述
支付通道是一种提高区块链可扩展性的机制,使交易的双方减少与链的交互。提高了系统的负载量,同时减少了与链交互产生的gas费。在Filecoin中,交易双方通过智能合约使用支付通道。
作用
支付通道主要是解决支付双方在一次服务中多次交易,这些交易不需要事实上链,只需要在最终状态上链进行结算。对交易双方有一下几个好处:
1. 减少等待时间,由于区块链的特性,如果和链进行交互,相较于普通的计算机操作,会有巨大的延迟,以检索市场为例,如果检索一个30G文件,存在频繁的支付交易,当交易必须上链确认时,等待消息上链几乎占据了检索的整个耗时。
2. 减少gas费,与链交互会产生gas费,降低与链的交互会降低服务的成本。
3. 提高网络容量,支付通道将多次频繁的交易简化为只需要最终状态时上链进行结算,大大减少了频繁交易占据的网络容量。
术语
· payment channel sender
支付通道发起者,是创建支付通道并向支付通道添加资金的一方,支付通道发起者接受支付通道接收者提供的服务。
· payment channel recipient
支付通道接收者,接收从支付通道来的资金并向支付通道发起者提供相应的服务。
· voucher
支付凭证,由支付通道任意一方创建的签名消息,用于更新支付通道支付资金。
· lane
支付航道,多个服务可以共享一个支付通道,通过支付航道区别不同的服务。每个支付凭证必须指定一个支付航道。
· Redeeming a voucher
兑现凭证,凭证必须由创建凭证的另一方在链上提交。兑换凭证不会触发资金从渠道转移到接收者的账户,但它会产生gas费。凭证可以在任何时间兑换,直到归集为止。
· UpdateChannelState
更新支付通道状态,兑付凭证的过程,即在链上提交(但不兑现)凭证。
· Settle
结算,此过程开始关闭支付通道。它可以由通道的创建者(发送方)或通道接收方调用,这个过程会产生gas费。
· Collect
归集,通过此过程,资金最终从支付通道发送方转移到支付通道接收方,这个过程会产生gas费。
执行逻辑
1. 交易双方进行一系列交易,一方需要向一方支付支付通道资金。双方在链下协商好交易的具体内容。
2. 支付通道发起者通过PayChActor创建一个支付通道并质押一些资金。 (合约并没有限制创建支付通道的地址,任何账户地址都可以为任意两个交易双方创建支付通道。但是创建支付通道需要支付一定的费用,所以正常情况下是由支付通道发起者创建)
3. 交易双方创建并相互发送支付凭证
4. 双方暂时将支付凭证保存在本地,支付凭证只能被支付凭证接收者兑换
5. 接收到凭证的一方可以立即或者以后将凭证提交到链上
6. 任意一方调用Settle开始关闭支付通道(为了防止有一方过早的关闭支付通道,可以通过MinSettleHeight值限制支付通道最早的截止日期)
7. 12小时后关闭支付通道
8. 如果双方还有更高Nonce的支付凭证没有兑换,现在需要提交凭证到链上
9. 12小时期限接收,支付通道不再接收支付凭证兑换
10. 任意一方调用Collect
11. 支付资金转入到支付通道接收者账户,剩余的资金返回给支付通道发起者
具体实现
支付通道状态
支付通道发起者通过智能合约创建一个支付通道,支付通道发起者需要提供支付通道发起者地址和支付通道接收者地址。支付通道创建后生成一个PayChActor,以下是PayChActor状态:
type State struct {
// Channel owner, who has funded the actor
From addr.Address
// Recipient of payouts from channel
To addr.Address
// Amount successfully redeemed through the payment channel, paid out on `Collect()`
ToSend abi.TokenAmount
// Height at which the channel can be `Collected`
SettlingAt abi.ChainEpoch
// Height before which the channel `ToSend` cannot be collected
MinSettleHeight abi.ChainEpoch
// Collections of lane states for the channel, maintained in ID order.
LaneStates cid.Cid // AMT<LaneState>
}
From : 支付通道发起者,必须和创建支付通道的地址一致
To : 支付通道接收者
ToSend : 通过voucher更新的支付通道支付资金,结算后,ToSend资金支付给支付通道接收者,支付通道余额归还支付通道发起者
SettlingAt : 支付通道的停止兑换支付凭证时间,高度达到SettlingAt后,未兑换的支付凭证失效
MinSettleHeight : 最晚的兑换支付凭证时间,SettlingAt不可以小于MinSettleHeight
LaneStates : 航道状态集合
Voucher
为了使用支付通道进行交易,双方会互相发送签名消息更新支付通道支付资金。在Filecoin网络,这个签名消息称作voucher。voucher用于更新支付通道状态。
type SignedVoucher struct {
// ChannelAddr is the address of the payment channel this signed voucher is valid for
ChannelAddr addr.Address
// TimeLockMin sets a min epoch before which the voucher cannot be redeemed
TimeLockMin abi.ChainEpoch
// TimeLockMax sets a max epoch beyond which the voucher cannot be redeemed
// TimeLockMax set to 0 means no timeout
TimeLockMax abi.ChainEpoch
// (optional) The SecretPreImage is used by `To` to validate
SecretPreimage []byte
// (optional) Extra can be specified by `From` to add a verification method to the voucher
Extra *ModVerifyParams
// Specifies which lane the Voucher merges into (will be created if does not exist)
Lane uint64
// Nonce is set by `From` to prevent redemption of stale vouchers on a lane
Nonce uint64
// Amount voucher can be redeemed for
Amount big.Int
// (optional) MinSettleHeight can extend channel MinSettleHeight if needed
MinSettleHeight abi.ChainEpoch
// (optional) Set of lanes to be merged into `Lane`
Merges []Merge
// Sender's signature over the voucher
Signature *crypto.Signature
}
介绍一些重点字段:
TimeLockMin字段限制了兑换支付凭证的最小高度
TimeLockMax字段限制了兑换支付凭证的最大高度,如果字段非0,当超过TimeLockMax高度后凭证过期
SecretPreimage字段会和Secret共同验证支付凭证的合法性。支付者向接收者发送凭证时,会额外提供Secret,凭证在链上兑换时,会检查SecretPreimage是和Secret是否一致。SecretPreimage等于hash(Secret),目前filecoin实际暂时未使用这个功能。
if len(sv.SecretPreimage) > 0 {
hashedSecret := rt.HashBlake2b(params.Secret)
if !bytes.Equal(hashedSecret[:], sv.SecretPreimage) {
rt.Abortf(exitcode.ErrIllegalArgument, "incorrect secret!")
}
}
Extra比较有意思,在凭证兑换时,支付者可以要求验证某种条件是否满足。
if sv.Extra != nil {
code := rt.Send(
sv.Extra.Actor,
sv.Extra.Method,
builtin.CBORBytes(sv.Extra.Data),
abi.NewTokenAmount(0),
&builtin.Discard{},
)
builtin.RequireSuccess(rt, code, "spend voucher verification failed")
}
Merges用于将多个lane合并。
创建支付通道
支付通道创建者通过智能合约创建一个支付通道,支付通道发起者需要提供支付通道发起者地址和支付通道接收者地址参数
type ConstructorParams struct {
From addr.Address // Payer
To addr.Address // Payee
}
创建完成后链上生成支付通道状态,返回支付通道的地址。交易双方通过支付通道完成交易
注意:支付通道创建者不一定是支付通道发起者。任何人都可以为一对交易者创建支付通道。只需要向智能合约提供通道发起者地址和支付通道接收者地址参数和一笔手续费。但是一般情况下支付通道发起者是支付通道创建者
UpdateChannelState
UpdateChannelState用于兑换凭证,更新支付通道状态,只有凭证接收方才可以兑换凭证。UpdateChannelState方法除了更新支付金额外,还可以通过MinSettleHeight字段更新支付通道状态的MinSettleHeight。即便在Settle方法调用后,停止兑换支付凭证时间还可以被凭证更新。
type UpdateChannelStateParams struct {
Sv SignedVoucher
Secret []byte
}
合约UpdateChannelState验证凭证和更新支付通道状态,逻辑主要有以下部分:
1. 验证调用者是否是支付者或者接收者,只有支付通道的双方才可以创建凭证
2. 验证签名是否是凭证创建者
3. 验证凭证的时间是否可以接受
4. 验证SecretPreimage和Secret是否匹配
5. Extra验证凭证创建者要求的额外条件是否满足
6. 凭证对应的lane的nonce是否大于支付通道最大的nonce
7. 处理合并凭证
8. 检查余额是否足够最新的支付金额
9. 更新状态
Nonce最高的支付凭证会刷新掉其他支付凭证,例如以下情况:
当前支付通道有余额100FiL,支付资金为0。有某lane的三个支付凭证(voucher_val,voucher_nonce):(10,1),(20,2),(30,3),如果接收者兑换了(20,2),则支付资金为20。(10,1)支付凭证不可以被兑换了。如果接着兑换(30,3),支付资金为30。支付凭证是一种无状态的凭证。 支付通道发起者根据支付通道接收者提供的服务支付支付凭证,接收到支付凭证的一方可以兑换凭证。兑换凭证的过程需要和链交互,会产生gas费。所以只需要在服务结束时将Nonce最高的支付凭证兑换即可。在支付凭证兑换之前可以在链上检查支付通道是否有足够的余额可以兑换支付凭证,这个过程不需要消耗gas费。
Settle
交易双方都可以调用Settle方法,用于设置支付通道的允许兑换凭证的截止兑换高度,Settle方法不需要参数 主要逻辑:
1. 验证调用者是否是支付者或者接收者,只有支付通道的双方才可以调用Settle
2. 验证截止兑换高度是否已经被设置
3. 设置截止日期为Settle方法后12小时但不可以小于支付通道MinSettleHeight值
注意:但是还可以在截止兑换高度之前通过凭证更改截止兑换高度
Collect
交易双方都可以调用Collect方法,用于结算资金 主要逻辑:
1. 验证调用者是否是支付者或者接收者,只有支付通道的双方才可以调用Collect
2. 验证是否到达截止兑换高度
3. 将ToSend资金转给支付通道接收者,销毁支付通道并将剩余资金归还支付通道发起者
当前实现问题
支付凭证在合并时,合并的不同的lane的支付凭证必须有相应nonce小的凭证已经提交上链。所以只有在相应lane的凭证已经上链,当需要更新时才有作用。但是使用合并还引入另一个问题,支付通道设计思想是最终凭证并不受以前凭证影响,当时当使用合并时这种思想被打破。被合并之后的lane需要再次更新凭证时受接收者最后一次相应lane兑换的凭证影响。
思考
支付凭证总是指当前支付线的支付总数,支付凭证虽然不是绝对递增的,但是由于兑换的权利在于另一方。nonce值更大的支付凭证会刷新掉以前的支付凭证,所以支付通道接收者肯定不会乐意兑换一个资金更少的凭证。
支付通道发起者,由于是支付资金的一方,如果发送的支付凭证少于上一个支付凭证支付的金额。作为支付凭证的兑换者-支付通道接收者不会乐意接受这个支付凭证,可以用上一个金额更大的支付凭证作为兑换的凭证并停止提供服务。所以,支付通道发起者为了继续接受来自支付通道接收者提供的服务,应当提供越来越大的支付凭证。
支付通道接收者,是获取支付通道资金的一方,如果支付通道接收者随意发起一个更高金额的支付凭证,作为支付凭证的兑换者-一个理性的支付通道发起者肯定不会接收这个支付凭证,所以这个支付凭证也就毫无意义。当然,如果支付通道接收者由于提供的提供的服务没有完成,出于人性的善意,可以会退回部分资金给支付通道发起者,支付通道接收者可能会发起一个较低金额的支付凭证,最为支付的一方,支付通道发起者肯定也会欣喜的接受并感叹世界上还是有好人。