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
PaychVoucherCheckSpendable(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, 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)
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"`
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"`
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"`
PaychVoucherList func(context.Context, address.Address) ([]*types.SignedVoucher, error) `perm:"write"`
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)
}
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *types.SignedVoucher) error {
return c.Internal.PaychVoucherAdd(ctx, addr, sv)
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *types.SignedVoucher, proof []byte) error {
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) {

View File

@ -1,6 +1,7 @@
package types
import (
"bytes"
"encoding/base64"
"github.com/filecoin-project/go-lotus/chain/address"
@ -42,6 +43,24 @@ func (sv *SignedVoucher) EncodedString() (string, error) {
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) {
data, err := base64.RawURLEncoding.DecodeString(s)
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)
}
func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *types.SignedVoucher) error {
_ = a.PaychMgr.TrackInboundChannel(ctx, ch)
func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *types.SignedVoucher, proof []byte) error {
_ = a.PaychMgr.TrackInboundChannel(ctx, ch) // TODO: expose those calls
if err := a.PaychVoucherCheckValid(ctx, ch, sv); err != nil {
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
@ -199,7 +199,7 @@ func (a *PaychAPI) paychVoucherCreate(ctx context.Context, pch address.Address,
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)
}
@ -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) {
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) {

View File

@ -4,16 +4,19 @@ import (
"context"
"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/address"
"github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
hamt "github.com/ipfs/go-hamt-ipld"
)
var log = logging.Logger("paych")
type Manager struct {
chain *store.ChainStore
store *Store
@ -118,6 +121,24 @@ func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address
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{
Sv: *sv,
Secret: secret,
@ -186,15 +207,15 @@ func (pm *Manager) getPaychOwner(ctx context.Context, ch address.Address) (addre
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 {
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
// there should be some filtering we're doing here
return pm.store.VouchersForPaych(ch)
@ -208,9 +229,9 @@ func (pm *Manager) NextNonceForLane(ctx context.Context, ch address.Address, lan
var maxnonce uint64
for _, v := range vouchers {
if v.Lane == lane {
if v.Nonce > maxnonce {
maxnonce = v.Nonce
if v.Voucher.Lane == lane {
if v.Voucher.Nonce > maxnonce {
maxnonce = v.Voucher.Nonce
}
}
}

View File

@ -1,6 +1,7 @@
package paych
import (
"bytes"
"errors"
"fmt"
"strings"
@ -18,6 +19,7 @@ import (
var ErrChannelNotTracked = errors.New("channel not tracked")
func init() {
cbor.RegisterCborType(VoucherInfo{})
cbor.RegisterCborType(ChannelInfo{})
}
@ -37,11 +39,16 @@ const (
DirOutbound = 2
)
type VoucherInfo struct {
Voucher *types.SignedVoucher
Proof []byte
}
type ChannelInfo struct {
Channel address.Address
ControlAddr address.Address
Direction int
Vouchers []*types.SignedVoucher
Vouchers []*VoucherInfo
}
func dskeyForChannel(addr address.Address) datastore.Key {
@ -118,18 +125,53 @@ func (ps *Store) ListChannels() ([]address.Address, error) {
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)
if err != nil {
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)
}
func (ps *Store) VouchersForPaych(ch address.Address) ([]*types.SignedVoucher, error) {
func (ps *Store) VouchersForPaych(ch address.Address) ([]*VoucherInfo, error) {
ci, err := ps.getChannelInfo(ch)
if err != nil {
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
}