lotus/chain/actors/actor_paych.go
2019-08-08 10:58:21 -07:00

289 lines
6.6 KiB
Go

package actors
import (
"bytes"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/types"
)
const ChannelClosingDelay = 6 * 60 * 2 // six hours
func init() {
cbor.RegisterCborType(PaymentChannelActorState{})
cbor.RegisterCborType(PCAConstructorParams{})
cbor.RegisterCborType(SignedVoucher{})
cbor.RegisterCborType(Merge{})
cbor.RegisterCborType(LaneState{})
cbor.RegisterCborType(UpdateChannelState{})
cbor.RegisterCborType(PaymentInfo{})
}
type PaymentChannelActor struct{}
type PaymentInfo struct {
PayChActor address.Address
Payer address.Address
ChannelMessage cid.Cid
Vouchers []SignedVoucher
}
type LaneState struct {
Closed bool
Redeemed types.BigInt
Nonce uint64
}
type PaymentChannelActorState struct {
From address.Address
To address.Address
ChannelTotal types.BigInt
ToSend types.BigInt
ClosingAt uint64
MinCloseHeight uint64
LaneStates map[uint64]*LaneState
}
func (pca PaymentChannelActor) Exports() []interface{} {
return []interface{}{
0: pca.Constructor,
1: pca.UpdateChannelState,
2: pca.Close,
3: pca.Collect,
}
}
type PCAConstructorParams struct {
To address.Address
VerifActor address.Address
VerifMethod uint64
}
func (pca PaymentChannelActor) Constructor(act *types.Actor, vmctx types.VMContext, params *PCAConstructorParams) ([]byte, ActorError) {
var self PaymentChannelActorState
self.From = vmctx.Origin()
self.To = params.To
storage := vmctx.Storage()
c, err := storage.Put(self)
if err != nil {
return nil, err
}
if err := storage.Commit(EmptyCBOR, c); err != nil {
return nil, err
}
return nil, nil
}
type SignedVoucher struct {
TimeLock uint64
SecretPreimage []byte
Extra *ModVerifyParams
Lane uint64
Nonce uint64
Amount types.BigInt
MinCloseHeight uint64
Merges []Merge
Signature types.Signature
}
type ModVerifyParams struct {
Actor address.Address
Method uint64
Data []byte
}
type Merge struct {
Lane uint64
Nonce uint64
}
type UpdateChannelState struct {
Sv SignedVoucher
Secret []byte
Proof []byte
}
func hash(b []byte) []byte {
panic("blake 2b hash pls")
}
func (pca PaymentChannelActor) UpdateChannelState(act *types.Actor, vmctx types.VMContext, params *UpdateChannelState) ([]byte, ActorError) {
var self PaymentChannelActorState
oldstate := vmctx.Storage().GetHead()
storage := vmctx.Storage()
if err := storage.Get(oldstate, &self); err != nil {
return nil, err
}
sv := params.Sv
if err := vmctx.VerifySignature(sv.Signature, self.From); err != nil {
return nil, err
}
if vmctx.BlockHeight() < sv.TimeLock {
return nil, aerrors.New(2, "cannot use this voucher yet!")
}
if sv.SecretPreimage != nil {
if !bytes.Equal(hash(params.Secret), sv.SecretPreimage) {
return nil, aerrors.New(3, "Incorrect secret!")
}
}
if sv.Extra != nil {
encoded, err := SerializeParams([]interface{}{sv.Extra.Data, params.Proof})
if err != nil {
return nil, err
}
_, err = vmctx.Send(sv.Extra.Actor, sv.Extra.Method, types.NewInt(0), encoded)
if err != nil {
return nil, aerrors.New(4, "spend voucher verification failed")
}
}
ls := self.LaneStates[sv.Lane]
if ls.Closed {
return nil, aerrors.New(5, "cannot redeem a voucher on a closed lane")
}
if ls.Nonce > sv.Nonce {
return nil, aerrors.New(6, "voucher has an outdated nonce, cannot redeem")
}
mergeValue := types.NewInt(0)
for _, merge := range sv.Merges {
if merge.Lane == sv.Lane {
return nil, aerrors.New(7, "voucher cannot merge its own lane")
}
ols := self.LaneStates[merge.Lane]
if ols.Nonce >= merge.Nonce {
return nil, aerrors.New(8, "merge in voucher has outdated nonce, cannot redeem")
}
mergeValue = types.BigAdd(mergeValue, ols.Redeemed)
ols.Nonce = merge.Nonce
}
ls.Nonce = sv.Nonce
balanceDelta := types.BigSub(sv.Amount, types.BigAdd(mergeValue, ls.Redeemed))
ls.Redeemed = sv.Amount
newSendBalance := types.BigAdd(self.ToSend, balanceDelta)
if types.BigCmp(newSendBalance, types.NewInt(0)) < 0 {
// TODO: is this impossible?
return nil, aerrors.New(9, "voucher would leave channel balance negative")
}
if types.BigCmp(newSendBalance, self.ChannelTotal) > 0 {
return nil, aerrors.New(10, "not enough funds in channel to cover voucher")
}
self.ToSend = newSendBalance
if sv.MinCloseHeight != 0 {
if self.ClosingAt != 0 && self.ClosingAt < sv.MinCloseHeight {
self.ClosingAt = sv.MinCloseHeight
}
if self.MinCloseHeight < sv.MinCloseHeight {
self.MinCloseHeight = sv.MinCloseHeight
}
}
ncid, err := storage.Put(self)
if err != nil {
return nil, err
}
if err := storage.Commit(oldstate, ncid); err != nil {
return nil, err
}
return nil, nil
}
func (pca PaymentChannelActor) Close(act *types.Actor, vmctx types.VMContext, params struct{}) ([]byte, aerrors.ActorError) {
var self PaymentChannelActorState
storage := vmctx.Storage()
oldstate := storage.GetHead()
if err := storage.Get(oldstate, &self); err != nil {
return nil, err
}
if vmctx.Message().From != self.From && vmctx.Message().From != self.To {
return nil, aerrors.New(1, "not authorized to close channel")
}
if self.ClosingAt != 0 {
return nil, aerrors.New(2, "channel already closing")
}
self.ClosingAt = vmctx.BlockHeight() + ChannelClosingDelay
if self.ClosingAt < self.MinCloseHeight {
self.ClosingAt = self.MinCloseHeight
}
ncid, err := storage.Put(self)
if err != nil {
return nil, err
}
if err := storage.Commit(oldstate, ncid); err != nil {
return nil, err
}
return nil, nil
}
func (pca PaymentChannelActor) Collect(act *types.Actor, vmctx types.VMContext, params struct{}) ([]byte, aerrors.ActorError) {
var self PaymentChannelActorState
storage := vmctx.Storage()
oldstate := storage.GetHead()
if err := storage.Get(oldstate, &self); err != nil {
return nil, err
}
if self.ClosingAt == 0 {
return nil, aerrors.New(1, "payment channel not closing or closed")
}
if vmctx.BlockHeight() < self.ClosingAt {
return nil, aerrors.New(2, "payment channel not closed yet")
}
_, err := vmctx.Send(self.From, 0, types.BigSub(self.ChannelTotal, self.ToSend), nil)
if err != nil {
return nil, err
}
_, err = vmctx.Send(self.To, 0, self.ToSend, nil)
if err != nil {
return nil, err
}
self.ChannelTotal = types.NewInt(0)
self.ToSend = types.NewInt(0)
ncid, err := storage.Put(self)
if err != nil {
return nil, err
}
if err := storage.Commit(oldstate, ncid); err != nil {
return nil, err
}
return nil, nil
}