paychmgr: store proofs with vouchers

This commit is contained in:
Łukasz Magiera 2019-09-09 15:59:07 +02:00
parent 8b29c98919
commit 5200a37349
7 changed files with 142 additions and 21 deletions

View File

@ -114,7 +114,7 @@ type FullNode interface {
PaychVoucherCheckValid(context.Context, address.Address, *types.SignedVoucher) error PaychVoucherCheckValid(context.Context, address.Address, *types.SignedVoucher) error
PaychVoucherCheckSpendable(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error) PaychVoucherCheckSpendable(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error)
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error) PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error)
PaychVoucherAdd(context.Context, address.Address, *types.SignedVoucher) error PaychVoucherAdd(context.Context, address.Address, *types.SignedVoucher, []byte) error
PaychVoucherList(context.Context, address.Address) ([]*types.SignedVoucher, error) PaychVoucherList(context.Context, address.Address) ([]*types.SignedVoucher, error)
PaychVoucherSubmit(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error) PaychVoucherSubmit(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error)
} }

View File

@ -86,7 +86,7 @@ type FullNodeStruct struct {
PaychVoucherCheck func(context.Context, *types.SignedVoucher) error `perm:"read"` PaychVoucherCheck func(context.Context, *types.SignedVoucher) error `perm:"read"`
PaychVoucherCheckValid func(context.Context, address.Address, *types.SignedVoucher) error `perm:"read"` PaychVoucherCheckValid func(context.Context, address.Address, *types.SignedVoucher) error `perm:"read"`
PaychVoucherCheckSpendable func(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"` PaychVoucherCheckSpendable func(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
PaychVoucherAdd func(context.Context, address.Address, *types.SignedVoucher) error `perm:"write"` PaychVoucherAdd func(context.Context, address.Address, *types.SignedVoucher, []byte) error `perm:"write"`
PaychVoucherCreate func(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error) `perm:"sign"` PaychVoucherCreate func(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error) `perm:"sign"`
PaychVoucherList func(context.Context, address.Address) ([]*types.SignedVoucher, error) `perm:"write"` PaychVoucherList func(context.Context, address.Address) ([]*types.SignedVoucher, error) `perm:"write"`
PaychVoucherSubmit func(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error) `perm:"sign"` PaychVoucherSubmit func(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error) `perm:"sign"`
@ -303,8 +303,8 @@ func (c *FullNodeStruct) PaychVoucherCheckSpendable(ctx context.Context, addr ad
return c.Internal.PaychVoucherCheckSpendable(ctx, addr, sv, secret, proof) return c.Internal.PaychVoucherCheckSpendable(ctx, addr, sv, secret, proof)
} }
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *types.SignedVoucher) error { func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *types.SignedVoucher, proof []byte) error {
return c.Internal.PaychVoucherAdd(ctx, addr, sv) return c.Internal.PaychVoucherAdd(ctx, addr, sv, proof)
} }
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*types.SignedVoucher, error) { func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*types.SignedVoucher, error) {

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"bytes"
"encoding/base64" "encoding/base64"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
@ -42,6 +43,24 @@ func (sv *SignedVoucher) EncodedString() (string, error) {
return base64.RawURLEncoding.EncodeToString(data), nil return base64.RawURLEncoding.EncodeToString(data), nil
} }
func (sv *SignedVoucher) Equals(other *SignedVoucher) bool {
// TODO: make this less bad
selfB, err := cbor.DumpObject(sv)
if err != nil {
log.Errorf("SignedVoucher.Equals: dump self: %s", err)
return false
}
otherB, err := cbor.DumpObject(other)
if err != nil {
log.Errorf("SignedVoucher.Equals: dump other: %s", err)
return false
}
return bytes.Equal(selfB, otherB)
}
func DecodeSignedVoucher(s string) (*SignedVoucher, error) { func DecodeSignedVoucher(s string) (*SignedVoucher, error) {
data, err := base64.RawURLEncoding.DecodeString(s) data, err := base64.RawURLEncoding.DecodeString(s)
if err != nil { if err != nil {

View File

@ -154,14 +154,14 @@ func (a *PaychAPI) PaychVoucherCheckSpendable(ctx context.Context, ch address.Ad
return a.PaychMgr.CheckVoucherSpendable(ctx, ch, sv, secret, proof) return a.PaychMgr.CheckVoucherSpendable(ctx, ch, sv, secret, proof)
} }
func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *types.SignedVoucher) error { func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *types.SignedVoucher, proof []byte) error {
_ = a.PaychMgr.TrackInboundChannel(ctx, ch) _ = a.PaychMgr.TrackInboundChannel(ctx, ch) // TODO: expose those calls
if err := a.PaychVoucherCheckValid(ctx, ch, sv); err != nil { if err := a.PaychVoucherCheckValid(ctx, ch, sv); err != nil {
return err return err
} }
return a.PaychMgr.AddVoucher(ctx, ch, sv) return a.PaychMgr.AddVoucher(ctx, ch, sv, proof)
} }
// PaychVoucherCreate creates a new signed voucher on the given payment channel // PaychVoucherCreate creates a new signed voucher on the given payment channel
@ -199,7 +199,7 @@ func (a *PaychAPI) paychVoucherCreate(ctx context.Context, pch address.Address,
sv.Signature = sig sv.Signature = sig
if err := a.PaychMgr.AddVoucher(ctx, pch, sv); err != nil { if err := a.PaychMgr.AddVoucher(ctx, pch, sv, nil); err != nil {
return nil, xerrors.Errorf("failed to persist voucher: %w", err) return nil, xerrors.Errorf("failed to persist voucher: %w", err)
} }
@ -207,7 +207,17 @@ func (a *PaychAPI) paychVoucherCreate(ctx context.Context, pch address.Address,
} }
func (a *PaychAPI) PaychVoucherList(ctx context.Context, pch address.Address) ([]*types.SignedVoucher, error) { func (a *PaychAPI) PaychVoucherList(ctx context.Context, pch address.Address) ([]*types.SignedVoucher, error) {
return a.PaychMgr.ListVouchers(ctx, pch) vi, err := a.PaychMgr.ListVouchers(ctx, pch)
if err != nil {
return nil, err
}
out := make([]*types.SignedVoucher, len(vi))
for k, v := range vi {
out[k] = v.Voucher
}
return out, nil
} }
func (a *PaychAPI) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *types.SignedVoucher) (cid.Cid, error) { func (a *PaychAPI) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *types.SignedVoucher) (cid.Cid, error) {

View File

@ -4,16 +4,19 @@ import (
"context" "context"
"fmt" "fmt"
hamt "github.com/ipfs/go-hamt-ipld"
logging "github.com/ipfs/go-log"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/vm"
hamt "github.com/ipfs/go-hamt-ipld"
) )
var log = logging.Logger("paych")
type Manager struct { type Manager struct {
chain *store.ChainStore chain *store.ChainStore
store *Store store *Store
@ -118,6 +121,24 @@ func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address
return false, err return false, err
} }
if sv.Extra != nil && proof == nil {
known, err := pm.ListVouchers(ctx, ch)
if err != nil {
return false, err
}
for _, v := range known {
if v.Proof != nil && v.Voucher.Equals(sv) {
log.Info("CheckVoucherSpendable: using stored proof")
proof = v.Proof
break
}
}
if proof == nil {
log.Warn("CheckVoucherSpendable: nil proof for voucher with validation")
}
}
enc, err := actors.SerializeParams(&actors.PCAUpdateChannelStateParams{ enc, err := actors.SerializeParams(&actors.PCAUpdateChannelStateParams{
Sv: *sv, Sv: *sv,
Secret: secret, Secret: secret,
@ -186,15 +207,15 @@ func (pm *Manager) getPaychOwner(ctx context.Context, ch address.Address) (addre
return address.NewFromBytes(ret.Return) return address.NewFromBytes(ret.Return)
} }
func (pm *Manager) AddVoucher(ctx context.Context, ch address.Address, sv *types.SignedVoucher) error { func (pm *Manager) AddVoucher(ctx context.Context, ch address.Address, sv *types.SignedVoucher, proof []byte) error {
if err := pm.CheckVoucherValid(ctx, ch, sv); err != nil { if err := pm.CheckVoucherValid(ctx, ch, sv); err != nil {
return err return err
} }
return pm.store.AddVoucher(ch, sv) return pm.store.AddVoucher(ch, sv, proof)
} }
func (pm *Manager) ListVouchers(ctx context.Context, ch address.Address) ([]*types.SignedVoucher, error) { func (pm *Manager) ListVouchers(ctx context.Context, ch address.Address) ([]*VoucherInfo, error) {
// TODO: just having a passthrough method like this feels odd. Seems like // TODO: just having a passthrough method like this feels odd. Seems like
// there should be some filtering we're doing here // there should be some filtering we're doing here
return pm.store.VouchersForPaych(ch) return pm.store.VouchersForPaych(ch)
@ -208,9 +229,9 @@ func (pm *Manager) NextNonceForLane(ctx context.Context, ch address.Address, lan
var maxnonce uint64 var maxnonce uint64
for _, v := range vouchers { for _, v := range vouchers {
if v.Lane == lane { if v.Voucher.Lane == lane {
if v.Nonce > maxnonce { if v.Voucher.Nonce > maxnonce {
maxnonce = v.Nonce maxnonce = v.Voucher.Nonce
} }
} }
} }

View File

@ -1,6 +1,7 @@
package paych package paych
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -18,6 +19,7 @@ import (
var ErrChannelNotTracked = errors.New("channel not tracked") var ErrChannelNotTracked = errors.New("channel not tracked")
func init() { func init() {
cbor.RegisterCborType(VoucherInfo{})
cbor.RegisterCborType(ChannelInfo{}) cbor.RegisterCborType(ChannelInfo{})
} }
@ -37,11 +39,16 @@ const (
DirOutbound = 2 DirOutbound = 2
) )
type VoucherInfo struct {
Voucher *types.SignedVoucher
Proof []byte
}
type ChannelInfo struct { type ChannelInfo struct {
Channel address.Address Channel address.Address
ControlAddr address.Address ControlAddr address.Address
Direction int Direction int
Vouchers []*types.SignedVoucher Vouchers []*VoucherInfo
} }
func dskeyForChannel(addr address.Address) datastore.Key { func dskeyForChannel(addr address.Address) datastore.Key {
@ -118,18 +125,53 @@ func (ps *Store) ListChannels() ([]address.Address, error) {
return out, nil return out, nil
} }
func (ps *Store) AddVoucher(ch address.Address, sv *types.SignedVoucher) error { func (ps *Store) AddVoucher(ch address.Address, sv *types.SignedVoucher, proof []byte) error {
ci, err := ps.getChannelInfo(ch) ci, err := ps.getChannelInfo(ch)
if err != nil { if err != nil {
return err return err
} }
ci.Vouchers = append(ci.Vouchers, sv) svs, err := sv.EncodedString()
if err != nil {
return err
}
// look for duplicates
for i, v := range ci.Vouchers {
osvs, err := v.Voucher.EncodedString()
if err != nil {
return err
}
if osvs != svs {
continue
}
if v.Proof != nil {
if !bytes.Equal(v.Proof, proof) {
log.Warnf("AddVoucher: multiple proofs for single voucher: v:'%s', storing both", svs)
break
}
log.Warnf("AddVoucher: voucher re-added with matching proof: v:'%s'", svs)
return nil
}
log.Warnf("AddVoucher: adding proof to stored voucher")
ci.Vouchers[i] = &VoucherInfo{
Voucher: v.Voucher,
Proof: proof,
}
return ps.putChannelInfo(ci)
}
ci.Vouchers = append(ci.Vouchers, &VoucherInfo{
Voucher: sv,
Proof: proof,
})
return ps.putChannelInfo(ci) return ps.putChannelInfo(ci)
} }
func (ps *Store) VouchersForPaych(ch address.Address) ([]*types.SignedVoucher, error) { func (ps *Store) VouchersForPaych(ch address.Address) ([]*VoucherInfo, error) {
ci, err := ps.getChannelInfo(ch) ci, err := ps.getChannelInfo(ch)
if err != nil { if err != nil {
return nil, err return nil, err

29
paych/watcher.go Normal file
View File

@ -0,0 +1,29 @@
package paych
import (
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/events"
"github.com/filecoin-project/go-lotus/chain/types"
)
type watchedLane struct {
bestVoucher *types.SignedVoucher
closed bool
}
type inboundWatcher struct {
ev *events.Events
paych *address.Address
control address.Address
lanes map[uint64]*watchedLane
closeAt uint64 // at what H do we plan to call close
collectAt uint64 // at what H do we plan to call collect
}