Merge pull request #183 from filecoin-project/feat/stor-deal-payments
Payment integration in deals
This commit is contained in:
commit
2f03ac000e
15
api/api.go
15
api/api.go
@ -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)
|
||||
}
|
||||
@ -181,7 +181,18 @@ type ActorState struct {
|
||||
State interface{}
|
||||
}
|
||||
|
||||
type PaychStatus struct{}
|
||||
type PCHDir int
|
||||
|
||||
const (
|
||||
PCHUndef PCHDir = iota
|
||||
PCHInbound
|
||||
PCHOutbound
|
||||
)
|
||||
|
||||
type PaychStatus struct {
|
||||
ControlAddr address.Address
|
||||
Direction PCHDir
|
||||
}
|
||||
|
||||
type MinerPower struct {
|
||||
MinerPower types.BigInt
|
||||
|
@ -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) {
|
||||
|
@ -7,6 +7,10 @@ const UnixfsLinksPerLevel = 1024
|
||||
|
||||
const SectorSize = 1024
|
||||
|
||||
const PaymentChannelClosingDelay = 6 * 60 * 2 // six hours
|
||||
|
||||
const DealVoucherSkewLimit = 10
|
||||
|
||||
const ForkLengthThreshold = 20
|
||||
|
||||
// TODO: Move other important consts here
|
||||
|
@ -5,14 +5,13 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
@ -211,7 +210,8 @@ func (ias *InitActorState) Lookup(cst *hamt.CborIpldStore, addr address.Address)
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
val, err := amap.Find(context.TODO(), string(addr.Bytes()))
|
||||
var val interface{}
|
||||
err = amap.Find(context.TODO(), string(addr.Bytes()), &val)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
@ -2,15 +2,13 @@ package actors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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"
|
||||
"github.com/filecoin-project/go-lotus/lib/sectorbuilder"
|
||||
|
||||
amt "github.com/filecoin-project/go-amt-ipld"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
@ -181,13 +179,14 @@ func (sma StorageMinerActor) StorageMinerConstructor(act *types.Actor, vmctx typ
|
||||
}
|
||||
|
||||
var self StorageMinerActorState
|
||||
nd := hamt.NewNode(vmctx.Ipld())
|
||||
sectors, nerr := vmctx.Ipld().Put(context.TODO(), nd)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Escalate(nerr, "could not put in storage")
|
||||
sectors := amt.NewAMT(types.WrapStorage(vmctx.Storage()))
|
||||
scid, serr := sectors.Flush()
|
||||
if serr != nil {
|
||||
return nil, aerrors.Escalate(serr, "initializing AMT")
|
||||
}
|
||||
self.Sectors = sectors
|
||||
self.ProvingSet = sectors
|
||||
|
||||
self.Sectors = scid
|
||||
self.ProvingSet = scid
|
||||
self.Info = minfocid
|
||||
|
||||
storage := vmctx.Storage()
|
||||
@ -383,7 +382,7 @@ func GetFromSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, sectorID
|
||||
var comms [][]byte
|
||||
err = ssr.Get(sectorID, &comms)
|
||||
if err != nil {
|
||||
if _, ok := err.(amt.ErrNotFound); ok {
|
||||
if _, ok := err.(*amt.ErrNotFound); ok {
|
||||
return false, nil, nil, nil
|
||||
}
|
||||
return false, nil, nil, aerrors.Escalate(err, "failed to find sector in sector set")
|
||||
@ -559,7 +558,7 @@ func (sma StorageMinerActor) PaymentVerifyInclusion(act *types.Actor, vmctx type
|
||||
return nil, aerrors.New(1, "miner does not have required sector")
|
||||
}
|
||||
|
||||
ok, err := sectorbuilder.VerifyPieceInclusionProof(mi.SectorSize.Uint64(), voucherData.PieceSize.Uint64(), voucherData.CommP, commD, params.Proof)
|
||||
ok, err := sectorbuilder.VerifyPieceInclusionProof(mi.SectorSize.Uint64(), voucherData.PieceSize.Uint64(), voucherData.CommP, commD, proof.Proof)
|
||||
if err != nil {
|
||||
return nil, aerrors.Escalate(err, "verify piece inclusion proof failed")
|
||||
}
|
||||
|
@ -7,13 +7,12 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/build"
|
||||
"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{})
|
||||
@ -29,7 +28,7 @@ type PaymentInfo struct {
|
||||
Payer address.Address
|
||||
ChannelMessage cid.Cid
|
||||
|
||||
Vouchers []types.SignedVoucher
|
||||
Vouchers []*types.SignedVoucher
|
||||
}
|
||||
|
||||
type LaneState struct {
|
||||
@ -132,19 +131,19 @@ func (pca PaymentChannelActor) UpdateChannelState(act *types.Actor, vmctx types.
|
||||
|
||||
if sv.SecretPreimage != nil {
|
||||
if !bytes.Equal(hash(params.Secret), sv.SecretPreimage) {
|
||||
return nil, aerrors.New(3, "Incorrect secret!")
|
||||
return nil, aerrors.New(3, "incorrect secret!")
|
||||
}
|
||||
}
|
||||
|
||||
if sv.Extra != nil {
|
||||
encoded, err := SerializeParams([]interface{}{sv.Extra.Data, params.Proof})
|
||||
encoded, err := SerializeParams(PaymentVerifyParams{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")
|
||||
return nil, aerrors.Newf(4, "spend voucher verification failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,7 +230,7 @@ func (pca PaymentChannelActor) Close(act *types.Actor, vmctx types.VMContext, pa
|
||||
return nil, aerrors.New(2, "channel already closing")
|
||||
}
|
||||
|
||||
self.ClosingAt = vmctx.BlockHeight() + ChannelClosingDelay
|
||||
self.ClosingAt = vmctx.BlockHeight() + build.PaymentChannelClosingDelay
|
||||
if self.ClosingAt < self.MinCloseHeight {
|
||||
self.ClosingAt = self.MinCloseHeight
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package deals
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/go-lotus/chain/types"
|
||||
"github.com/filecoin-project/go-lotus/storage/sectorblocks"
|
||||
"math"
|
||||
|
||||
@ -34,6 +35,8 @@ type MinerDeal struct {
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
pricePerByteBlock types.BigInt // how much we want for storing one byte for one block
|
||||
|
||||
secst *sectorblocks.SectorBlocks
|
||||
full api.FullNode
|
||||
|
||||
@ -74,6 +77,8 @@ func NewHandler(ds dtypes.MetadataDS, secst *sectorblocks.SectorBlocks, dag dtyp
|
||||
dag: dag,
|
||||
full: fullNode,
|
||||
|
||||
pricePerByteBlock: types.NewInt(3), // TODO: allow setting
|
||||
|
||||
conns: map[cid.Cid]inet.Stream{},
|
||||
|
||||
incoming: make(chan MinerDeal),
|
||||
|
@ -1,12 +1,17 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/filecoin-project/go-lotus/build"
|
||||
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/chain/actors"
|
||||
"github.com/filecoin-project/go-lotus/chain/types"
|
||||
"github.com/filecoin-project/go-lotus/lib/sectorbuilder"
|
||||
"github.com/filecoin-project/go-lotus/storage/sectorblocks"
|
||||
)
|
||||
@ -30,6 +35,68 @@ func (h *Handler) handle(ctx context.Context, deal MinerDeal, cb handlerFunc, ne
|
||||
|
||||
// ACCEPTED
|
||||
|
||||
func (h *Handler) validateVouchers(ctx context.Context, deal MinerDeal) error {
|
||||
curHead, err := h.full.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, voucher := range deal.Proposal.Payment.Vouchers {
|
||||
err := h.full.PaychVoucherCheckValid(ctx, deal.Proposal.Payment.PayChActor, voucher)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("validating payment voucher %d: %w", i, err)
|
||||
}
|
||||
|
||||
if voucher.Extra == nil {
|
||||
return xerrors.Errorf("validating payment voucher %d: voucher.Extra not set")
|
||||
}
|
||||
|
||||
if voucher.Extra.Actor != deal.Proposal.MinerAddress {
|
||||
return xerrors.Errorf("validating payment voucher %d: extra params actor didn't match miner address in proposal: '%s' != '%s'", i, voucher.Extra.Actor, deal.Proposal.MinerAddress)
|
||||
}
|
||||
if voucher.Extra.Method != actors.MAMethods.PaymentVerifyInclusion {
|
||||
return xerrors.Errorf("validating payment voucher %d: expected extra method %d, got %d", i, actors.MAMethods.PaymentVerifyInclusion, voucher.Extra.Method)
|
||||
}
|
||||
|
||||
var inclChallenge actors.PieceInclVoucherData
|
||||
if err := cbor.DecodeInto(voucher.Extra.Data, &inclChallenge); err != nil {
|
||||
return xerrors.Errorf("validating payment voucher %d: failed to decode storage voucher data for verification: %w", i, err)
|
||||
}
|
||||
if inclChallenge.PieceSize.Uint64() != deal.Proposal.Size {
|
||||
return xerrors.Errorf("validating payment voucher %d: paych challenge piece size didn't match deal proposal size: %d != %d", i, inclChallenge.PieceSize.Uint64(), deal.Proposal.Size)
|
||||
}
|
||||
if !bytes.Equal(inclChallenge.CommP, deal.Proposal.CommP) {
|
||||
return xerrors.Errorf("validating payment voucher %d: paych challenge commP didn't match deal proposal", i)
|
||||
}
|
||||
|
||||
maxClose := curHead.Height() + deal.Proposal.Duration + build.DealVoucherSkewLimit
|
||||
if voucher.MinCloseHeight > maxClose {
|
||||
return xerrors.Errorf("validating payment voucher %d: MinCloseHeight too high (%d), max expected: %d", i, voucher.MinCloseHeight, maxClose)
|
||||
}
|
||||
|
||||
if voucher.TimeLock > maxClose {
|
||||
return xerrors.Errorf("validating payment voucher %d: TimeLock too high (%d), max expected: %d", i, voucher.TimeLock, maxClose)
|
||||
}
|
||||
|
||||
if len(voucher.Merges) > 0 {
|
||||
return xerrors.Errorf("validating payment voucher %d: didn't expect any merges", i)
|
||||
}
|
||||
|
||||
// TODO: make sure that current laneStatus.Amount == 0
|
||||
|
||||
if types.BigCmp(voucher.Amount, deal.Proposal.TotalPrice) < 0 {
|
||||
return xerrors.Errorf("validating payment voucher %d: not enough funds in the voucher", i)
|
||||
}
|
||||
|
||||
minPrice := types.BigMul(types.BigMul(h.pricePerByteBlock, types.NewInt(deal.Proposal.Size)), types.NewInt(deal.Proposal.Duration))
|
||||
if types.BigCmp(minPrice, deal.Proposal.TotalPrice) > 0 {
|
||||
return xerrors.Errorf("validating payment voucher %d: minimum price: %s", i, minPrice)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) accept(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
switch deal.Proposal.SerializationMode {
|
||||
//case SerializationRaw:
|
||||
@ -39,7 +106,15 @@ func (h *Handler) accept(ctx context.Context, deal MinerDeal) (func(*MinerDeal),
|
||||
return nil, xerrors.Errorf("deal proposal with unsupported serialization: %s", deal.Proposal.SerializationMode)
|
||||
}
|
||||
|
||||
// TODO: check payment
|
||||
if err := h.validateVouchers(ctx, deal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, voucher := range deal.Proposal.Payment.Vouchers {
|
||||
if err := h.full.PaychVoucherAdd(ctx, deal.Proposal.Payment.PayChActor, voucher, nil); err != nil {
|
||||
return nil, xerrors.Errorf("consuming payment voucher %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("fetching data for a deal")
|
||||
err := h.sendSignedResponse(StorageDealResponse{
|
||||
@ -140,6 +215,24 @@ func (h *Handler) sealing(ctx context.Context, deal MinerDeal) (func(*MinerDeal)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proof := &actors.InclusionProof{
|
||||
Sector: deal.SectorID,
|
||||
Proof: ip.ProofElements,
|
||||
}
|
||||
proofB, err := cbor.DumpObject(proof)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// store proofs for channels
|
||||
for i, v := range deal.Proposal.Payment.Vouchers {
|
||||
if v.Extra.Method == actors.MAMethods.PaymentVerifyInclusion {
|
||||
if err := h.full.PaychVoucherAdd(ctx, deal.Proposal.Payment.PayChActor, v, proofB); err != nil {
|
||||
return nil, xerrors.Errorf("storing payment voucher %d proof: %w", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = h.sendSignedResponse(StorageDealResponse{
|
||||
State: Sealing,
|
||||
Proposal: deal.ProposalCid,
|
||||
|
@ -2,12 +2,11 @@ package gen
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
bls "github.com/filecoin-project/go-bls-sigs"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/filecoin-project/go-bls-sigs"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/pkg/errors"
|
||||
sharray "github.com/whyrusleeping/sharray"
|
||||
"github.com/whyrusleeping/sharray"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/chain/actors"
|
||||
|
@ -113,7 +113,7 @@ func (mp *MessagePool) Remove(from address.Address, nonce uint64) {
|
||||
func (mp *MessagePool) Pending() []*types.SignedMessage {
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
var out []*types.SignedMessage
|
||||
out := make([]*types.SignedMessage, 0)
|
||||
for _, mset := range mp.pending {
|
||||
for i := mset.startNonce; true; i++ {
|
||||
m, ok := mset.msgs[i]
|
||||
|
@ -100,7 +100,8 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) {
|
||||
return cact, nil
|
||||
}
|
||||
|
||||
thing, err := st.root.Find(context.TODO(), string(addr.Bytes()))
|
||||
var thing interface{}
|
||||
err := st.root.Find(context.TODO(), string(addr.Bytes()), &thing)
|
||||
if err != nil {
|
||||
if err == hamt.ErrNotFound {
|
||||
return nil, types.ErrActorNotFound
|
||||
|
@ -440,7 +440,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
|
||||
|
||||
if h.Timestamp < baseTs.MinTimestamp()+uint64(build.BlockDelay*len(h.Tickets)) {
|
||||
log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), len(h.Tickets))
|
||||
return xerrors.Errorf("block was generated too soon (timestamp < BLOCK_DELAY * len(tickets))")
|
||||
return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets))
|
||||
}
|
||||
|
||||
if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil {
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/polydawn/refmt/obj/atlas"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const BigIntMaxSerializedLen = 128 // is this big enough? or too big?
|
||||
@ -93,7 +94,7 @@ func (bi *BigInt) UnmarshalJSON(b []byte) error {
|
||||
if string(s) == "<nil>" {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to parse bigint string")
|
||||
return xerrors.Errorf("failed to parse bigint string: '%s'", string(b))
|
||||
}
|
||||
|
||||
bi.Int = i
|
||||
|
@ -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 {
|
||||
|
20
cli/paych.go
20
cli/paych.go
@ -202,7 +202,8 @@ var paychVoucherAddCmd = &cli.Command{
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
if err := api.PaychVoucherAdd(ctx, ch, sv); err != nil {
|
||||
// TODO: allow passing proof bytes
|
||||
if err := api.PaychVoucherAdd(ctx, ch, sv, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -213,6 +214,12 @@ var paychVoucherAddCmd = &cli.Command{
|
||||
var paychVoucherListCmd = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List stored vouchers for a given payment channel",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "export",
|
||||
Usage: "Print export strings",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() != 1 {
|
||||
return fmt.Errorf("must pass payment channel address")
|
||||
@ -236,7 +243,16 @@ var paychVoucherListCmd = &cli.Command{
|
||||
}
|
||||
|
||||
for _, v := range vouchers {
|
||||
fmt.Printf("Lane %d, Nonce %d: %s\n", v.Lane, v.Nonce, v.Amount.String())
|
||||
if cctx.Bool("export") {
|
||||
enc, err := v.EncodedString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Lane %d, Nonce %d: %s; %s\n", v.Lane, v.Nonce, v.Amount.String(), enc)
|
||||
} else {
|
||||
fmt.Printf("Lane %d, Nonce %d: %s\n", v.Lane, v.Nonce, v.Amount.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -179,7 +179,7 @@ func configureStorageMiner(ctx context.Context, api api.FullNode, addr address.A
|
||||
Nonce: nonce,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1000),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
}
|
||||
|
||||
smsg, err := api.WalletSignMessage(ctx, waddr, msg)
|
||||
|
6
go.mod
6
go.mod
@ -22,7 +22,7 @@ require (
|
||||
github.com/ipfs/go-ds-badger v0.0.5
|
||||
github.com/ipfs/go-filestore v0.0.2
|
||||
github.com/ipfs/go-fs-lock v0.0.1
|
||||
github.com/ipfs/go-hamt-ipld v0.0.12-0.20190822003241-7ff276389cbf
|
||||
github.com/ipfs/go-hamt-ipld v0.0.12-0.20190830015840-8aabc0c74ac6
|
||||
github.com/ipfs/go-ipfs-blockstore v0.1.0
|
||||
github.com/ipfs/go-ipfs-chunker v0.0.1
|
||||
github.com/ipfs/go-ipfs-ds-help v0.0.1
|
||||
@ -67,7 +67,7 @@ require (
|
||||
github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190822231004-8db835b09a5a
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190906235522-125fcd082c67
|
||||
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
|
||||
github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d
|
||||
github.com/whyrusleeping/sharray v0.0.0-20190718051354-e41931821e33
|
||||
@ -76,6 +76,8 @@ require (
|
||||
go.uber.org/fx v1.9.0
|
||||
go.uber.org/goleak v0.10.0 // indirect
|
||||
go4.org v0.0.0-20190313082347-94abd6928b1d // indirect
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 // indirect
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
|
||||
google.golang.org/api v0.9.0 // indirect
|
||||
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
|
||||
|
11
go.sum
11
go.sum
@ -168,8 +168,8 @@ github.com/ipfs/go-filestore v0.0.2 h1:pcYwpjtXXwirtbjBXKVJM9CTa9F7/8v1EkfnDaHTO
|
||||
github.com/ipfs/go-filestore v0.0.2/go.mod h1:KnZ41qJsCt2OX2mxZS0xsK3Psr0/oB93HMMssLujjVc=
|
||||
github.com/ipfs/go-fs-lock v0.0.1 h1:XHX8uW4jQBYWHj59XXcjg7BHlHxV9ZOYs6Y43yb7/l0=
|
||||
github.com/ipfs/go-fs-lock v0.0.1/go.mod h1:DNBekbboPKcxs1aukPSaOtFA3QfSdi5C855v0i9XJ8Y=
|
||||
github.com/ipfs/go-hamt-ipld v0.0.12-0.20190822003241-7ff276389cbf h1:P9Kkd8YCG4gCvfi8O839HHK2c+p5sdtyXMHcc1rjp2M=
|
||||
github.com/ipfs/go-hamt-ipld v0.0.12-0.20190822003241-7ff276389cbf/go.mod h1:gaK14QN1GOlYGgq+o+t5+WTExZZogkMt0k0IIBNjXsM=
|
||||
github.com/ipfs/go-hamt-ipld v0.0.12-0.20190830015840-8aabc0c74ac6 h1:qVk+425ErvzJEz/9f38lhjPfmmu0GAj/BSNt56SW4xQ=
|
||||
github.com/ipfs/go-hamt-ipld v0.0.12-0.20190830015840-8aabc0c74ac6/go.mod h1:UPmViPxLn1GGxxnllIww8yWUVO60qEYLFFfF9e3ojgo=
|
||||
github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08=
|
||||
github.com/ipfs/go-ipfs-blockstore v0.0.2/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08=
|
||||
github.com/ipfs/go-ipfs-blockstore v0.1.0 h1:V1GZorHFUIB6YgTJQdq7mcaIpUfCM3fCyVi+MTo9O88=
|
||||
@ -524,9 +524,10 @@ github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboa
|
||||
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM=
|
||||
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba h1:X4n8JG2e2biEZZXdBKt9HX7DN3bYGFUqljqqy0DqgnY=
|
||||
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba/go.mod h1:CHQnYnQUEPydYCwuy8lmTHfGmdw9TKrhWV0xLx8l0oM=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190822002707-4e02357de5c1/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190822231004-8db835b09a5a h1:9oEQR9eq2H2JDmglMcrCa+TxUEYy3HKSiNoLIkPNy/U=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190822231004-8db835b09a5a/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190906235522-125fcd082c67 h1:1JDNlhJZDMCdB/8KH7w7Aq0yvBYRA8pVdhzPZluDBHU=
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20190906235522-125fcd082c67/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
|
||||
@ -584,6 +585,8 @@ golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@ -642,6 +645,8 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/go-lotus/lib/jsonrpc"
|
||||
"io"
|
||||
@ -203,6 +204,45 @@ func (api *api) SpawnStorage(fullNodeRepo string) (nodeInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (api *api) FullID(id int32) (int32, error) {
|
||||
api.runningLk.Lock()
|
||||
defer api.runningLk.Unlock()
|
||||
|
||||
stor, ok := api.running[id]
|
||||
if !ok {
|
||||
return 0, xerrors.New("storage node not found")
|
||||
}
|
||||
|
||||
if !stor.meta.Storage {
|
||||
return 0, xerrors.New("node is not a storage node")
|
||||
}
|
||||
|
||||
for id, n := range api.running {
|
||||
if n.meta.Repo == stor.meta.FullNode {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
return 0, xerrors.New("node not found")
|
||||
}
|
||||
|
||||
func (api *api) CreateRandomFile(size int64) (string, error) {
|
||||
tf, err := ioutil.TempFile(os.TempDir(), "pond-random-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = io.CopyN(tf, rand.Reader, size)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := tf.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tf.Name(), nil
|
||||
}
|
||||
|
||||
type client struct {
|
||||
Nodes func() []nodeInfo
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ function truncAddr(addr, len) {
|
||||
return addr
|
||||
}
|
||||
|
||||
let sheet = document.createElement('style')
|
||||
document.body.appendChild(sheet);
|
||||
|
||||
class Address extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
@ -32,6 +35,7 @@ class Address extends React.Component {
|
||||
let actor = {}
|
||||
let actorInfo
|
||||
let minerInfo
|
||||
let nonce
|
||||
|
||||
try {
|
||||
balance = await this.props.client.call('Filecoin.WalletBalance', [this.props.addr])
|
||||
@ -41,11 +45,14 @@ class Address extends React.Component {
|
||||
if(this.props.miner) {
|
||||
minerInfo = await this.props.client.call('Filecoin.StateMinerPower', [this.props.addr, this.props.ts || null])
|
||||
}
|
||||
if(this.props.nonce) {
|
||||
nonce = await this.props.client.call('Filecoin.MpoolGetNonce', [this.props.addr])
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
balance = -1
|
||||
}
|
||||
this.setState({balance, actor, actorInfo, minerInfo})
|
||||
this.setState({balance, actor, actorInfo, minerInfo, nonce})
|
||||
}
|
||||
|
||||
openState() {
|
||||
@ -71,10 +78,17 @@ class Address extends React.Component {
|
||||
return info
|
||||
}
|
||||
|
||||
add10k = async () => {
|
||||
[...Array(10).keys()].map(() => async () => await this.props.add1k(this.props.addr)).reduce(async (p, c) => [await p, await c()], Promise.resolve(null))
|
||||
}
|
||||
|
||||
render() {
|
||||
let add1k = <span/>
|
||||
if(this.props.add1k) {
|
||||
add1k = <span> <a href="#" onClick={() => this.props.add1k(this.props.addr)}>[+1k]</a></span>
|
||||
if (this.props.add10k) {
|
||||
add1k = <span>{add1k} <a href="#" onClick={this.add10k}>[+10k]</a></span>
|
||||
}
|
||||
}
|
||||
let addr = truncAddr(this.props.addr, this.props.short ? 12 : 17)
|
||||
|
||||
@ -84,6 +98,16 @@ class Address extends React.Component {
|
||||
addr = <a href="#" onClick={this.openState}>{addr}</a>
|
||||
}
|
||||
|
||||
addr = <span className={`pondaddr-${this.props.addr}`}
|
||||
onMouseEnter={() => sheet.sheet.insertRule(`.pondaddr-${this.props.addr}, .pondaddr-${this.props.addr} * { color: #11ee11; }`, 0)}
|
||||
onMouseLeave={() => sheet.sheet.deleteRule(0)}
|
||||
>{addr}</span>
|
||||
|
||||
let nonce = <span/>
|
||||
if(this.props.nonce) {
|
||||
nonce = <span> <abbr title={"Next nonce"}>Nc:{this.state.nonce}</abbr>{nonce}</span>
|
||||
}
|
||||
|
||||
let balance = <span>: {this.state.balance} </span>
|
||||
if(this.props.nobalance) {
|
||||
balance = <span/>
|
||||
@ -103,7 +127,7 @@ class Address extends React.Component {
|
||||
minerInfo = <span> Power: {this.state.minerInfo.MinerPower} ({this.state.minerInfo.MinerPower/this.state.minerInfo.TotalPower*100}%)</span>
|
||||
}
|
||||
|
||||
return <span>{addr}{balance}{actInfo}{add1k}{transfer}{minerInfo}</span>
|
||||
return <span>{addr}{balance}{actInfo}{nonce}{add1k}{transfer}{minerInfo}</span>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,9 @@ class ChainExplorer extends React.Component {
|
||||
}
|
||||
|
||||
async fetch(h, cache, msgcache) {
|
||||
if (h < 0) {
|
||||
return
|
||||
}
|
||||
const cids = cache[h + 1].Blocks.map(b => b.Parents).reduce((acc, val) => acc.concat(val), [])
|
||||
const blocks = await Promise.all(cids.map(cid => this.props.client.call('Filecoin.ChainGetBlock', [cid])))
|
||||
|
||||
|
46
lotuspond/front/src/Client.js
Normal file
46
lotuspond/front/src/Client.js
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import Cristal from 'react-cristal'
|
||||
|
||||
class Client extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
kbs: 1,
|
||||
blocks: 12,
|
||||
total: 36000,
|
||||
miner: "t0101"
|
||||
}
|
||||
}
|
||||
|
||||
update = (name) => (e) => this.setState({ [name]: e.target.value });
|
||||
|
||||
makeDeal = async () => {
|
||||
let file = await this.props.pondClient.call('Pond.CreateRandomFile', [this.state.kbs * 1000]) // 1024 won't fit in 1k blocks :(
|
||||
let cid = await this.props.client.call('Filecoin.ClientImport', [file])
|
||||
let dealcid = await this.props.client.call('Filecoin.ClientStartDeal', [cid, this.state.miner, `${Math.round(this.state.total / this.state.blocks)}`, this.state.blocks])
|
||||
console.log("deal cid: ", dealcid)
|
||||
}
|
||||
|
||||
render() {
|
||||
let ppb = Math.round(this.state.total / this.state.blocks * 100) / 100
|
||||
let ppmbb = Math.round(ppb / (this.state.kbs / 1000) * 100) / 100
|
||||
|
||||
let dealMaker = <div>
|
||||
<span>Make Deal: </span>
|
||||
<select><option>t0101</option></select>
|
||||
<abbr title="Data length">L:</abbr> <input placeholder="KBs" defaultValue={1} onChange={this.update("kbs")}/>
|
||||
<abbr title="Deal duration">Dur:</abbr><input placeholder="blocks" defaultValue={12} onChange={this.update("blocks")}/>
|
||||
Total: <input placeholder="total price" defaultValue={36000} onChange={this.update("total")}/>
|
||||
<span><abbr title="Price per block">PpB:</abbr> {ppb} </span>
|
||||
<span><abbr title="Price per block-MiB">PpMbB:</abbr> {ppmbb} </span>
|
||||
<button onClick={this.makeDeal}>Deal!</button>
|
||||
</div>
|
||||
|
||||
return <Cristal title={"Client - Node " + this.props.node.ID}>
|
||||
<div>{dealMaker}</div>
|
||||
</Cristal>
|
||||
}
|
||||
}
|
||||
|
||||
export default Client
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Client } from 'rpc-websockets'
|
||||
import Cristal from 'react-cristal'
|
||||
import { BlockLinks } from "./BlockLink";
|
||||
import StorageNodeInit from "./StorageNodeInit";
|
||||
import Address from "./Address";
|
||||
import ChainExplorer from "./ChainExplorer";
|
||||
import Client from "./Client";
|
||||
|
||||
class FullNode extends React.Component {
|
||||
constructor(props) {
|
||||
@ -18,6 +18,7 @@ class FullNode extends React.Component {
|
||||
this.startStorageMiner = this.startStorageMiner.bind(this)
|
||||
this.add1k = this.add1k.bind(this)
|
||||
this.explorer = this.explorer.bind(this)
|
||||
this.client = this.client.bind(this)
|
||||
|
||||
this.loadInfo()
|
||||
setInterval(this.loadInfo, 2050)
|
||||
@ -46,12 +47,16 @@ class FullNode extends React.Component {
|
||||
|
||||
let minerList = await this.props.client.call('Filecoin.MinerAddresses', [])
|
||||
|
||||
let mpoolPending = (await this.props.client.call('Filecoin.MpoolPending', [tipset])).length
|
||||
|
||||
this.setState(() => ({
|
||||
id: id,
|
||||
version: version,
|
||||
peers: peers.length,
|
||||
tipset: tipset,
|
||||
|
||||
mpoolPending: mpoolPending,
|
||||
|
||||
addrs: addrs,
|
||||
paychs: paychs,
|
||||
vouchers: vouchers,
|
||||
@ -74,8 +79,8 @@ class FullNode extends React.Component {
|
||||
this.loadInfo()
|
||||
}
|
||||
|
||||
async startStorageMiner() {
|
||||
this.props.mountWindow((onClose) => <StorageNodeInit fullRepo={this.props.node.Repo} fullConn={this.props.client} pondClient={this.props.pondClient} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
startStorageMiner() {
|
||||
this.props.spawnStorageNode(this.props.node.Repo, this.props.client)
|
||||
}
|
||||
|
||||
async add1k(to) {
|
||||
@ -86,6 +91,10 @@ class FullNode extends React.Component {
|
||||
this.props.mountWindow((onClose) => <ChainExplorer onClose={onClose} ts={this.state.tipset} client={this.props.client} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
client() {
|
||||
this.props.mountWindow((onClose) => <Client onClose={onClose} node={this.props.node} client={this.props.client} pondClient={this.props.pondClient} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
render() {
|
||||
let runtime = <div></div>
|
||||
|
||||
@ -96,7 +105,7 @@ class FullNode extends React.Component {
|
||||
<div>
|
||||
Head: {
|
||||
<BlockLinks cids={this.state.tipset.Cids} conn={this.props.client} mountWindow={this.props.mountWindow} />
|
||||
} H:{this.state.tipset.Height} <a href="#" onClick={this.explorer}>[Explore]</a>
|
||||
} H:{this.state.tipset.Height} Mp:{this.state.mpoolPending} <a href="#" onClick={this.explorer}>[Explore]</a> <a href="#" onClick={this.client}>[Client]</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -109,14 +118,14 @@ class FullNode extends React.Component {
|
||||
let storageMine = <a href="#" onClick={this.startStorageMiner}>[Spawn Storage Miner]</a>
|
||||
|
||||
let addresses = this.state.addrs.map((addr) => {
|
||||
let line = <Address client={this.props.client} add1k={this.add1k} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
let line = <Address client={this.props.client} add1k={this.add1k} add10k={true} nonce={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
if (this.state.defaultAddr === addr) {
|
||||
line = <b>{line}</b>
|
||||
}
|
||||
return <div key={addr}>{line}</div>
|
||||
})
|
||||
let paychannels = this.state.paychs.map((addr, ak) => {
|
||||
const line = <Address client={this.props.client} add1k={this.add1k} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
const line = <Address client={this.props.client} add1k={this.add1k} add10k={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
const vouchers = this.state.vouchers[ak].map(voucher => {
|
||||
let extra = <span></span>
|
||||
if(voucher.Extra) {
|
||||
|
@ -7,6 +7,7 @@ import StorageNode from "./StorageNode";
|
||||
import {Client} from "rpc-websockets";
|
||||
import pushMessage from "./chain/send";
|
||||
import Logs from "./Logs";
|
||||
import StorageNodeInit from "./StorageNodeInit";
|
||||
|
||||
class NodeList extends React.Component {
|
||||
constructor(props) {
|
||||
@ -21,6 +22,7 @@ class NodeList extends React.Component {
|
||||
|
||||
// This binding is necessary to make `this` work in the callback
|
||||
this.spawnNode = this.spawnNode.bind(this)
|
||||
this.spawnStorageNode = this.spawnStorageNode.bind(this)
|
||||
this.connMgr = this.connMgr.bind(this)
|
||||
this.consensus = this.consensus.bind(this)
|
||||
this.transfer1kFrom1 = this.transfer1kFrom1.bind(this)
|
||||
@ -49,12 +51,18 @@ class NodeList extends React.Component {
|
||||
client={client}
|
||||
pondClient={this.props.client}
|
||||
give1k={this.transfer1kFrom1}
|
||||
mountWindow={this.props.mountWindow}/>)
|
||||
mountWindow={this.props.mountWindow}
|
||||
spawnStorageNode={this.spawnStorageNode}
|
||||
/>)
|
||||
} else {
|
||||
const fullId = await this.props.client.call('Pond.FullID', [node.ID])
|
||||
|
||||
this.props.mountWindow((onClose) =>
|
||||
<StorageNode node={{...node}}
|
||||
pondClient={this.props.client}
|
||||
mountWindow={this.props.mountWindow}/>)
|
||||
fullConn={this.state.nodes[fullId].conn}
|
||||
mountWindow={this.props.mountWindow}
|
||||
/>)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -81,7 +89,7 @@ class NodeList extends React.Component {
|
||||
return [addr, balance]
|
||||
}).reduce(async (c, n) => (await c)[1] > (await n)[1] ? await c : await n, Promise.resolve(['', -2]))
|
||||
|
||||
pushMessage(this.state.nodes[1].conn, bestaddr, {
|
||||
await pushMessage(this.state.nodes[1].conn, bestaddr, {
|
||||
To: to,
|
||||
From: bestaddr,
|
||||
Value: "1000",
|
||||
@ -96,6 +104,16 @@ class NodeList extends React.Component {
|
||||
this.setState(state => ({nodes: {...state.nodes, [node.ID]: node}}))
|
||||
}
|
||||
|
||||
async spawnStorageNode(fullRepo, fullConn) {
|
||||
let nodePromise = this.props.client.call('Pond.SpawnStorage', [fullRepo])
|
||||
|
||||
this.props.mountWindow((onClose) => <StorageNodeInit node={nodePromise} fullRepo={fullRepo} fullConn={fullConn} pondClient={this.props.client} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
let node = await nodePromise
|
||||
await this.mountNode(node)
|
||||
|
||||
//this.setState(state => ({nodes: {...state.nodes, [node.ID]: node}}))
|
||||
}
|
||||
|
||||
connMgr() {
|
||||
this.setState({showConnMgr: true})
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ import StorageNode from "./StorageNode";
|
||||
|
||||
class StorageNodeInit extends React.Component {
|
||||
async componentDidMount() {
|
||||
const info = await this.props.pondClient.call('Pond.SpawnStorage', [this.props.fullRepo])
|
||||
const info = await this.props.node
|
||||
|
||||
this.props.onClose()
|
||||
this.props.mountWindow((onClose) => <StorageNode node={info} fullRepo={this.props.fullRepo} fullConn={this.props.fullConn} pondClient={this.props.pondClient} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
//this.props.mountWindow((onClose) => <StorageNode node={info} fullRepo={this.props.fullRepo} fullConn={this.props.fullConn} pondClient={this.props.pondClient} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -336,8 +336,10 @@ func (m *Miner) createBlock(base *MiningBase, ticket *types.Ticket, proof types.
|
||||
|
||||
msgs := m.selectMessages(pending)
|
||||
|
||||
uts := time.Now().Unix() // TODO: put smallest valid timestamp
|
||||
|
||||
// why even return this? that api call could just submit it for us
|
||||
return m.api.MinerCreateBlock(context.TODO(), m.addresses[0], base.ts, append(base.tickets, ticket), proof, msgs, 0)
|
||||
return m.api.MinerCreateBlock(context.TODO(), m.addresses[0], base.ts, append(base.tickets, ticket), proof, msgs, uint64(uts))
|
||||
}
|
||||
|
||||
func (m *Miner) selectMessages(msgs []*types.SignedMessage) []*types.SignedMessage {
|
||||
|
@ -3,31 +3,32 @@ package full
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/filecoin-project/go-lotus/build"
|
||||
"github.com/filecoin-project/go-lotus/retrieval"
|
||||
"github.com/filecoin-project/go-lotus/retrieval/discovery"
|
||||
"github.com/ipfs/go-blockservice"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
"os"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/api"
|
||||
"github.com/filecoin-project/go-lotus/chain/actors"
|
||||
"github.com/filecoin-project/go-lotus/chain/address"
|
||||
"github.com/filecoin-project/go-lotus/chain/deals"
|
||||
"github.com/filecoin-project/go-lotus/chain/types"
|
||||
"github.com/filecoin-project/go-lotus/node/modules/dtypes"
|
||||
|
||||
"github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-filestore"
|
||||
chunker "github.com/ipfs/go-ipfs-chunker"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
"github.com/ipfs/go-unixfs/importer/balanced"
|
||||
ihelper "github.com/ipfs/go-unixfs/importer/helpers"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"go.uber.org/fx"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/api"
|
||||
"github.com/filecoin-project/go-lotus/build"
|
||||
"github.com/filecoin-project/go-lotus/chain/actors"
|
||||
"github.com/filecoin-project/go-lotus/chain/address"
|
||||
"github.com/filecoin-project/go-lotus/chain/deals"
|
||||
"github.com/filecoin-project/go-lotus/chain/store"
|
||||
"github.com/filecoin-project/go-lotus/chain/types"
|
||||
"github.com/filecoin-project/go-lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/go-lotus/retrieval"
|
||||
"github.com/filecoin-project/go-lotus/retrieval/discovery"
|
||||
)
|
||||
|
||||
type ClientAPI struct {
|
||||
@ -40,6 +41,7 @@ type ClientAPI struct {
|
||||
DealClient *deals.Client
|
||||
RetDiscovery discovery.PeerResolver
|
||||
Retrieval *retrieval.Client
|
||||
Chain *store.ChainStore
|
||||
|
||||
LocalDAG dtypes.ClientDAG
|
||||
Blockstore dtypes.ClientBlockstore
|
||||
@ -88,16 +90,18 @@ func (a *ClientAPI) ClientStartDeal(ctx context.Context, data cid.Cid, miner add
|
||||
return nil, err
|
||||
}
|
||||
|
||||
voucher := types.SignedVoucher{
|
||||
// TimeLock: 0, // TODO: do we want to use this somehow?
|
||||
head := a.Chain.GetHeaviestTipSet()
|
||||
|
||||
voucher := types.SignedVoucher{ // TODO: split into smaller payments
|
||||
TimeLock: head.Height() + blocksDuration,
|
||||
Extra: &types.ModVerifyParams{
|
||||
Actor: miner,
|
||||
Method: actors.MAMethods.PaymentVerifyInclusion,
|
||||
Data: voucherData,
|
||||
},
|
||||
Lane: 0,
|
||||
Lane: 0, // TODO: some api to make this easy
|
||||
Amount: total,
|
||||
MinCloseHeight: blocksDuration, // TODO: some way to start this after initial piece inclusion by actor? (also, at least add current height)
|
||||
MinCloseHeight: head.Height() + blocksDuration, // TODO: some way to start this after initial piece inclusion by actor? Using actors.PieceInclVoucherData?
|
||||
}
|
||||
|
||||
sv, err := a.paychVoucherCreate(ctx, paych, voucher)
|
||||
@ -113,7 +117,7 @@ func (a *ClientAPI) ClientStartDeal(ctx context.Context, data cid.Cid, miner add
|
||||
PayChActor: paych,
|
||||
Payer: self,
|
||||
ChannelMessage: paychMsg,
|
||||
Vouchers: []types.SignedVoucher{*sv},
|
||||
Vouchers: []*types.SignedVoucher{sv},
|
||||
},
|
||||
MinerAddress: miner,
|
||||
ClientAddress: self,
|
||||
|
@ -53,7 +53,7 @@ func (a *PaychAPI) paychCreate(ctx context.Context, from, to address.Address, am
|
||||
Nonce: nonce,
|
||||
Method: actors.IAMethods.Exec,
|
||||
Params: enc,
|
||||
GasLimit: types.NewInt(1000),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
GasPrice: types.NewInt(0),
|
||||
}
|
||||
|
||||
@ -102,7 +102,14 @@ func (a *PaychAPI) PaychList(ctx context.Context) ([]address.Address, error) {
|
||||
}
|
||||
|
||||
func (a *PaychAPI) PaychStatus(ctx context.Context, pch address.Address) (*api.PaychStatus, error) {
|
||||
panic("nyi")
|
||||
ci, err := a.PaychMgr.GetChannelInfo(pch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &api.PaychStatus{
|
||||
ControlAddr: ci.ControlAddr,
|
||||
Direction: api.PCHDir(ci.Direction),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *PaychAPI) PaychClose(ctx context.Context, addr address.Address) (cid.Cid, error) {
|
||||
@ -147,12 +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 {
|
||||
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
|
||||
@ -190,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)
|
||||
}
|
||||
|
||||
@ -198,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) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package paych
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@ -14,7 +16,10 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var ErrChannelNotTracked = errors.New("channel not tracked")
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(VoucherInfo{})
|
||||
cbor.RegisterCborType(ChannelInfo{})
|
||||
}
|
||||
|
||||
@ -34,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 {
|
||||
@ -60,6 +70,9 @@ func (ps *Store) getChannelInfo(addr address.Address) (*ChannelInfo, error) {
|
||||
k := dskeyForChannel(addr)
|
||||
|
||||
b, err := ps.ds.Get(k)
|
||||
if err == datastore.ErrNotFound {
|
||||
return nil, ErrChannelNotTracked
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -79,7 +92,7 @@ func (ps *Store) TrackChannel(ch *ChannelInfo) error {
|
||||
return err
|
||||
case nil:
|
||||
return fmt.Errorf("already tracking channel: %s", ch.Channel)
|
||||
case datastore.ErrNotFound:
|
||||
case ErrChannelNotTracked:
|
||||
return ps.putChannelInfo(ch)
|
||||
}
|
||||
}
|
||||
@ -112,18 +125,44 @@ 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)
|
||||
// look for duplicates
|
||||
for i, v := range ci.Vouchers {
|
||||
if !sv.Equals(v.Voucher) {
|
||||
continue
|
||||
}
|
||||
if v.Proof != nil {
|
||||
if !bytes.Equal(v.Proof, proof) {
|
||||
log.Warnf("AddVoucher: multiple proofs for single voucher, storing both")
|
||||
break
|
||||
}
|
||||
log.Warnf("AddVoucher: voucher re-added with matching proof")
|
||||
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
|
||||
|
@ -129,7 +129,7 @@ func (m *Miner) commitSector(ctx context.Context, sinfo sectorbuilder.SectorSeal
|
||||
Method: actors.MAMethods.CommitSector,
|
||||
Params: enc,
|
||||
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
|
||||
GasLimit: types.NewInt(1000 /* i dont know help */),
|
||||
GasLimit: types.NewInt(100000 /* i dont know help */),
|
||||
GasPrice: types.NewInt(1),
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user