Skip to content

Commit

Permalink
Merge pull request #74 from filecoin-project/feat/payment-channels
Browse files Browse the repository at this point in the history
implement initial payment channel actor
  • Loading branch information
whyrusleeping authored Jul 25, 2019
2 parents 7925c54 + bcdb3ed commit 663cdbe
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 101 deletions.
2 changes: 1 addition & 1 deletion api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ type FullNode interface {
WalletNew(context.Context, string) (address.Address, error)
WalletList(context.Context) ([]address.Address, error)
WalletBalance(context.Context, address.Address) (types.BigInt, error)
WalletSign(context.Context, address.Address, []byte) (*chain.Signature, error)
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
WalletDefaultAddress(context.Context) (address.Address, error)

// Really not sure where this belongs. It could go on the wallet, or the message pool, or the chain...
Expand Down
4 changes: 2 additions & 2 deletions api/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type FullNodeStruct struct {
WalletNew func(context.Context, string) (address.Address, error) `perm:"write"`
WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"`
WalletSign func(context.Context, address.Address, []byte) (*chain.Signature, error) `perm:"sign"`
WalletSign func(context.Context, address.Address, []byte) (*types.Signature, error) `perm:"sign"`
WalletDefaultAddress func(context.Context) (address.Address, error) `perm:"write"`
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`

Expand Down Expand Up @@ -147,7 +147,7 @@ func (c *FullNodeStruct) WalletBalance(ctx context.Context, a address.Address) (
return c.Internal.WalletBalance(ctx, a)
}

func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*chain.Signature, error) {
func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) {
return c.Internal.WalletSign(ctx, k, msg)
}

Expand Down
281 changes: 281 additions & 0 deletions chain/actors/actor_paych.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
package actors

import (
"bytes"

"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"

cbor "github.com/ipfs/go-ipld-cbor"
)

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{})
}

type PaymentChannelActor struct{}

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

VerifActor address.Address
VerifMethod uint64
}

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
self.VerifActor = params.VerifActor
self.VerifMethod = params.VerifMethod

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 []byte
Lane uint64
Nonce uint64
Amount types.BigInt
MinCloseHeight uint64

Merges []Merge

Signature types.Signature
}

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 {
if self.VerifActor == address.Undef {
return nil, aerrors.New(4, "no verifActor for extra data")
}

encoded, err := SerializeParams([]interface{}{sv.Extra, params.Proof})
if err != nil {
return nil, err
}

_, err = vmctx.Send(self.VerifActor, self.VerifMethod, types.NewInt(0), encoded)
if err != nil {
return nil, aerrors.New(5, "spend voucher verification failed")
}
}

ls := self.LaneStates[sv.Lane]
if ls.Closed {
return nil, aerrors.New(6, "cannot redeem a voucher on a closed lane")
}

if ls.Nonce > sv.Nonce {
return nil, aerrors.New(7, "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(8, "voucher cannot merge its own lane")
}

ols := self.LaneStates[merge.Lane]

if ols.Nonce >= merge.Nonce {
return nil, aerrors.New(9, "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(10, "voucher would leave channel balance negative")
}

if types.BigCmp(newSendBalance, self.ChannelTotal) > 0 {
return nil, aerrors.New(11, "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
}
2 changes: 1 addition & 1 deletion chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
func MakeGenesisBlock(bs bstore.Blockstore, w *Wallet) (*GenesisBootstrap, error) {
fmt.Println("at end of make Genesis block")

minerAddr, err := w.GenerateKey(KTSecp256k1)
minerAddr, err := w.GenerateKey(types.KTSecp256k1)
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions chain/mining.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti

fmt.Printf("adding %d messages to block...", len(msgs))
var msgCids []cid.Cid
var blsSigs []Signature
var blsSigs []types.Signature
var receipts []interface{}
for _, msg := range msgs {
if msg.Signature.TypeCode() == 2 {
Expand Down Expand Up @@ -108,7 +108,7 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti
return fullBlock, nil
}

func aggregateSignatures(sigs []Signature) (Signature, error) {
func aggregateSignatures(sigs []types.Signature) (types.Signature, error) {
var blsSigs []bls.Signature
for _, s := range sigs {
var bsig bls.Signature
Expand All @@ -117,8 +117,8 @@ func aggregateSignatures(sigs []Signature) (Signature, error) {
}

aggSig := bls.Aggregate(blsSigs)
return Signature{
Type: KTBLS,
return types.Signature{
Type: types.KTBLS,
Data: aggSig[:],
}, nil
}
Expand Down
Loading

0 comments on commit 663cdbe

Please sign in to comment.