Merge branch 'master' into inmem-journal
This commit is contained in:
commit
1ec534d607
@ -1,3 +1,5 @@
|
||||
comment: off
|
||||
ignore:
|
||||
- "cbor_gen.go"
|
||||
github_checks:
|
||||
annotations: false
|
||||
|
40
CHANGELOG.md
40
CHANGELOG.md
@ -1,5 +1,45 @@
|
||||
# Lotus changelog
|
||||
|
||||
# 0.5.10 / 2020-09-03
|
||||
|
||||
This patch includes a crucial fix to the message pool selection logic, strongly disfavouring messages that might cause a miner penalty.
|
||||
|
||||
## Changes
|
||||
|
||||
- Fix calculation of GasReward in messagepool (https://github.com/filecoin-project/lotus/pull/3528)
|
||||
|
||||
# 0.5.9 / 2020-09-03
|
||||
|
||||
This patch includes a hotfix to the `GasEstimateFeeCap` method, capping the estimated fee to a reasonable level by default.
|
||||
|
||||
## Changes
|
||||
|
||||
- Added target height to sync wait (https://github.com/filecoin-project/lotus/pull/3502)
|
||||
- Disable codecov annotations (https://github.com/filecoin-project/lotus/pull/3514)
|
||||
- Cap fees to reasonable level by default (https://github.com/filecoin-project/lotus/pull/3516)
|
||||
- Add APIs and command to inspect bandwidth usage (https://github.com/filecoin-project/lotus/pull/3497)
|
||||
- Track expected nonce in mpool, ignore messages with large nonce gaps (https://github.com/filecoin-project/lotus/pull/3450)
|
||||
|
||||
# 0.5.8 / 2020-09-02
|
||||
|
||||
This patch includes some bugfixes to the sector sealing process, and updates go-fil-markets. It also improves the performance of blocksync, adds a method to export chain state trees, and improves chainwatch.
|
||||
|
||||
## Changes
|
||||
|
||||
- Upgrade markets to v0.5.9 (https://github.com/filecoin-project/lotus/pull/3496)
|
||||
- Improve blocksync to load fewer messages: (https://github.com/filecoin-project/lotus/pull/3494)
|
||||
- Fix a panic in the ffi-wrapper's `ReadPiece` (https://github.com/filecoin-project/lotus/pull/3492/files)
|
||||
- Fix a deadlock in the sealing scheduler (https://github.com/filecoin-project/lotus/pull/3489)
|
||||
- Add test vectors for tipset tests (https://github.com/filecoin-project/lotus/pull/3485/files)
|
||||
- Improve the advance-block debug command (https://github.com/filecoin-project/lotus/pull/3476)
|
||||
- Add toggle for message processing to Lotus PCR (https://github.com/filecoin-project/lotus/pull/3470)
|
||||
- Allow exporting recent chain state trees (https://github.com/filecoin-project/lotus/pull/3463)
|
||||
- Remove height from chain rand (https://github.com/filecoin-project/lotus/pull/3458)
|
||||
- Disable GC on chain badger datastore (https://github.com/filecoin-project/lotus/pull/3457)
|
||||
- Account for `GasPremium` in `GasEstimateFeeCap` (https://github.com/filecoin-project/lotus/pull/3456)
|
||||
- Update go-libp2p-pubsub to `master` (https://github.com/filecoin-project/lotus/pull/3455)
|
||||
- Chainwatch improvements (https://github.com/filecoin-project/lotus/pull/3442)
|
||||
|
||||
# 0.5.7 / 2020-08-31
|
||||
|
||||
This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic.
|
||||
|
@ -5,8 +5,10 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
metrics "github.com/libp2p/go-libp2p-core/metrics"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
@ -28,6 +30,19 @@ type Common interface {
|
||||
NetFindPeer(context.Context, peer.ID) (peer.AddrInfo, error)
|
||||
NetPubsubScores(context.Context) ([]PubsubScore, error)
|
||||
NetAutoNatStatus(context.Context) (NatInfo, error)
|
||||
NetAgentVersion(ctx context.Context, p peer.ID) (string, error)
|
||||
|
||||
// NetBandwidthStats returns statistics about the nodes total bandwidth
|
||||
// usage and current rate across all peers and protocols.
|
||||
NetBandwidthStats(ctx context.Context) (metrics.Stats, error)
|
||||
|
||||
// NetBandwidthStatsByPeer returns statistics about the nodes bandwidth
|
||||
// usage and current rate per peer
|
||||
NetBandwidthStatsByPeer(ctx context.Context) (map[string]metrics.Stats, error)
|
||||
|
||||
// NetBandwidthStatsByProtocol returns statistics about the nodes bandwidth
|
||||
// usage and current rate per protocol
|
||||
NetBandwidthStatsByProtocol(ctx context.Context) (map[protocol.ID]metrics.Stats, error)
|
||||
|
||||
// MethodGroup: Common
|
||||
|
||||
|
@ -421,6 +421,7 @@ type FullNode interface {
|
||||
|
||||
PaychGet(ctx context.Context, from, to address.Address, amt types.BigInt) (*ChannelInfo, error)
|
||||
PaychGetWaitReady(context.Context, cid.Cid) (address.Address, error)
|
||||
PaychAvailableFunds(from, to address.Address) (*ChannelAvailableFunds, error)
|
||||
PaychList(context.Context) ([]address.Address, error)
|
||||
PaychStatus(context.Context, address.Address) (*PaychStatus, error)
|
||||
PaychSettle(context.Context, address.Address) (cid.Cid, error)
|
||||
@ -429,7 +430,7 @@ type FullNode interface {
|
||||
PaychNewPayment(ctx context.Context, from, to address.Address, vouchers []VoucherSpec) (*PaymentInfo, error)
|
||||
PaychVoucherCheckValid(context.Context, address.Address, *paych.SignedVoucher) error
|
||||
PaychVoucherCheckSpendable(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error)
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*paych.SignedVoucher, error)
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*VoucherCreateResult, error)
|
||||
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error)
|
||||
@ -538,6 +539,23 @@ type ChannelInfo struct {
|
||||
WaitSentinel cid.Cid
|
||||
}
|
||||
|
||||
type ChannelAvailableFunds struct {
|
||||
Channel *address.Address
|
||||
// ConfirmedAmt is the amount of funds that have been confirmed on-chain
|
||||
// for the channel
|
||||
ConfirmedAmt types.BigInt
|
||||
// PendingAmt is the amount of funds that are pending confirmation on-chain
|
||||
PendingAmt types.BigInt
|
||||
// PendingWaitSentinel can be used with PaychGetWaitReady to wait for
|
||||
// confirmation of pending funds
|
||||
PendingWaitSentinel *cid.Cid
|
||||
// QueuedAmt is the amount that is queued up behind a pending request
|
||||
QueuedAmt types.BigInt
|
||||
// VoucherRedeemedAmt is the amount that is redeemed by vouchers on-chain
|
||||
// and in the local datastore
|
||||
VoucherReedeemedAmt types.BigInt
|
||||
}
|
||||
|
||||
type PaymentInfo struct {
|
||||
Channel address.Address
|
||||
WaitSentinel cid.Cid
|
||||
@ -553,6 +571,16 @@ type VoucherSpec struct {
|
||||
Extra *paych.ModVerifyParams
|
||||
}
|
||||
|
||||
// VoucherCreateResult is the response to calling PaychVoucherCreate
|
||||
type VoucherCreateResult struct {
|
||||
// Voucher that was created, or nil if there was an error or if there
|
||||
// were insufficient funds in the channel
|
||||
Voucher *paych.SignedVoucher
|
||||
// Shortfall is the additional amount that would be needed in the channel
|
||||
// in order to be able to create the voucher
|
||||
Shortfall types.BigInt
|
||||
}
|
||||
|
||||
type MinerPower struct {
|
||||
MinerPower power.Claim
|
||||
TotalPower power.Claim
|
||||
|
@ -73,7 +73,7 @@ type StorageMiner interface {
|
||||
MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error
|
||||
MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error)
|
||||
MarketListRetrievalDeals(ctx context.Context) ([]retrievalmarket.ProviderDealState, error)
|
||||
MarketGetDealUpdates(ctx context.Context, d cid.Cid) (<-chan storagemarket.MinerDeal, error)
|
||||
MarketGetDealUpdates(ctx context.Context) (<-chan storagemarket.MinerDeal, error)
|
||||
MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error)
|
||||
MarketSetAsk(ctx context.Context, price types.BigInt, verifiedPrice types.BigInt, duration abi.ChainEpoch, minPieceSize abi.PaddedPieceSize, maxPieceSize abi.PaddedPieceSize) error
|
||||
MarketGetAsk(ctx context.Context) (*storagemarket.SignedStorageAsk, error)
|
||||
|
@ -6,8 +6,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
metrics "github.com/libp2p/go-libp2p-core/metrics"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/piecestore"
|
||||
@ -42,14 +44,18 @@ type CommonStruct struct {
|
||||
AuthVerify func(ctx context.Context, token string) ([]auth.Permission, error) `perm:"read"`
|
||||
AuthNew func(ctx context.Context, perms []auth.Permission) ([]byte, error) `perm:"admin"`
|
||||
|
||||
NetConnectedness func(context.Context, peer.ID) (network.Connectedness, error) `perm:"read"`
|
||||
NetPeers func(context.Context) ([]peer.AddrInfo, error) `perm:"read"`
|
||||
NetConnect func(context.Context, peer.AddrInfo) error `perm:"write"`
|
||||
NetAddrsListen func(context.Context) (peer.AddrInfo, error) `perm:"read"`
|
||||
NetDisconnect func(context.Context, peer.ID) error `perm:"write"`
|
||||
NetFindPeer func(context.Context, peer.ID) (peer.AddrInfo, error) `perm:"read"`
|
||||
NetPubsubScores func(context.Context) ([]api.PubsubScore, error) `perm:"read"`
|
||||
NetAutoNatStatus func(context.Context) (api.NatInfo, error) `perm:"read"`
|
||||
NetConnectedness func(context.Context, peer.ID) (network.Connectedness, error) `perm:"read"`
|
||||
NetPeers func(context.Context) ([]peer.AddrInfo, error) `perm:"read"`
|
||||
NetConnect func(context.Context, peer.AddrInfo) error `perm:"write"`
|
||||
NetAddrsListen func(context.Context) (peer.AddrInfo, error) `perm:"read"`
|
||||
NetDisconnect func(context.Context, peer.ID) error `perm:"write"`
|
||||
NetFindPeer func(context.Context, peer.ID) (peer.AddrInfo, error) `perm:"read"`
|
||||
NetPubsubScores func(context.Context) ([]api.PubsubScore, error) `perm:"read"`
|
||||
NetAutoNatStatus func(context.Context) (api.NatInfo, error) `perm:"read"`
|
||||
NetBandwidthStats func(ctx context.Context) (metrics.Stats, error) `perm:"read"`
|
||||
NetBandwidthStatsByPeer func(ctx context.Context) (map[string]metrics.Stats, error) `perm:"read"`
|
||||
NetBandwidthStatsByProtocol func(ctx context.Context) (map[protocol.ID]metrics.Stats, error) `perm:"read"`
|
||||
NetAgentVersion func(ctx context.Context, p peer.ID) (string, error) `perm:"read"`
|
||||
|
||||
ID func(context.Context) (peer.ID, error) `perm:"read"`
|
||||
Version func(context.Context) (api.Version, error) `perm:"read"`
|
||||
@ -203,6 +209,7 @@ type FullNodeStruct struct {
|
||||
|
||||
PaychGet func(ctx context.Context, from, to address.Address, amt types.BigInt) (*api.ChannelInfo, error) `perm:"sign"`
|
||||
PaychGetWaitReady func(context.Context, cid.Cid) (address.Address, error) `perm:"sign"`
|
||||
PaychAvailableFunds func(address.Address, address.Address) (*api.ChannelAvailableFunds, error) `perm:"sign"`
|
||||
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
|
||||
PaychStatus func(context.Context, address.Address) (*api.PaychStatus, error) `perm:"read"`
|
||||
PaychSettle func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
|
||||
@ -213,7 +220,7 @@ type FullNodeStruct struct {
|
||||
PaychVoucherCheckValid func(context.Context, address.Address, *paych.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckSpendable func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
|
||||
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*paych.SignedVoucher, error) `perm:"sign"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*api.VoucherCreateResult, error) `perm:"sign"`
|
||||
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
}
|
||||
@ -235,7 +242,7 @@ type StorageMinerStruct struct {
|
||||
MarketImportDealData func(context.Context, cid.Cid, string) error `perm:"write"`
|
||||
MarketListDeals func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
|
||||
MarketListRetrievalDeals func(ctx context.Context) ([]retrievalmarket.ProviderDealState, error) `perm:"read"`
|
||||
MarketGetDealUpdates func(ctx context.Context, d cid.Cid) (<-chan storagemarket.MinerDeal, error) `perm:"read"`
|
||||
MarketGetDealUpdates func(ctx context.Context) (<-chan storagemarket.MinerDeal, error) `perm:"read"`
|
||||
MarketListIncompleteDeals func(ctx context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"`
|
||||
MarketSetAsk func(ctx context.Context, price types.BigInt, verifiedPrice types.BigInt, duration abi.ChainEpoch, minPieceSize abi.PaddedPieceSize, maxPieceSize abi.PaddedPieceSize) error `perm:"admin"`
|
||||
MarketGetAsk func(ctx context.Context) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
||||
@ -371,6 +378,22 @@ func (c *CommonStruct) NetAutoNatStatus(ctx context.Context) (api.NatInfo, error
|
||||
return c.Internal.NetAutoNatStatus(ctx)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) NetBandwidthStats(ctx context.Context) (metrics.Stats, error) {
|
||||
return c.Internal.NetBandwidthStats(ctx)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) NetBandwidthStatsByPeer(ctx context.Context) (map[string]metrics.Stats, error) {
|
||||
return c.Internal.NetBandwidthStatsByPeer(ctx)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) NetBandwidthStatsByProtocol(ctx context.Context) (map[protocol.ID]metrics.Stats, error) {
|
||||
return c.Internal.NetBandwidthStatsByProtocol(ctx)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) {
|
||||
return c.Internal.NetAgentVersion(ctx, p)
|
||||
}
|
||||
|
||||
// ID implements API.ID
|
||||
func (c *CommonStruct) ID(ctx context.Context) (peer.ID, error) {
|
||||
return c.Internal.ID(ctx)
|
||||
@ -882,6 +905,10 @@ func (c *FullNodeStruct) PaychGetWaitReady(ctx context.Context, sentinel cid.Cid
|
||||
return c.Internal.PaychGetWaitReady(ctx, sentinel)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychAvailableFunds(from address.Address, to address.Address) (*api.ChannelAvailableFunds, error) {
|
||||
return c.Internal.PaychAvailableFunds(from, to)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychList(ctx context.Context) ([]address.Address, error) {
|
||||
return c.Internal.PaychList(ctx)
|
||||
}
|
||||
@ -902,7 +929,7 @@ func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Addre
|
||||
return c.Internal.PaychVoucherAdd(ctx, addr, sv, proof, minDelta)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*paych.SignedVoucher, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*api.VoucherCreateResult, error) {
|
||||
return c.Internal.PaychVoucherCreate(ctx, pch, amt, lane)
|
||||
}
|
||||
|
||||
@ -1070,8 +1097,8 @@ func (c *StorageMinerStruct) MarketListRetrievalDeals(ctx context.Context) ([]re
|
||||
return c.Internal.MarketListRetrievalDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketGetDealUpdates(ctx context.Context, d cid.Cid) (<-chan storagemarket.MinerDeal, error) {
|
||||
return c.Internal.MarketGetDealUpdates(ctx, d)
|
||||
func (c *StorageMinerStruct) MarketGetDealUpdates(ctx context.Context) (<-chan storagemarket.MinerDeal, error) {
|
||||
return c.Internal.MarketGetDealUpdates(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error) {
|
||||
|
@ -14,8 +14,10 @@ import (
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-filestore"
|
||||
metrics "github.com/libp2p/go-libp2p-core/metrics"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
|
||||
@ -133,6 +135,22 @@ func init() {
|
||||
InvalidMessageDeliveries: 3,
|
||||
},
|
||||
})
|
||||
addExample(map[string]metrics.Stats{
|
||||
"12D3KooWSXmXLJmBR1M7i9RW9GQPNUhZSzXKzxDHWtAgNuJAbyEJ": {
|
||||
RateIn: 100,
|
||||
RateOut: 50,
|
||||
TotalIn: 174000,
|
||||
TotalOut: 12500,
|
||||
},
|
||||
})
|
||||
addExample(map[protocol.ID]metrics.Stats{
|
||||
"/fil/hello/1.0.0": {
|
||||
RateIn: 100,
|
||||
RateOut: 50,
|
||||
TotalIn: 174000,
|
||||
TotalOut: 12500,
|
||||
},
|
||||
})
|
||||
|
||||
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
|
||||
if err != nil {
|
||||
|
@ -334,7 +334,7 @@ loop:
|
||||
func waitDealPublished(t *testing.T, ctx context.Context, miner TestStorageNode, deal *cid.Cid) {
|
||||
subCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
updates, err := miner.MarketGetDealUpdates(subCtx, *deal)
|
||||
updates, err := miner.MarketGetDealUpdates(subCtx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -343,18 +343,20 @@ func waitDealPublished(t *testing.T, ctx context.Context, miner TestStorageNode,
|
||||
case <-ctx.Done():
|
||||
t.Fatal("context timeout")
|
||||
case di := <-updates:
|
||||
switch di.State {
|
||||
case storagemarket.StorageDealProposalRejected:
|
||||
t.Fatal("deal rejected")
|
||||
case storagemarket.StorageDealFailing:
|
||||
t.Fatal("deal failed")
|
||||
case storagemarket.StorageDealError:
|
||||
t.Fatal("deal errored", di.Message)
|
||||
case storagemarket.StorageDealFinalizing, storagemarket.StorageDealSealing, storagemarket.StorageDealActive:
|
||||
fmt.Println("COMPLETE", di)
|
||||
return
|
||||
if deal.Equals(di.ProposalCid) {
|
||||
switch di.State {
|
||||
case storagemarket.StorageDealProposalRejected:
|
||||
t.Fatal("deal rejected")
|
||||
case storagemarket.StorageDealFailing:
|
||||
t.Fatal("deal failed")
|
||||
case storagemarket.StorageDealError:
|
||||
t.Fatal("deal errored", di.Message)
|
||||
case storagemarket.StorageDealFinalizing, storagemarket.StorageDealSealing, storagemarket.StorageDealActive:
|
||||
fmt.Println("COMPLETE", di)
|
||||
return
|
||||
}
|
||||
fmt.Println("Deal state: ", storagemarket.DealStates[di.State])
|
||||
}
|
||||
fmt.Println("Deal state: ", storagemarket.DealStates[di.State])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,18 +96,24 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if vouch1.Voucher == nil {
|
||||
t.Fatal(fmt.Errorf("Not enough funds to create voucher: missing %d", vouch1.Shortfall))
|
||||
}
|
||||
vouch2, err := paymentCreator.PaychVoucherCreate(ctx, channel, abi.NewTokenAmount(2000), lane)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
delta1, err := paymentReceiver.PaychVoucherAdd(ctx, channel, vouch1, nil, abi.NewTokenAmount(1000))
|
||||
if vouch2.Voucher == nil {
|
||||
t.Fatal(fmt.Errorf("Not enough funds to create voucher: missing %d", vouch2.Shortfall))
|
||||
}
|
||||
delta1, err := paymentReceiver.PaychVoucherAdd(ctx, channel, vouch1.Voucher, nil, abi.NewTokenAmount(1000))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !delta1.Equals(abi.NewTokenAmount(1000)) {
|
||||
t.Fatal("voucher didn't have the right amount")
|
||||
}
|
||||
delta2, err := paymentReceiver.PaychVoucherAdd(ctx, channel, vouch2, nil, abi.NewTokenAmount(1000))
|
||||
delta2, err := paymentReceiver.PaychVoucherAdd(ctx, channel, vouch2.Voucher, nil, abi.NewTokenAmount(1000))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func buildType() string {
|
||||
}
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "0.5.7"
|
||||
const BuildVersion = "0.5.10"
|
||||
|
||||
func UserVersion() string {
|
||||
return BuildVersion + buildType() + CurrentCommit
|
||||
|
@ -221,37 +221,36 @@ func collectChainSegment(
|
||||
func gatherMessages(cs *store.ChainStore, ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
blsmsgmap := make(map[cid.Cid]uint64)
|
||||
secpkmsgmap := make(map[cid.Cid]uint64)
|
||||
var secpkmsgs []*types.SignedMessage
|
||||
var blsmsgs []*types.Message
|
||||
var secpkincl, blsincl [][]uint64
|
||||
|
||||
var blscids, secpkcids []cid.Cid
|
||||
for _, block := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := cs.MessagesForBlock(block)
|
||||
bc, sc, err := cs.ReadMsgMetaCids(block.Messages)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
// FIXME: DRY. Use `chain.Message` interface.
|
||||
bmi := make([]uint64, 0, len(bmsgs))
|
||||
for _, m := range bmsgs {
|
||||
i, ok := blsmsgmap[m.Cid()]
|
||||
bmi := make([]uint64, 0, len(bc))
|
||||
for _, m := range bc {
|
||||
i, ok := blsmsgmap[m]
|
||||
if !ok {
|
||||
i = uint64(len(blsmsgs))
|
||||
blsmsgs = append(blsmsgs, m)
|
||||
blsmsgmap[m.Cid()] = i
|
||||
i = uint64(len(blscids))
|
||||
blscids = append(blscids, m)
|
||||
blsmsgmap[m] = i
|
||||
}
|
||||
|
||||
bmi = append(bmi, i)
|
||||
}
|
||||
blsincl = append(blsincl, bmi)
|
||||
|
||||
smi := make([]uint64, 0, len(smsgs))
|
||||
for _, m := range smsgs {
|
||||
i, ok := secpkmsgmap[m.Cid()]
|
||||
smi := make([]uint64, 0, len(sc))
|
||||
for _, m := range sc {
|
||||
i, ok := secpkmsgmap[m]
|
||||
if !ok {
|
||||
i = uint64(len(secpkmsgs))
|
||||
secpkmsgs = append(secpkmsgs, m)
|
||||
secpkmsgmap[m.Cid()] = i
|
||||
i = uint64(len(secpkcids))
|
||||
secpkcids = append(secpkcids, m)
|
||||
secpkmsgmap[m] = i
|
||||
}
|
||||
|
||||
smi = append(smi, i)
|
||||
@ -259,5 +258,15 @@ func gatherMessages(cs *store.ChainStore, ts *types.TipSet) ([]*types.Message, [
|
||||
secpkincl = append(secpkincl, smi)
|
||||
}
|
||||
|
||||
blsmsgs, err := cs.LoadMessagesFromCids(blscids)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
secpkmsgs, err := cs.LoadSignedMessagesFromCids(secpkcids)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
return blsmsgs, blsincl, secpkmsgs, secpkincl, nil
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ var minimumBaseFee = types.NewInt(uint64(build.MinimumBaseFee))
|
||||
|
||||
var MaxActorPendingMessages = 1000
|
||||
|
||||
var MaxNonceGap = uint64(4)
|
||||
|
||||
var (
|
||||
ErrMessageTooBig = errors.New("message too big")
|
||||
|
||||
@ -69,6 +71,7 @@ var (
|
||||
ErrSoftValidationFailure = errors.New("validation failure")
|
||||
ErrRBFTooLowPremium = errors.New("replace by fee has too low GasPremium")
|
||||
ErrTooManyPendingMessages = errors.New("too many pending messages for actor")
|
||||
ErrNonceGap = errors.New("unfulfilled nonce gap")
|
||||
|
||||
ErrTryAgain = errors.New("state inconsistency while pushing message; please try again")
|
||||
)
|
||||
@ -154,19 +157,39 @@ type msgSet struct {
|
||||
requiredFunds *stdbig.Int
|
||||
}
|
||||
|
||||
func newMsgSet() *msgSet {
|
||||
func newMsgSet(nonce uint64) *msgSet {
|
||||
return &msgSet{
|
||||
msgs: make(map[uint64]*types.SignedMessage),
|
||||
nextNonce: nonce,
|
||||
requiredFunds: stdbig.NewInt(0),
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, limit bool) (bool, error) {
|
||||
if len(ms.msgs) == 0 || m.Message.Nonce >= ms.nextNonce {
|
||||
ms.nextNonce = m.Message.Nonce + 1
|
||||
func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, strict bool) (bool, error) {
|
||||
nextNonce := ms.nextNonce
|
||||
nonceGap := false
|
||||
switch {
|
||||
case m.Message.Nonce == nextNonce:
|
||||
nextNonce++
|
||||
// advance if we are filling a gap
|
||||
for _, fillGap := ms.msgs[nextNonce]; fillGap; _, fillGap = ms.msgs[nextNonce] {
|
||||
nextNonce++
|
||||
}
|
||||
|
||||
case strict && m.Message.Nonce > nextNonce+MaxNonceGap:
|
||||
return false, xerrors.Errorf("message nonce has too big a gap from expected nonce (Nonce: %d, nextNonce: %d): %w", m.Message.Nonce, nextNonce, ErrNonceGap)
|
||||
|
||||
case m.Message.Nonce > nextNonce:
|
||||
nonceGap = true
|
||||
}
|
||||
|
||||
exms, has := ms.msgs[m.Message.Nonce]
|
||||
if has {
|
||||
// refuse RBF if we have a gap
|
||||
if strict && nonceGap {
|
||||
return false, xerrors.Errorf("rejecting replace by fee because of nonce gap (Nonce: %d, nextNonce: %d): %w", m.Message.Nonce, nextNonce, ErrNonceGap)
|
||||
}
|
||||
|
||||
if m.Cid() != exms.Cid() {
|
||||
// check if RBF passes
|
||||
minPrice := exms.Message.GasPremium
|
||||
@ -182,17 +205,26 @@ func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, limit bool) (bool
|
||||
m.Message.From, m.Message.Nonce, minPrice, m.Message.GasPremium,
|
||||
ErrRBFTooLowPremium)
|
||||
}
|
||||
} else {
|
||||
return false, xerrors.Errorf("message from %s with nonce %d already in mpool: %w",
|
||||
m.Message.From, m.Message.Nonce, ErrSoftValidationFailure)
|
||||
}
|
||||
|
||||
ms.requiredFunds.Sub(ms.requiredFunds, exms.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Sub(ms.requiredFunds, exms.Message.Value.Int)
|
||||
}
|
||||
|
||||
if !has && limit && len(ms.msgs) > MaxActorPendingMessages {
|
||||
if !has && strict && len(ms.msgs) > MaxActorPendingMessages {
|
||||
log.Errorf("too many pending messages from actor %s", m.Message.From)
|
||||
return false, ErrTooManyPendingMessages
|
||||
}
|
||||
|
||||
if strict && nonceGap {
|
||||
log.Warnf("adding nonce-gapped message from %s (nonce: %d, nextNonce: %d)",
|
||||
m.Message.From, m.Message.Nonce, nextNonce)
|
||||
}
|
||||
|
||||
ms.nextNonce = nextNonce
|
||||
ms.msgs[m.Message.Nonce] = m
|
||||
ms.requiredFunds.Add(ms.requiredFunds, m.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Add(ms.requiredFunds, m.Message.Value.Int)
|
||||
@ -200,12 +232,38 @@ func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, limit bool) (bool
|
||||
return !has, nil
|
||||
}
|
||||
|
||||
func (ms *msgSet) rm(nonce uint64) {
|
||||
func (ms *msgSet) rm(nonce uint64, applied bool) {
|
||||
m, has := ms.msgs[nonce]
|
||||
if has {
|
||||
ms.requiredFunds.Sub(ms.requiredFunds, m.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Sub(ms.requiredFunds, m.Message.Value.Int)
|
||||
delete(ms.msgs, nonce)
|
||||
if !has {
|
||||
if applied && nonce >= ms.nextNonce {
|
||||
// we removed a message we did not know about because it was applied
|
||||
// we need to adjust the nonce and check if we filled a gap
|
||||
ms.nextNonce = nonce + 1
|
||||
for _, fillGap := ms.msgs[ms.nextNonce]; fillGap; _, fillGap = ms.msgs[ms.nextNonce] {
|
||||
ms.nextNonce++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ms.requiredFunds.Sub(ms.requiredFunds, m.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Sub(ms.requiredFunds, m.Message.Value.Int)
|
||||
delete(ms.msgs, nonce)
|
||||
|
||||
// adjust next nonce
|
||||
if applied {
|
||||
// we removed a (known) message because it was applied in a tipset
|
||||
// we can't possibly have filled a gap in this case
|
||||
if nonce >= ms.nextNonce {
|
||||
ms.nextNonce = nonce + 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// we removed a message because it was pruned
|
||||
// we have to adjust the nonce if it creates a gap or rewinds state
|
||||
if nonce < ms.nextNonce {
|
||||
ms.nextNonce = nonce
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,6 +564,40 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error
|
||||
return mp.addLocked(m, true)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addLoaded(m *types.SignedMessage) error {
|
||||
err := mp.checkMessage(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
|
||||
curTs := mp.curTs
|
||||
|
||||
snonce, err := mp.getStateNonce(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrSoftValidationFailure)
|
||||
}
|
||||
|
||||
if snonce > m.Message.Nonce {
|
||||
return xerrors.Errorf("minimum expected nonce is %d: %w", snonce, ErrNonceTooLow)
|
||||
}
|
||||
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
if err := mp.verifyMsgBeforeAdd(m, curTs.Height()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mp.checkBalance(m, curTs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mp.addLocked(m, false)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addSkipChecks(m *types.SignedMessage) error {
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
@ -513,7 +605,7 @@ func (mp *MessagePool) addSkipChecks(m *types.SignedMessage) error {
|
||||
return mp.addLocked(m, false)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addLocked(m *types.SignedMessage, limit bool) error {
|
||||
func (mp *MessagePool) addLocked(m *types.SignedMessage, strict bool) error {
|
||||
log.Debugf("mpooladd: %s %d", m.Message.From, m.Message.Nonce)
|
||||
if m.Signature.Type == crypto.SigTypeBLS {
|
||||
mp.blsSigCache.Add(m.Cid(), m.Signature)
|
||||
@ -531,11 +623,16 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage, limit bool) error {
|
||||
|
||||
mset, ok := mp.pending[m.Message.From]
|
||||
if !ok {
|
||||
mset = newMsgSet()
|
||||
nonce, err := mp.getStateNonce(m.Message.From, mp.curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get initial actor nonce: %w", err)
|
||||
}
|
||||
|
||||
mset = newMsgSet(nonce)
|
||||
mp.pending[m.Message.From] = mset
|
||||
}
|
||||
|
||||
incr, err := mset.add(m, mp, limit)
|
||||
incr, err := mset.add(m, mp, strict)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
@ -702,14 +799,14 @@ func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address,
|
||||
return msg, mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) Remove(from address.Address, nonce uint64) {
|
||||
func (mp *MessagePool) Remove(from address.Address, nonce uint64, applied bool) {
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
mp.remove(from, nonce)
|
||||
mp.remove(from, nonce, applied)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) remove(from address.Address, nonce uint64) {
|
||||
func (mp *MessagePool) remove(from address.Address, nonce uint64, applied bool) {
|
||||
mset, ok := mp.pending[from]
|
||||
if !ok {
|
||||
return
|
||||
@ -732,22 +829,10 @@ func (mp *MessagePool) remove(from address.Address, nonce uint64) {
|
||||
|
||||
// NB: This deletes any message with the given nonce. This makes sense
|
||||
// as two messages with the same sender cannot have the same nonce
|
||||
mset.rm(nonce)
|
||||
mset.rm(nonce, applied)
|
||||
|
||||
if len(mset.msgs) == 0 {
|
||||
delete(mp.pending, from)
|
||||
} else {
|
||||
var max uint64
|
||||
for nonce := range mset.msgs {
|
||||
if max < nonce {
|
||||
max = nonce
|
||||
}
|
||||
}
|
||||
if max < nonce {
|
||||
max = nonce // we could have not seen the removed message before
|
||||
}
|
||||
|
||||
mset.nextNonce = max + 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -815,7 +900,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
rm := func(from address.Address, nonce uint64) {
|
||||
s, ok := rmsgs[from]
|
||||
if !ok {
|
||||
mp.Remove(from, nonce)
|
||||
mp.Remove(from, nonce, true)
|
||||
return
|
||||
}
|
||||
|
||||
@ -824,7 +909,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
return
|
||||
}
|
||||
|
||||
mp.Remove(from, nonce)
|
||||
mp.Remove(from, nonce, true)
|
||||
}
|
||||
|
||||
maybeRepub := func(cid cid.Cid) {
|
||||
@ -1126,7 +1211,7 @@ func (mp *MessagePool) loadLocal() error {
|
||||
return xerrors.Errorf("unmarshaling local message: %w", err)
|
||||
}
|
||||
|
||||
if err := mp.Add(&sm); err != nil {
|
||||
if err := mp.addLoaded(&sm); err != nil {
|
||||
if xerrors.Is(err, ErrNonceTooLow) {
|
||||
continue // todo: drop the message from local cache (if above certain confidence threshold)
|
||||
}
|
||||
|
@ -352,6 +352,12 @@ func TestRevertMessages(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPruningSimple(t *testing.T) {
|
||||
oldMaxNonceGap := MaxNonceGap
|
||||
MaxNonceGap = 1000
|
||||
defer func() {
|
||||
MaxNonceGap = oldMaxNonceGap
|
||||
}()
|
||||
|
||||
tma := newTestMpoolAPI()
|
||||
|
||||
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
|
@ -98,7 +98,7 @@ keepLoop:
|
||||
// and remove all messages that are still in pruneMsgs after processing the chains
|
||||
log.Infof("Pruning %d messages", len(pruneMsgs))
|
||||
for _, m := range pruneMsgs {
|
||||
mp.remove(m.Message.From, m.Message.Nonce)
|
||||
mp.remove(m.Message.From, m.Message.Nonce, false)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -3,6 +3,7 @@ package messagepool
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@ -16,6 +17,8 @@ import (
|
||||
|
||||
const repubMsgLimit = 30
|
||||
|
||||
var RepublishBatchDelay = 200 * time.Millisecond
|
||||
|
||||
func (mp *MessagePool) republishPendingMessages() error {
|
||||
mp.curTsLk.Lock()
|
||||
ts := mp.curTs
|
||||
@ -132,6 +135,12 @@ func (mp *MessagePool) republishPendingMessages() error {
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
if count < len(msgs) {
|
||||
// this delay is here to encourage the pubsub subsystem to process the messages serially
|
||||
// and avoid creating nonce gaps because of concurrent validation.
|
||||
time.Sleep(RepublishBatchDelay)
|
||||
}
|
||||
}
|
||||
|
||||
if len(msgs) > 0 {
|
||||
|
@ -12,6 +12,12 @@ import (
|
||||
)
|
||||
|
||||
func TestRepubMessages(t *testing.T) {
|
||||
oldRepublishBatchDelay := RepublishBatchDelay
|
||||
RepublishBatchDelay = time.Microsecond
|
||||
defer func() {
|
||||
RepublishBatchDelay = oldRepublishBatchDelay
|
||||
}()
|
||||
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
|
@ -585,16 +585,18 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address.
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) getGasReward(msg *types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) *big.Int {
|
||||
func (*MessagePool) getGasReward(msg *types.SignedMessage, baseFee types.BigInt) *big.Int {
|
||||
maxPremium := types.BigSub(msg.Message.GasFeeCap, baseFee)
|
||||
if types.BigCmp(maxPremium, msg.Message.GasPremium) < 0 {
|
||||
|
||||
if types.BigCmp(maxPremium, msg.Message.GasPremium) > 0 {
|
||||
maxPremium = msg.Message.GasPremium
|
||||
}
|
||||
|
||||
gasReward := abig.Mul(maxPremium, types.NewInt(uint64(msg.Message.GasLimit)))
|
||||
return gasReward.Int
|
||||
}
|
||||
|
||||
func (mp *MessagePool) getGasPerf(gasReward *big.Int, gasLimit int64) float64 {
|
||||
func (*MessagePool) getGasPerf(gasReward *big.Int, gasLimit int64) float64 {
|
||||
// gasPerf = gasReward * build.BlockGasLimit / gasLimit
|
||||
a := new(big.Rat).SetInt(new(big.Int).Mul(gasReward, bigBlockGasLimit))
|
||||
b := big.NewRat(1, gasLimit)
|
||||
@ -672,7 +674,7 @@ func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint6
|
||||
balance = new(big.Int).Sub(balance, value)
|
||||
}
|
||||
|
||||
gasReward := mp.getGasReward(m, baseFee, ts)
|
||||
gasReward := mp.getGasReward(m, baseFee)
|
||||
rewards = append(rewards, gasReward)
|
||||
}
|
||||
|
||||
@ -776,7 +778,7 @@ func (mc *msgChain) Before(other *msgChain) bool {
|
||||
func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet) {
|
||||
i := len(mc.msgs) - 1
|
||||
for i >= 0 && (mc.gasLimit > gasLimit || mc.gasPerf < 0) {
|
||||
gasReward := mp.getGasReward(mc.msgs[i], baseFee, ts)
|
||||
gasReward := mp.getGasReward(mc.msgs[i], baseFee)
|
||||
mc.gasReward = new(big.Int).Sub(mc.gasReward, gasReward)
|
||||
mc.gasLimit -= mc.msgs[i].Message.GasLimit
|
||||
if mc.gasLimit > 0 {
|
||||
|
@ -2,6 +2,7 @@ package messagepool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
@ -369,6 +370,12 @@ func TestMessageChainSkipping(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBasicMessageSelection(t *testing.T) {
|
||||
oldMaxNonceGap := MaxNonceGap
|
||||
MaxNonceGap = 1000
|
||||
defer func() {
|
||||
MaxNonceGap = oldMaxNonceGap
|
||||
}()
|
||||
|
||||
mp, tma := makeTestMpool()
|
||||
|
||||
// the actors
|
||||
@ -1055,17 +1062,17 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
|
||||
|
||||
greedyReward := big.NewInt(0)
|
||||
for _, m := range greedyMsgs {
|
||||
greedyReward.Add(greedyReward, mp.getGasReward(m, baseFee, ts))
|
||||
greedyReward.Add(greedyReward, mp.getGasReward(m, baseFee))
|
||||
}
|
||||
|
||||
optReward := big.NewInt(0)
|
||||
for _, m := range optMsgs {
|
||||
optReward.Add(optReward, mp.getGasReward(m, baseFee, ts))
|
||||
optReward.Add(optReward, mp.getGasReward(m, baseFee))
|
||||
}
|
||||
|
||||
bestTqReward := big.NewInt(0)
|
||||
for _, m := range bestMsgs {
|
||||
bestTqReward.Add(bestTqReward, mp.getGasReward(m, baseFee, ts))
|
||||
bestTqReward.Add(bestTqReward, mp.getGasReward(m, baseFee))
|
||||
}
|
||||
|
||||
totalBestTQReward += float64(bestTqReward.Uint64())
|
||||
@ -1146,3 +1153,35 @@ func TestCompetitiveMessageSelectionZipf(t *testing.T) {
|
||||
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
||||
t.Logf("Average reward of best ticket across all seeds: %f", tqReward)
|
||||
}
|
||||
|
||||
func TestGasReward(t *testing.T) {
|
||||
tests := []struct {
|
||||
Premium uint64
|
||||
FeeCap uint64
|
||||
BaseFee uint64
|
||||
GasReward int64
|
||||
}{
|
||||
{Premium: 100, FeeCap: 200, BaseFee: 100, GasReward: 100},
|
||||
{Premium: 100, FeeCap: 200, BaseFee: 210, GasReward: -10},
|
||||
{Premium: 200, FeeCap: 250, BaseFee: 210, GasReward: 40},
|
||||
{Premium: 200, FeeCap: 250, BaseFee: 2000, GasReward: -1750},
|
||||
}
|
||||
|
||||
mp := new(MessagePool)
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
|
||||
msg := &types.SignedMessage{
|
||||
Message: types.Message{
|
||||
GasLimit: 10,
|
||||
GasFeeCap: types.NewInt(test.FeeCap),
|
||||
GasPremium: types.NewInt(test.Premium),
|
||||
},
|
||||
}
|
||||
rew := mp.getGasReward(msg, types.NewInt(test.BaseFee))
|
||||
if rew.Cmp(big.NewInt(test.GasReward*10)) != 0 {
|
||||
t.Errorf("bad reward: expected %d, got %s", test.GasReward*10, rew)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ var chainHeadKey = dstore.NewKey("head")
|
||||
var blockValidationCacheKeyPrefix = dstore.NewKey("blockValidation")
|
||||
|
||||
var DefaultTipSetCacheSize = 8192
|
||||
var DefaultMsgMetaCacheSize = 2048
|
||||
|
||||
func init() {
|
||||
if s := os.Getenv("LOTUS_CHAIN_TIPSET_CACHE"); s != "" {
|
||||
@ -58,6 +59,14 @@ func init() {
|
||||
}
|
||||
DefaultTipSetCacheSize = tscs
|
||||
}
|
||||
|
||||
if s := os.Getenv("LOTUS_CHAIN_MSGMETA_CACHE"); s != "" {
|
||||
mmcs, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse 'LOTUS_CHAIN_MSGMETA_CACHE' env var: %s", err)
|
||||
}
|
||||
DefaultMsgMetaCacheSize = mmcs
|
||||
}
|
||||
}
|
||||
|
||||
// ReorgNotifee represents a callback that gets called upon reorgs.
|
||||
@ -113,7 +122,7 @@ type ChainStore struct {
|
||||
}
|
||||
|
||||
func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls vm.SyscallBuilder) *ChainStore {
|
||||
c, _ := lru.NewARC(2048)
|
||||
c, _ := lru.NewARC(DefaultMsgMetaCacheSize)
|
||||
tsc, _ := lru.NewARC(DefaultTipSetCacheSize)
|
||||
cs := &ChainStore{
|
||||
bs: bs,
|
||||
@ -857,7 +866,7 @@ type mmCids struct {
|
||||
secpk []cid.Cid
|
||||
}
|
||||
|
||||
func (cs *ChainStore) readMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) {
|
||||
func (cs *ChainStore) ReadMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) {
|
||||
o, ok := cs.mmCache.Get(mmc)
|
||||
if ok {
|
||||
mmcids := o.(*mmCids)
|
||||
@ -913,7 +922,7 @@ func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to type
|
||||
}
|
||||
|
||||
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||
blscids, secpkcids, err := cs.readMsgMetaCids(b.Messages)
|
||||
blscids, secpkcids, err := cs.ReadMsgMetaCids(b.Messages)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -555,6 +555,8 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrTooManyPendingMessages):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrNonceGap):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrNonceTooLow):
|
||||
return pubsub.ValidationIgnore
|
||||
default:
|
||||
|
108
cli/net.go
108
cli/net.go
@ -6,9 +6,12 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/addrutil"
|
||||
@ -25,12 +28,20 @@ var netCmd = &cli.Command{
|
||||
netFindPeer,
|
||||
netScores,
|
||||
NetReachability,
|
||||
NetBandwidthCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var NetPeers = &cli.Command{
|
||||
Name: "peers",
|
||||
Usage: "Print peers",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "agent",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Print agent name",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetAPI(cctx)
|
||||
if err != nil {
|
||||
@ -48,7 +59,17 @@ var NetPeers = &cli.Command{
|
||||
})
|
||||
|
||||
for _, peer := range peers {
|
||||
fmt.Printf("%s, %s\n", peer.ID, peer.Addrs)
|
||||
var agent string
|
||||
if cctx.Bool("agent") {
|
||||
agent, err = api.NetAgentVersion(ctx, peer.ID)
|
||||
if err != nil {
|
||||
log.Warnf("getting agent version: %s", err)
|
||||
} else {
|
||||
agent = ", " + agent
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s, %s%s\n", peer.ID, peer.Addrs, agent)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -228,3 +249,88 @@ var NetReachability = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var NetBandwidthCmd = &cli.Command{
|
||||
Name: "bandwidth",
|
||||
Usage: "Print bandwidth usage information",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "by-peer",
|
||||
Usage: "list bandwidth usage by peer",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "by-protocol",
|
||||
Usage: "list bandwidth usage by protocol",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
bypeer := cctx.Bool("by-peer")
|
||||
byproto := cctx.Bool("by-protocol")
|
||||
|
||||
tw := tabwriter.NewWriter(os.Stdout, 4, 4, 2, ' ', 0)
|
||||
|
||||
fmt.Fprintf(tw, "Segment\tTotalIn\tTotalOut\tRateIn\tRateOut\n")
|
||||
|
||||
if bypeer {
|
||||
bw, err := api.NetBandwidthStatsByPeer(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var peers []string
|
||||
for p := range bw {
|
||||
peers = append(peers, p)
|
||||
}
|
||||
|
||||
sort.Slice(peers, func(i, j int) bool {
|
||||
return peers[i] < peers[j]
|
||||
})
|
||||
|
||||
for _, p := range peers {
|
||||
s := bw[p]
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s/s\t%s/s\n", p, humanize.Bytes(uint64(s.TotalIn)), humanize.Bytes(uint64(s.TotalOut)), humanize.Bytes(uint64(s.RateIn)), humanize.Bytes(uint64(s.RateOut)))
|
||||
}
|
||||
} else if byproto {
|
||||
bw, err := api.NetBandwidthStatsByProtocol(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var protos []protocol.ID
|
||||
for p := range bw {
|
||||
protos = append(protos, p)
|
||||
}
|
||||
|
||||
sort.Slice(protos, func(i, j int) bool {
|
||||
return protos[i] < protos[j]
|
||||
})
|
||||
|
||||
for _, p := range protos {
|
||||
s := bw[p]
|
||||
if p == "" {
|
||||
p = "<unknown>"
|
||||
}
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\t%s/s\t%s/s\n", p, humanize.Bytes(uint64(s.TotalIn)), humanize.Bytes(uint64(s.TotalOut)), humanize.Bytes(uint64(s.RateIn)), humanize.Bytes(uint64(s.RateOut)))
|
||||
}
|
||||
} else {
|
||||
|
||||
s, err := api.NetBandwidthStats(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(tw, "Total\t%s\t%s\t%s/s\t%s/s\n", humanize.Bytes(uint64(s.TotalIn)), humanize.Bytes(uint64(s.TotalOut)), humanize.Bytes(uint64(s.RateIn)), humanize.Bytes(uint64(s.RateOut)))
|
||||
}
|
||||
|
||||
return tw.Flush()
|
||||
|
||||
},
|
||||
}
|
||||
|
107
cli/paych.go
107
cli/paych.go
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/filecoin-project/lotus/paychmgr"
|
||||
|
||||
@ -21,7 +22,7 @@ var paychCmd = &cli.Command{
|
||||
Name: "paych",
|
||||
Usage: "Manage payment channels",
|
||||
Subcommands: []*cli.Command{
|
||||
paychGetCmd,
|
||||
paychAddFundsCmd,
|
||||
paychListCmd,
|
||||
paychVoucherCmd,
|
||||
paychSettleCmd,
|
||||
@ -29,9 +30,9 @@ var paychCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var paychGetCmd = &cli.Command{
|
||||
Name: "get",
|
||||
Usage: "Create a new payment channel or get existing one and add amount to it",
|
||||
var paychAddFundsCmd = &cli.Command{
|
||||
Name: "add-funds",
|
||||
Usage: "Add funds to the payment channel between fromAddress and toAddress. Creates the payment channel if it doesn't already exist.",
|
||||
ArgsUsage: "[fromAddress toAddress amount]",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() != 3 {
|
||||
@ -79,6 +80,92 @@ var paychGetCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var paychStatusCmd = &cli.Command{
|
||||
Name: "status",
|
||||
Usage: "Show the status of an outbound payment channel between fromAddress and toAddress",
|
||||
ArgsUsage: "[fromAddress toAddress]",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() != 2 {
|
||||
return ShowHelp(cctx, fmt.Errorf("must pass two arguments: <from> <to>"))
|
||||
}
|
||||
|
||||
from, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return ShowHelp(cctx, fmt.Errorf("failed to parse from address: %s", err))
|
||||
}
|
||||
|
||||
to, err := address.NewFromString(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return ShowHelp(cctx, fmt.Errorf("failed to parse to address: %s", err))
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
avail, err := api.PaychAvailableFunds(from, to)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if avail.Channel == nil {
|
||||
if avail.PendingWaitSentinel != nil {
|
||||
fmt.Fprint(cctx.App.Writer, "Creating channel\n")
|
||||
fmt.Fprintf(cctx.App.Writer, " From: %s\n", from)
|
||||
fmt.Fprintf(cctx.App.Writer, " To: %s\n", to)
|
||||
fmt.Fprintf(cctx.App.Writer, " Pending Amt: %d\n", avail.PendingAmt)
|
||||
fmt.Fprintf(cctx.App.Writer, " Wait Sentinel: %s\n", avail.PendingWaitSentinel)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprint(cctx.App.Writer, "Channel does not exist\n")
|
||||
fmt.Fprintf(cctx.App.Writer, " From: %s\n", from)
|
||||
fmt.Fprintf(cctx.App.Writer, " To: %s\n", to)
|
||||
return nil
|
||||
}
|
||||
|
||||
if avail.PendingWaitSentinel != nil {
|
||||
fmt.Fprint(cctx.App.Writer, "Adding Funds to channel\n")
|
||||
} else {
|
||||
fmt.Fprint(cctx.App.Writer, "Channel exists\n")
|
||||
}
|
||||
|
||||
nameValues := [][]string{
|
||||
{"Channel", avail.Channel.String()},
|
||||
{"From", from.String()},
|
||||
{"To", to.String()},
|
||||
{"Confirmed Amt", fmt.Sprintf("%d", avail.ConfirmedAmt)},
|
||||
{"Pending Amt", fmt.Sprintf("%d", avail.PendingAmt)},
|
||||
{"Queued Amt", fmt.Sprintf("%d", avail.QueuedAmt)},
|
||||
{"Voucher Redeemed Amt", fmt.Sprintf("%d", avail.VoucherReedeemedAmt)},
|
||||
}
|
||||
if avail.PendingWaitSentinel != nil {
|
||||
nameValues = append(nameValues, []string{
|
||||
"Add Funds Wait Sentinel",
|
||||
avail.PendingWaitSentinel.String(),
|
||||
})
|
||||
}
|
||||
fmt.Fprint(cctx.App.Writer, formatNameValues(nameValues))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func formatNameValues(nameValues [][]string) string {
|
||||
maxLen := 0
|
||||
for _, nv := range nameValues {
|
||||
if len(nv[0]) > maxLen {
|
||||
maxLen = len(nv[0])
|
||||
}
|
||||
}
|
||||
out := make([]string, len(nameValues))
|
||||
for i, nv := range nameValues {
|
||||
namePad := strings.Repeat(" ", maxLen-len(nv[0]))
|
||||
out[i] = " " + nv[0] + ": " + namePad + nv[1]
|
||||
}
|
||||
return strings.Join(out, "\n") + "\n"
|
||||
}
|
||||
|
||||
var paychListCmd = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List all locally registered payment channels",
|
||||
@ -217,9 +304,9 @@ var paychVoucherCreateCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
amt, err := types.BigFromString(cctx.Args().Get(1))
|
||||
amt, err := types.ParseFIL(cctx.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
return ShowHelp(cctx, fmt.Errorf("parsing amount failed: %s", err))
|
||||
}
|
||||
|
||||
lane := cctx.Int("lane")
|
||||
@ -232,12 +319,16 @@ var paychVoucherCreateCmd = &cli.Command{
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
sv, err := api.PaychVoucherCreate(ctx, ch, amt, uint64(lane))
|
||||
v, err := api.PaychVoucherCreate(ctx, ch, types.BigInt(amt), uint64(lane))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc, err := EncodedString(sv)
|
||||
if v.Voucher == nil {
|
||||
return fmt.Errorf("Could not create voucher: insufficient funds in channel, shortfall: %d", v.Shortfall)
|
||||
}
|
||||
|
||||
enc, err := EncodedString(v.Voucher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -6,11 +6,14 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
@ -52,7 +55,7 @@ func TestPaymentChannels(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
||||
paymentCreator := nodes[0]
|
||||
paymentReceiver := nodes[0]
|
||||
paymentReceiver := nodes[1]
|
||||
creatorAddr := addrs[0]
|
||||
receiverAddr := addrs[1]
|
||||
|
||||
@ -61,10 +64,10 @@ func TestPaymentChannels(t *testing.T) {
|
||||
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
|
||||
|
||||
// creator: paych get <creator> <receiver> <amount>
|
||||
// creator: paych add-funds <creator> <receiver> <amount>
|
||||
channelAmt := "100000"
|
||||
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
|
||||
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
|
||||
chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd)
|
||||
|
||||
chAddr, err := address.NewFromString(chstr)
|
||||
require.NoError(t, err)
|
||||
@ -98,6 +101,83 @@ type voucherSpec struct {
|
||||
lane int
|
||||
}
|
||||
|
||||
// TestPaymentChannelStatus tests the payment channel status CLI command
|
||||
func TestPaymentChannelStatus(t *testing.T) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
blocktime := 5 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
||||
paymentCreator := nodes[0]
|
||||
creatorAddr := addrs[0]
|
||||
receiverAddr := addrs[1]
|
||||
|
||||
// Create mock CLI
|
||||
mockCLI := newMockCLI(t)
|
||||
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||
|
||||
cmd := []string{creatorAddr.String(), receiverAddr.String()}
|
||||
out := creatorCLI.runCmd(paychStatusCmd, cmd)
|
||||
fmt.Println(out)
|
||||
noChannelState := "Channel does not exist"
|
||||
require.Regexp(t, regexp.MustCompile(noChannelState), out)
|
||||
|
||||
channelAmt := uint64(100)
|
||||
create := make(chan string)
|
||||
go func() {
|
||||
// creator: paych get <creator> <receiver> <amount>
|
||||
cmd = []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
|
||||
create <- creatorCLI.runCmd(paychGetCmd, cmd)
|
||||
}()
|
||||
|
||||
// Wait for the output to stop being "Channel does not exist"
|
||||
for regexp.MustCompile(noChannelState).MatchString(out) {
|
||||
cmd = []string{creatorAddr.String(), receiverAddr.String()}
|
||||
out = creatorCLI.runCmd(paychStatusCmd, cmd)
|
||||
}
|
||||
fmt.Println(out)
|
||||
|
||||
// The next state should be creating channel or channel created, depending
|
||||
// on timing
|
||||
stateCreating := regexp.MustCompile("Creating channel").MatchString(out)
|
||||
stateCreated := regexp.MustCompile("Channel exists").MatchString(out)
|
||||
require.True(t, stateCreating || stateCreated)
|
||||
|
||||
channelAmtAtto := types.BigMul(types.NewInt(channelAmt), types.NewInt(build.FilecoinPrecision))
|
||||
channelAmtStr := fmt.Sprintf("%d", channelAmtAtto)
|
||||
if stateCreating {
|
||||
// If we're in the creating state (most likely) the amount should be pending
|
||||
require.Regexp(t, regexp.MustCompile("Pending.*"+channelAmtStr), out)
|
||||
}
|
||||
|
||||
// Wait for create channel to complete
|
||||
chstr := <-create
|
||||
|
||||
cmd = []string{creatorAddr.String(), receiverAddr.String()}
|
||||
out = creatorCLI.runCmd(paychStatusCmd, cmd)
|
||||
fmt.Println(out)
|
||||
// Output should have the channel address
|
||||
require.Regexp(t, regexp.MustCompile("Channel.*"+chstr), out)
|
||||
// Output should have confirmed amount
|
||||
require.Regexp(t, regexp.MustCompile("Confirmed.*"+channelAmtStr), out)
|
||||
|
||||
chAddr, err := address.NewFromString(chstr)
|
||||
require.NoError(t, err)
|
||||
|
||||
// creator: paych voucher create <channel> <amount>
|
||||
voucherAmt := uint64(10)
|
||||
cmd = []string{chAddr.String(), fmt.Sprintf("%d", voucherAmt)}
|
||||
creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
|
||||
cmd = []string{creatorAddr.String(), receiverAddr.String()}
|
||||
out = creatorCLI.runCmd(paychStatusCmd, cmd)
|
||||
fmt.Println(out)
|
||||
voucherAmtAtto := types.BigMul(types.NewInt(voucherAmt), types.NewInt(build.FilecoinPrecision))
|
||||
voucherAmtStr := fmt.Sprintf("%d", voucherAmtAtto)
|
||||
// Output should include voucher amount
|
||||
require.Regexp(t, regexp.MustCompile("Voucher.*"+voucherAmtStr), out)
|
||||
}
|
||||
|
||||
// TestPaymentChannelVouchers does a basic test to exercise some payment
|
||||
// channel voucher commands
|
||||
func TestPaymentChannelVouchers(t *testing.T) {
|
||||
@ -116,10 +196,10 @@ func TestPaymentChannelVouchers(t *testing.T) {
|
||||
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
|
||||
|
||||
// creator: paych get <creator> <receiver> <amount>
|
||||
// creator: paych add-funds <creator> <receiver> <amount>
|
||||
channelAmt := "100000"
|
||||
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
|
||||
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
|
||||
chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd)
|
||||
|
||||
chAddr, err := address.NewFromString(chstr)
|
||||
require.NoError(t, err)
|
||||
@ -248,6 +328,50 @@ func TestPaymentChannelVouchers(t *testing.T) {
|
||||
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
||||
}
|
||||
|
||||
// TestPaymentChannelVoucherCreateShortfall verifies that if a voucher amount
|
||||
// is greater than what's left in the channel, voucher create fails
|
||||
func TestPaymentChannelVoucherCreateShortfall(t *testing.T) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
blocktime := 5 * time.Millisecond
|
||||
ctx := context.Background()
|
||||
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
||||
paymentCreator := nodes[0]
|
||||
creatorAddr := addrs[0]
|
||||
receiverAddr := addrs[1]
|
||||
|
||||
// Create mock CLI
|
||||
mockCLI := newMockCLI(t)
|
||||
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||
|
||||
// creator: paych get <creator> <receiver> <amount>
|
||||
channelAmt := 100
|
||||
cmd := []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)}
|
||||
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
|
||||
|
||||
chAddr, err := address.NewFromString(chstr)
|
||||
require.NoError(t, err)
|
||||
|
||||
// creator: paych voucher create <channel> <amount> --lane=1
|
||||
voucherAmt1 := 60
|
||||
lane1 := "--lane=1"
|
||||
cmd = []string{lane1, chAddr.String(), strconv.Itoa(voucherAmt1)}
|
||||
voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
fmt.Println(voucher1)
|
||||
|
||||
// creator: paych voucher create <channel> <amount> --lane=2
|
||||
lane2 := "--lane=2"
|
||||
voucherAmt2 := 70
|
||||
cmd = []string{lane2, chAddr.String(), strconv.Itoa(voucherAmt2)}
|
||||
_, err = creatorCLI.runCmdRaw(paychVoucherCreateCmd, cmd)
|
||||
|
||||
// Should fail because channel doesn't have required amount
|
||||
require.Error(t, err)
|
||||
|
||||
shortfall := voucherAmt1 + voucherAmt2 - channelAmt
|
||||
require.Regexp(t, regexp.MustCompile(fmt.Sprintf("shortfall: %d", shortfall)), err.Error())
|
||||
}
|
||||
|
||||
func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
|
||||
lines := strings.Split(list, "\n")
|
||||
listVouchers := make(map[string]string)
|
||||
@ -350,6 +474,13 @@ type mockCLIClient struct {
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) runCmd(cmd *cli.Command, input []string) string {
|
||||
out, err := c.runCmdRaw(cmd, input)
|
||||
require.NoError(c.t, err)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) runCmdRaw(cmd *cli.Command, input []string) (string, error) {
|
||||
// prepend --api=<node api listener address>
|
||||
apiFlag := "--api=" + c.addr.String()
|
||||
input = append([]string{apiFlag}, input...)
|
||||
@ -359,12 +490,11 @@ func (c *mockCLIClient) runCmd(cmd *cli.Command, input []string) string {
|
||||
require.NoError(c.t, err)
|
||||
|
||||
err = cmd.Action(cli.NewContext(c.cctx.App, fs, c.cctx))
|
||||
require.NoError(c.t, err)
|
||||
|
||||
// Get the output
|
||||
str := strings.TrimSpace(c.out.String())
|
||||
c.out.Reset()
|
||||
return str
|
||||
return str, err
|
||||
}
|
||||
|
||||
func (c *mockCLIClient) flagSet(cmd *cli.Command) *flag.FlagSet {
|
||||
|
54
cli/state.go
54
cli/state.go
@ -26,11 +26,7 @@ import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
miner2 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/multisig"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/exported"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
@ -568,25 +564,7 @@ var stateGetActorCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
var strtype string
|
||||
switch a.Code {
|
||||
case builtin.AccountActorCodeID:
|
||||
strtype = "account"
|
||||
case builtin.MultisigActorCodeID:
|
||||
strtype = "multisig"
|
||||
case builtin.CronActorCodeID:
|
||||
strtype = "cron"
|
||||
case builtin.InitActorCodeID:
|
||||
strtype = "init"
|
||||
case builtin.StorageMinerActorCodeID:
|
||||
strtype = "miner"
|
||||
case builtin.StorageMarketActorCodeID:
|
||||
strtype = "market"
|
||||
case builtin.StoragePowerActorCodeID:
|
||||
strtype = "power"
|
||||
default:
|
||||
strtype = "unknown"
|
||||
}
|
||||
strtype := builtin.ActorNameByCode(a.Code)
|
||||
|
||||
fmt.Printf("Address:\t%s\n", addr)
|
||||
fmt.Printf("Balance:\t%s\n", types.FIL(a.Balance))
|
||||
@ -1460,21 +1438,21 @@ func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, er
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var f interface{}
|
||||
switch act {
|
||||
case builtin.StorageMarketActorCodeID:
|
||||
f = market.Actor{}.Exports()[method]
|
||||
case builtin.StorageMinerActorCodeID:
|
||||
f = miner2.Actor{}.Exports()[method]
|
||||
case builtin.StoragePowerActorCodeID:
|
||||
f = power.Actor{}.Exports()[method]
|
||||
case builtin.MultisigActorCodeID:
|
||||
f = multisig.Actor{}.Exports()[method]
|
||||
case builtin.PaymentChannelActorCodeID:
|
||||
f = paych.Actor{}.Exports()[method]
|
||||
default:
|
||||
return nil, fmt.Errorf("the lazy devs didnt add support for that actor to this call yet")
|
||||
var target abi.Invokee
|
||||
for _, actor := range exported.BuiltinActors() {
|
||||
if actor.Code() == act {
|
||||
target = actor
|
||||
}
|
||||
}
|
||||
if target == nil {
|
||||
return nil, fmt.Errorf("unknown actor %s", act)
|
||||
}
|
||||
methods := target.Exports()
|
||||
if uint64(len(methods)) <= method || methods[method] == nil {
|
||||
return nil, fmt.Errorf("unknown method %d for actor %s", method, act)
|
||||
}
|
||||
|
||||
f := methods[method]
|
||||
|
||||
rf := reflect.TypeOf(f)
|
||||
if rf.NumIn() != 3 {
|
||||
|
@ -180,11 +180,13 @@ func SyncWait(ctx context.Context, napi api.FullNode) error {
|
||||
ss := state.ActiveSyncs[working]
|
||||
|
||||
var target []cid.Cid
|
||||
var theight abi.ChainEpoch
|
||||
if ss.Target != nil {
|
||||
target = ss.Target.Cids()
|
||||
theight = ss.Target.Height()
|
||||
}
|
||||
|
||||
fmt.Printf("\r\x1b[2KWorker %d: Target: %s\tState: %s\tHeight: %d", working, target, chain.SyncStageString(ss.Stage), ss.Height)
|
||||
fmt.Printf("\r\x1b[2KWorker %d: Target Height: %d\tTarget: %s\tState: %s\tHeight: %d", working, theight, target, chain.SyncStageString(ss.Stage), ss.Height)
|
||||
|
||||
if time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs) {
|
||||
fmt.Println("\nDone!")
|
||||
|
@ -254,7 +254,9 @@ func (p *Processor) fetchMessages(ctx context.Context, blocks map[cid.Cid]*types
|
||||
parmap.Par(50, parmap.MapArr(blocks), func(header *types.BlockHeader) {
|
||||
msgs, err := p.node.ChainGetBlockMessages(ctx, header.Cid())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Error(err)
|
||||
log.Debugw("ChainGetBlockMessages", "header_cid", header.Cid())
|
||||
return
|
||||
}
|
||||
|
||||
vmm := make([]*types.Message, 0, len(msgs.Cids))
|
||||
@ -290,11 +292,15 @@ func (p *Processor) fetchParentReceipts(ctx context.Context, toSync map[cid.Cid]
|
||||
parmap.Par(50, parmap.MapArr(toSync), func(header *types.BlockHeader) {
|
||||
recs, err := p.node.ChainGetParentReceipts(ctx, header.Cid())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Error(err)
|
||||
log.Debugw("ChainGetParentReceipts", "header_cid", header.Cid())
|
||||
return
|
||||
}
|
||||
msgs, err := p.node.ChainGetParentMessages(ctx, header.Cid())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Error(err)
|
||||
log.Debugw("ChainGetParentMessages", "header_cid", header.Cid())
|
||||
return
|
||||
}
|
||||
|
||||
lk.Lock()
|
||||
|
@ -246,7 +246,8 @@ func (p *Processor) collectActorChanges(ctx context.Context, toProcess map[cid.C
|
||||
|
||||
pts, err := p.node.ChainGetTipSet(ctx, types.NewTipSetKey(bh.Parents...))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if pts.ParentState().Equals(bh.ParentStateRoot) {
|
||||
@ -260,7 +261,9 @@ func (p *Processor) collectActorChanges(ctx context.Context, toProcess map[cid.C
|
||||
// a separate strategy for deleted actors
|
||||
changes, err = p.node.StateChangedActors(ctx, pts.ParentState(), bh.ParentStateRoot)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Error(err)
|
||||
log.Debugw("StateChangedActors", "grandparent_state", pts.ParentState(), "parent_state", bh.ParentStateRoot)
|
||||
return
|
||||
}
|
||||
|
||||
// record the state of all actors that have changed
|
||||
@ -271,7 +274,9 @@ func (p *Processor) collectActorChanges(ctx context.Context, toProcess map[cid.C
|
||||
// ignore actors that were deleted.
|
||||
has, err := p.node.ChainHasObj(ctx, act.Head)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Error(err)
|
||||
log.Debugw("ChanHasObj", "actor_head", act.Head)
|
||||
return
|
||||
}
|
||||
if !has {
|
||||
continue
|
||||
@ -279,19 +284,24 @@ func (p *Processor) collectActorChanges(ctx context.Context, toProcess map[cid.C
|
||||
|
||||
addr, err := address.NewFromString(a)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
log.Error(err)
|
||||
log.Debugw("NewFromString", "address_string", a)
|
||||
return
|
||||
}
|
||||
|
||||
ast, err := p.node.StateReadState(ctx, addr, pts.Key())
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
log.Error(err)
|
||||
log.Debugw("StateReadState", "address_string", a, "parent_tipset_key", pts.Key())
|
||||
return
|
||||
}
|
||||
|
||||
// TODO look here for an empty state, maybe thats a sign the actor was deleted?
|
||||
|
||||
state, err := json.Marshal(ast.State)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
outMu.Lock()
|
||||
@ -324,10 +334,10 @@ func (p *Processor) unprocessedBlocks(ctx context.Context, batch int) (map[cid.C
|
||||
}()
|
||||
rows, err := p.db.Query(`
|
||||
with toProcess as (
|
||||
select blocks.cid, blocks.height, rank() over (order by height) as rnk
|
||||
from blocks
|
||||
left join blocks_synced bs on blocks.cid = bs.cid
|
||||
where bs.processed_at is null and blocks.height > 0
|
||||
select b.cid, b.height, rank() over (order by height) as rnk
|
||||
from blocks_synced bs
|
||||
left join blocks b on bs.cid = b.cid
|
||||
where bs.processed_at is null and b.height > 0
|
||||
)
|
||||
select cid
|
||||
from toProcess
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@ -345,6 +346,10 @@ var dealsListCmd = &cli.Command{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "watch",
|
||||
Usage: "watch deal updates in real-time, rather than a one time list",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
@ -360,42 +365,83 @@ var dealsListCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(deals, func(i, j int) bool {
|
||||
return deals[i].CreationTime.Time().Before(deals[j].CreationTime.Time())
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
|
||||
verbose := cctx.Bool("verbose")
|
||||
watch := cctx.Bool("watch")
|
||||
|
||||
if watch {
|
||||
updates, err := api.MarketGetDealUpdates(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
tm.Clear()
|
||||
tm.MoveCursor(1, 1)
|
||||
|
||||
err = outputStorageDeals(tm.Output, deals, verbose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tm.Flush()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case updated := <-updates:
|
||||
var found bool
|
||||
for i, existing := range deals {
|
||||
if existing.ProposalCid.Equals(updated.ProposalCid) {
|
||||
deals[i] = updated
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
deals = append(deals, updated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputStorageDeals(os.Stdout, deals, verbose)
|
||||
},
|
||||
}
|
||||
|
||||
func outputStorageDeals(out io.Writer, deals []storagemarket.MinerDeal, verbose bool) error {
|
||||
sort.Slice(deals, func(i, j int) bool {
|
||||
return deals[i].CreationTime.Time().Before(deals[j].CreationTime.Time())
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(out, 2, 4, 2, ' ', 0)
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "Creation\tProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\tMessage\n")
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "ProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\n")
|
||||
}
|
||||
|
||||
for _, deal := range deals {
|
||||
propcid := deal.ProposalCid.String()
|
||||
if !verbose {
|
||||
propcid = "..." + propcid[len(propcid)-8:]
|
||||
}
|
||||
|
||||
fil := types.FIL(types.BigMul(deal.Proposal.StoragePricePerEpoch, types.NewInt(uint64(deal.Proposal.Duration()))))
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "Creation\tProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\tMessage\n")
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "ProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\n")
|
||||
_, _ = fmt.Fprintf(w, "%s\t", deal.CreationTime.Time().Format(time.Stamp))
|
||||
}
|
||||
|
||||
for _, deal := range deals {
|
||||
propcid := deal.ProposalCid.String()
|
||||
if !verbose {
|
||||
propcid = "..." + propcid[len(propcid)-8:]
|
||||
}
|
||||
|
||||
fil := types.FIL(types.BigMul(deal.Proposal.StoragePricePerEpoch, types.NewInt(uint64(deal.Proposal.Duration()))))
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "%s\t", deal.CreationTime.Time().Format(time.Stamp))
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s", propcid, deal.DealID, storagemarket.DealStates[deal.State], deal.Proposal.Client, units.BytesSize(float64(deal.Proposal.PieceSize)), fil, deal.Proposal.Duration())
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "\t%s", deal.Message)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(w)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s", propcid, deal.DealID, storagemarket.DealStates[deal.State], deal.Proposal.Client, units.BytesSize(float64(deal.Proposal.PieceSize)), fil, deal.Proposal.Duration())
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "\t%s", deal.Message)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
},
|
||||
_, _ = fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
var getBlocklistCmd = &cli.Command{
|
||||
|
@ -2,7 +2,8 @@ package conformance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
@ -12,7 +13,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/lib/blockstore"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/puppet"
|
||||
|
||||
"github.com/filecoin-project/test-vectors/chaos"
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
@ -80,11 +80,14 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, preroot
|
||||
}
|
||||
switch msg.From.Protocol() {
|
||||
case address.SECP256K1:
|
||||
sb.SecpkMessages = append(sb.SecpkMessages, msg)
|
||||
sb.SecpkMessages = append(sb.SecpkMessages, toChainMsg(msg))
|
||||
case address.BLS:
|
||||
sb.BlsMessages = append(sb.BlsMessages, msg)
|
||||
sb.BlsMessages = append(sb.BlsMessages, toChainMsg(msg))
|
||||
default:
|
||||
return nil, fmt.Errorf("from account is not secpk nor bls: %s", msg.From)
|
||||
// sneak in messages originating from other addresses as both kinds.
|
||||
// these should fail, as they are actually invalid senders.
|
||||
sb.SecpkMessages = append(sb.SecpkMessages, msg)
|
||||
sb.BlsMessages = append(sb.BlsMessages, msg)
|
||||
}
|
||||
}
|
||||
blocks = append(blocks, sb)
|
||||
@ -133,17 +136,14 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, preroot cid.Cid, epoch
|
||||
|
||||
invoker := vm.NewInvoker()
|
||||
|
||||
// add support for the puppet and chaos actors.
|
||||
if puppetOn, ok := d.selector["puppet_actor"]; ok && puppetOn == "true" {
|
||||
invoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{})
|
||||
}
|
||||
// register the chaos actor if required by the vector.
|
||||
if chaosOn, ok := d.selector["chaos_actor"]; ok && chaosOn == "true" {
|
||||
invoker.Register(chaos.ChaosActorCodeCID, chaos.Actor{}, chaos.State{})
|
||||
}
|
||||
|
||||
lvm.SetInvoker(invoker)
|
||||
|
||||
ret, err := lvm.ApplyMessage(d.ctx, msg)
|
||||
ret, err := lvm.ApplyMessage(d.ctx, toChainMsg(msg))
|
||||
if err != nil {
|
||||
return nil, cid.Undef, err
|
||||
}
|
||||
@ -151,3 +151,22 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, preroot cid.Cid, epoch
|
||||
root, err := lvm.Flush(d.ctx)
|
||||
return ret, root, err
|
||||
}
|
||||
|
||||
// toChainMsg injects a synthetic 0-filled signature of the right length to
|
||||
// messages that originate from secp256k senders, leaving all
|
||||
// others untouched.
|
||||
// TODO: generate a signature in the DSL so that it's encoded in
|
||||
// the test vector.
|
||||
func toChainMsg(msg *types.Message) (ret types.ChainMsg) {
|
||||
ret = msg
|
||||
if msg.From.Protocol() == address.SECP256K1 {
|
||||
ret = &types.SignedMessage{
|
||||
Message: *msg,
|
||||
Signature: crypto.Signature{
|
||||
Type: crypto.SigTypeSecp256k1,
|
||||
Data: make([]byte, 65),
|
||||
},
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@ -84,7 +84,11 @@
|
||||
* [MsigSwapPropose](#MsigSwapPropose)
|
||||
* [Net](#Net)
|
||||
* [NetAddrsListen](#NetAddrsListen)
|
||||
* [NetAgentVersion](#NetAgentVersion)
|
||||
* [NetAutoNatStatus](#NetAutoNatStatus)
|
||||
* [NetBandwidthStats](#NetBandwidthStats)
|
||||
* [NetBandwidthStatsByPeer](#NetBandwidthStatsByPeer)
|
||||
* [NetBandwidthStatsByProtocol](#NetBandwidthStatsByProtocol)
|
||||
* [NetConnect](#NetConnect)
|
||||
* [NetConnectedness](#NetConnectedness)
|
||||
* [NetDisconnect](#NetDisconnect)
|
||||
@ -93,6 +97,7 @@
|
||||
* [NetPubsubScores](#NetPubsubScores)
|
||||
* [Paych](#Paych)
|
||||
* [PaychAllocateLane](#PaychAllocateLane)
|
||||
* [PaychAvailableFunds](#PaychAvailableFunds)
|
||||
* [PaychCollect](#PaychCollect)
|
||||
* [PaychGet](#PaychGet)
|
||||
* [PaychGetWaitReady](#PaychGetWaitReady)
|
||||
@ -2044,6 +2049,20 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
### NetAgentVersion
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
"12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf"
|
||||
]
|
||||
```
|
||||
|
||||
Response: `"string value"`
|
||||
|
||||
### NetAutoNatStatus
|
||||
|
||||
|
||||
@ -2059,6 +2078,61 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
### NetBandwidthStats
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs: `null`
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"TotalIn": 9,
|
||||
"TotalOut": 9,
|
||||
"RateIn": 12.3,
|
||||
"RateOut": 12.3
|
||||
}
|
||||
```
|
||||
|
||||
### NetBandwidthStatsByPeer
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs: `null`
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"12D3KooWSXmXLJmBR1M7i9RW9GQPNUhZSzXKzxDHWtAgNuJAbyEJ": {
|
||||
"TotalIn": 174000,
|
||||
"TotalOut": 12500,
|
||||
"RateIn": 100,
|
||||
"RateOut": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NetBandwidthStatsByProtocol
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs: `null`
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"/fil/hello/1.0.0": {
|
||||
"TotalIn": 174000,
|
||||
"TotalOut": 12500,
|
||||
"RateIn": 100,
|
||||
"RateOut": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NetConnect
|
||||
|
||||
|
||||
@ -2160,6 +2234,30 @@ Inputs:
|
||||
|
||||
Response: `42`
|
||||
|
||||
### PaychAvailableFunds
|
||||
There are not yet any comments for this method.
|
||||
|
||||
Perms: sign
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
"t01234"
|
||||
]
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"Channel": "\u003cempty\u003e",
|
||||
"ConfirmedAmt": "0",
|
||||
"PendingAmt": "0",
|
||||
"PendingWaitSentinel": null,
|
||||
"QueuedAmt": "0",
|
||||
"VoucherReedeemedAmt": "0"
|
||||
}
|
||||
```
|
||||
|
||||
### PaychCollect
|
||||
There are not yet any comments for this method.
|
||||
|
||||
@ -2415,24 +2513,27 @@ Inputs:
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"ChannelAddr": "t01234",
|
||||
"TimeLockMin": 10101,
|
||||
"TimeLockMax": 10101,
|
||||
"SecretPreimage": "Ynl0ZSBhcnJheQ==",
|
||||
"Extra": {
|
||||
"Actor": "t01234",
|
||||
"Method": 1,
|
||||
"Data": "Ynl0ZSBhcnJheQ=="
|
||||
"Voucher": {
|
||||
"ChannelAddr": "t01234",
|
||||
"TimeLockMin": 10101,
|
||||
"TimeLockMax": 10101,
|
||||
"SecretPreimage": "Ynl0ZSBhcnJheQ==",
|
||||
"Extra": {
|
||||
"Actor": "t01234",
|
||||
"Method": 1,
|
||||
"Data": "Ynl0ZSBhcnJheQ=="
|
||||
},
|
||||
"Lane": 42,
|
||||
"Nonce": 42,
|
||||
"Amount": "0",
|
||||
"MinSettleHeight": 10101,
|
||||
"Merges": null,
|
||||
"Signature": {
|
||||
"Type": 2,
|
||||
"Data": "Ynl0ZSBhcnJheQ=="
|
||||
}
|
||||
},
|
||||
"Lane": 42,
|
||||
"Nonce": 42,
|
||||
"Amount": "0",
|
||||
"MinSettleHeight": 10101,
|
||||
"Merges": null,
|
||||
"Signature": {
|
||||
"Type": 2,
|
||||
"Data": "Ynl0ZSBhcnJheQ=="
|
||||
}
|
||||
"Shortfall": "0"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -22,7 +22,7 @@ Note that payment channels and vouchers can be used for any situation in which t
|
||||
For example a client creates a payment channel to a provider with value 10 FIL.
|
||||
|
||||
```sh
|
||||
$ lotus paych get <client addr> <provider addr> 10
|
||||
$ lotus paych add-funds <client addr> <provider addr> 10
|
||||
<channel addr>
|
||||
```
|
||||
|
||||
@ -51,10 +51,10 @@ $ lotus paych voucher create <channel addr> 4
|
||||
The client sends the voucher to the provider and the provider adds the voucher and sends back more data.
|
||||
etc.
|
||||
|
||||
The client can add value to the channel after it has been created by calling `paych get` with the same client and provider addresses.
|
||||
The client can add value to the channel after it has been created by calling `paych add-funds` with the same client and provider addresses.
|
||||
|
||||
```sh
|
||||
$ lotus paych get <client addr> <provider addr> 5
|
||||
$ lotus paych add-funds <client addr> <provider addr> 5
|
||||
<channel addr> # Same address as above. Channel now has 15
|
||||
```
|
||||
|
||||
|
2
extern/test-vectors
vendored
2
extern/test-vectors
vendored
@ -1 +1 @@
|
||||
Subproject commit 9806d09b005dbaa0d08a6944aca67dd5ad2cd3b3
|
||||
Subproject commit 84da0a5ea1256a6e66bcbf73542c93e4916d6356
|
7
go.mod
7
go.mod
@ -2,7 +2,7 @@ module github.com/filecoin-project/lotus
|
||||
|
||||
go 1.14
|
||||
|
||||
replace github.com/supranational/blst => github.com/filecoin-project/blst v0.1.2-adx
|
||||
replace github.com/supranational/blst => github.com/supranational/blst v0.1.2-alpha.1
|
||||
|
||||
require (
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.1.0
|
||||
@ -18,6 +18,7 @@ require (
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/drand/drand v1.0.3-0.20200714175734-29705eaf09d4
|
||||
github.com/drand/kyber v1.1.1
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/elastic/go-sysinfo v1.3.0
|
||||
github.com/fatih/color v1.8.0
|
||||
github.com/filecoin-project/chain-validation v0.0.6-0.20200813000554-40c22fe26eef
|
||||
@ -28,7 +29,7 @@ require (
|
||||
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
|
||||
github.com/filecoin-project/go-data-transfer v0.6.3
|
||||
github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f
|
||||
github.com/filecoin-project/go-fil-markets v0.5.8
|
||||
github.com/filecoin-project/go-fil-markets v0.5.9
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200822201400-474f4fdccc52
|
||||
github.com/filecoin-project/go-multistore v0.0.3
|
||||
github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6
|
||||
@ -39,7 +40,7 @@ require (
|
||||
github.com/filecoin-project/specs-actors v0.9.3
|
||||
github.com/filecoin-project/specs-storage v0.1.1-0.20200730063404-f7db367e9401
|
||||
github.com/filecoin-project/statediff v0.0.1
|
||||
github.com/filecoin-project/test-vectors v0.0.0-20200902131127-9806d09b005d
|
||||
github.com/filecoin-project/test-vectors v0.0.0-20200903223506-84da0a5ea125
|
||||
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
|
||||
github.com/go-kit/kit v0.10.0
|
||||
github.com/google/uuid v1.1.1
|
||||
|
14
go.sum
14
go.sum
@ -215,8 +215,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
||||
github.com/fatih/color v1.8.0 h1:5bzFgL+oy7JITMTxUPJ00n7VxmYd/PdMp5mHFX40/RY=
|
||||
github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8=
|
||||
github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E=
|
||||
github.com/filecoin-project/blst v0.1.2-adx h1:qyirtiGFTN/C17y4xlCFAblgw2OXhW8+wtnLwV27/cM=
|
||||
github.com/filecoin-project/blst v0.1.2-adx/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/filecoin-project/chain-validation v0.0.6-0.20200813000554-40c22fe26eef h1:MtQRSnJLsQOOlmsd/Ua5KWXimpxcaa715h6FUh/eJPY=
|
||||
github.com/filecoin-project/chain-validation v0.0.6-0.20200813000554-40c22fe26eef/go.mod h1:SMj5VK1pYgqC8FXVEtOBRTc+9AIrYu+C+K3tAXi2Rk8=
|
||||
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0=
|
||||
@ -248,6 +246,8 @@ github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go
|
||||
github.com/filecoin-project/go-fil-markets v0.5.6-0.20200814234959-80b1788108ac/go.mod h1:umicPCaN99ysHTiYOmwhuLxTFbOwcsI+mdw/t96vvM4=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.8 h1:uwl0QNUVmmSlUQfxshpj21Dmhh6WKTQNhnb1GMfdp18=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.8/go.mod h1:6ZX1vbZbnukbVQ8tCB/MmEizuW/bmRX7SpGAltU3KVg=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.9 h1:iIO17UfIjUCiB37TRwgiBwAyfJJwHb8e8uAfu7F37gc=
|
||||
github.com/filecoin-project/go-fil-markets v0.5.9/go.mod h1:/cb1IoaiHhwFEWyIAPm9yN6Z+MiPujFZBT8BGH7LwB8=
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200817153016-2ea5cbaf5ec0/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200822201400-474f4fdccc52 h1:FXtCp0ybqdQL9knb3OGDpkNTaBbPxgkqPeWKotUwkH0=
|
||||
github.com/filecoin-project/go-jsonrpc v0.1.2-0.20200822201400-474f4fdccc52/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
|
||||
@ -268,7 +268,7 @@ github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZO
|
||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b h1:fkRZSPrYpk42PV3/lIXiL0LHetxde7vyYYvSsttQtfg=
|
||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b/go.mod h1:Q0GQOBtKf1oE10eSXSlhN45kDBdGvEcVOqMiffqX+N8=
|
||||
github.com/filecoin-project/lotus v0.4.3-0.20200820203717-d1718369a182/go.mod h1:biFZPQ/YyQGfkHUmHMiaNf2hnD6zm1+OAXPQYQ61Zkg=
|
||||
github.com/filecoin-project/lotus v0.5.8-0.20200902130912-0962292f920e/go.mod h1:OkZ5aUqs+fFnJOq9243WJDsTa9c3/Ae67NIAwVhAB+0=
|
||||
github.com/filecoin-project/lotus v0.5.8-0.20200903221953-ada5e6ae68cf/go.mod h1:wxuzS4ozpCFThia18G+J5P0Jp/DSiq9ezzJF1yvZuP4=
|
||||
github.com/filecoin-project/sector-storage v0.0.0-20200712023225-1d67dcfa3c15/go.mod h1:salgVdX7qeXFo/xaiEQE29J4pPkjn71T0kt0n+VDBzo=
|
||||
github.com/filecoin-project/sector-storage v0.0.0-20200730050024-3ee28c3b6d9a/go.mod h1:oOawOl9Yk+qeytLzzIryjI8iRbqo+qzS6EEeElP4PWA=
|
||||
github.com/filecoin-project/sector-storage v0.0.0-20200810171746-eac70842d8e0 h1:E1fZ27fhKK05bhZItfTwqr1i05vXnEZJznQFEYwEEUU=
|
||||
@ -687,6 +687,8 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.1.1-0.20190114141812-62fb9bc030d1 h1:qBCV/RLV02TSfQa7tFmxTihnG+u+7JXByOkhlkR5rmQ=
|
||||
github.com/jonboulle/clockwork v0.1.1-0.20190114141812-62fb9bc030d1/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190830100107-3784a6c7c552/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
@ -824,6 +826,7 @@ github.com/libp2p/go-libp2p-core v0.6.1 h1:XS+Goh+QegCDojUZp00CaPMfiEADCrLjNZskW
|
||||
github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
|
||||
github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE=
|
||||
github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I=
|
||||
github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ=
|
||||
github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI=
|
||||
github.com/libp2p/go-libp2p-daemon v0.2.2/go.mod h1:kyrpsLB2JeNYR2rvXSVWyY0iZuRIMhqzWR3im9BV6NQ=
|
||||
github.com/libp2p/go-libp2p-discovery v0.0.1/go.mod h1:ZkkF9xIFRLA1xCc7bstYFkd80gBGK8Fc1JqGoU2i+zI=
|
||||
@ -849,6 +852,7 @@ github.com/libp2p/go-libp2p-kbucket v0.4.2/go.mod h1:7sCeZx2GkNK1S6lQnGUW5JYZCFP
|
||||
github.com/libp2p/go-libp2p-loggables v0.0.1/go.mod h1:lDipDlBNYbpyqyPX/KcoO+eq0sJYEVR2JgOexcivchg=
|
||||
github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8=
|
||||
github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90=
|
||||
github.com/libp2p/go-libp2p-metrics v0.0.1 h1:yumdPC/P2VzINdmcKZd0pciSUCpou+s0lwYCjBbzQZU=
|
||||
github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08=
|
||||
github.com/libp2p/go-libp2p-mplex v0.1.1/go.mod h1:KUQWpGkCzfV7UIpi8SKsAVxyBgz1c9R5EvxgnwLsb/I=
|
||||
github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo=
|
||||
@ -871,6 +875,7 @@ github.com/libp2p/go-libp2p-noise v0.1.1 h1:vqYQWvnIcHpIoWJKC7Al4D6Hgj0H012TuXRh
|
||||
github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM=
|
||||
github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo=
|
||||
github.com/libp2p/go-libp2p-peer v0.1.1/go.mod h1:jkF12jGB4Gk/IOo+yomm+7oLWxF278F7UnrYUQ1Q8es=
|
||||
github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY=
|
||||
github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
|
||||
github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
|
||||
@ -887,6 +892,7 @@ github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuD
|
||||
github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k=
|
||||
github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA=
|
||||
github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s=
|
||||
github.com/libp2p/go-libp2p-protocol v0.1.0 h1:HdqhEyhg0ToCaxgMhnOmUO8snQtt/kQlcjVk3UoJU3c=
|
||||
github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.1.1/go.mod h1:ZwlKzRSe1eGvSIdU5bD7+8RZN/Uzw0t1Bp9R1znpR/Q=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.3.2-0.20200527132641-c0712c6e92cf/go.mod h1:TxPOBuo1FPdsTjFnv+FGZbNbWYsp74Culx+4ViQpato=
|
||||
@ -1370,6 +1376,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/supranational/blst v0.1.2-alpha.1 h1:v0UqVlvbRNZIaSeMPr+T01kvTUq1h0EZuZ6gnDR1Mlg=
|
||||
github.com/supranational/blst v0.1.2-alpha.1/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
|
@ -3,11 +3,14 @@ package retrievaladapter
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/shared"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
|
||||
@ -56,7 +59,10 @@ func (rcn *retrievalClientNode) CreatePaymentVoucher(ctx context.Context, paymen
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return voucher, nil
|
||||
if voucher.Voucher == nil {
|
||||
return nil, xerrors.Errorf("Could not create voucher - shortfall: %d", voucher.Shortfall)
|
||||
}
|
||||
return voucher.Voucher, nil
|
||||
}
|
||||
|
||||
func (rcn *retrievalClientNode) GetChainHead(ctx context.Context) (shared.TipSetToken, abi.ChainEpoch, error) {
|
||||
|
@ -87,6 +87,7 @@ var (
|
||||
NatPortMapKey = special{8} // Libp2p option
|
||||
ConnectionManagerKey = special{9} // Libp2p option
|
||||
AutoNATSvcKey = special{10} // Libp2p option
|
||||
BandwidthReporterKey = special{11} // Libp2p option
|
||||
)
|
||||
|
||||
type invoke int
|
||||
@ -192,6 +193,7 @@ func libp2p() Option {
|
||||
Override(new(routing.Routing), lp2p.Routing),
|
||||
|
||||
Override(NatPortMapKey, lp2p.NatPortMap),
|
||||
Override(BandwidthReporterKey, lp2p.BandwidthCounter),
|
||||
|
||||
Override(ConnectionManagerKey, lp2p.ConnectionManager(50, 200, 20*time.Second, nil)),
|
||||
Override(AutoNATSvcKey, lp2p.AutoNATService),
|
||||
|
@ -9,8 +9,10 @@ import (
|
||||
|
||||
"github.com/gbrlsnchs/jwt/v3"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
metrics "github.com/libp2p/go-libp2p-core/metrics"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
protocol "github.com/libp2p/go-libp2p-core/protocol"
|
||||
swarm "github.com/libp2p/go-libp2p-swarm"
|
||||
basichost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
@ -32,6 +34,7 @@ type CommonAPI struct {
|
||||
RawHost lp2p.RawHost
|
||||
Host host.Host
|
||||
Router lp2p.BaseIpfsRouting
|
||||
Reporter metrics.Reporter
|
||||
Sk *dtypes.ScoreKeeper
|
||||
ShutdownChan dtypes.ShutdownChan
|
||||
}
|
||||
@ -133,6 +136,35 @@ func (a *CommonAPI) NetAutoNatStatus(ctx context.Context) (i api.NatInfo, err er
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *CommonAPI) NetAgentVersion(ctx context.Context, p peer.ID) (string, error) {
|
||||
ag, err := a.Host.Peerstore().Get(p, "AgentVersion")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ag == nil {
|
||||
return "unknown", nil
|
||||
}
|
||||
|
||||
return ag.(string), nil
|
||||
}
|
||||
|
||||
func (a *CommonAPI) NetBandwidthStats(ctx context.Context) (metrics.Stats, error) {
|
||||
return a.Reporter.GetBandwidthTotals(), nil
|
||||
}
|
||||
|
||||
func (a *CommonAPI) NetBandwidthStatsByPeer(ctx context.Context) (map[string]metrics.Stats, error) {
|
||||
out := make(map[string]metrics.Stats)
|
||||
for p, s := range a.Reporter.GetBandwidthByPeer() {
|
||||
out[p.String()] = s
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a *CommonAPI) NetBandwidthStatsByProtocol(ctx context.Context) (map[protocol.ID]metrics.Stats, error) {
|
||||
return a.Reporter.GetBandwidthByProtocol(), nil
|
||||
}
|
||||
|
||||
func (a *CommonAPI) ID(context.Context) (peer.ID, error) {
|
||||
return a.Host.ID(), nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package full
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@ -503,7 +504,11 @@ func (a *ChainAPI) ChainExport(ctx context.Context, nroots abi.ChainEpoch, tsk t
|
||||
out := make(chan []byte)
|
||||
go func() {
|
||||
defer w.Close() //nolint:errcheck // it is a pipe
|
||||
if err := a.Chain.Export(ctx, ts, nroots, w); err != nil {
|
||||
|
||||
bw := bufio.NewWriterSize(w, 1<<20)
|
||||
defer bw.Flush() //nolint:errcheck // it is a write to a pipe
|
||||
|
||||
if err := a.Chain.Export(ctx, ts, nroots, bw); err != nil {
|
||||
log.Errorf("chain export call failed: %s", err)
|
||||
return
|
||||
}
|
||||
@ -512,7 +517,7 @@ func (a *ChainAPI) ChainExport(ctx context.Context, nroots abi.ChainEpoch, tsk t
|
||||
go func() {
|
||||
defer close(out)
|
||||
for {
|
||||
buf := make([]byte, 4096)
|
||||
buf := make([]byte, 1<<20)
|
||||
n, err := r.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Errorf("chain export pipe read failed: %s", err)
|
||||
@ -522,6 +527,7 @@ func (a *ChainAPI) ChainExport(ctx context.Context, nroots abi.ChainEpoch, tsk t
|
||||
case out <- buf[:n]:
|
||||
case <-ctx.Done():
|
||||
log.Warnf("export writer failed: %s", ctx.Err())
|
||||
return
|
||||
}
|
||||
if err == io.EOF {
|
||||
return
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
@ -36,28 +37,11 @@ func (a *GasAPI) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxq
|
||||
tsk types.TipSetKey) (types.BigInt, error) {
|
||||
ts := a.Chain.GetHeaviestTipSet()
|
||||
|
||||
var act types.Actor
|
||||
err := a.Stmgr.WithParentState(ts, a.Stmgr.WithActor(msg.From, stmgr.GetActor(&act)))
|
||||
if err != nil {
|
||||
return types.NewInt(0), xerrors.Errorf("getting actor: %w", err)
|
||||
}
|
||||
|
||||
parentBaseFee := ts.Blocks()[0].ParentBaseFee
|
||||
increaseFactor := math.Pow(1.+1./float64(build.BaseFeeMaxChangeDenom), float64(maxqueueblks))
|
||||
|
||||
feeInFuture := types.BigMul(parentBaseFee, types.NewInt(uint64(increaseFactor*(1<<8))))
|
||||
feeInFuture = types.BigDiv(feeInFuture, types.NewInt(1<<8))
|
||||
|
||||
gasLimitBig := types.NewInt(uint64(msg.GasLimit))
|
||||
maxAccepted := types.BigDiv(act.Balance, types.NewInt(MaxSpendOnFeeDenom))
|
||||
expectedFee := types.BigMul(feeInFuture, gasLimitBig)
|
||||
|
||||
out := feeInFuture
|
||||
if types.BigCmp(expectedFee, maxAccepted) > 0 {
|
||||
log.Warnf("Expected fee for message higher than tolerance: %s > %s, setting to tolerance",
|
||||
types.FIL(expectedFee), types.FIL(maxAccepted))
|
||||
out = types.BigDiv(maxAccepted, gasLimitBig)
|
||||
}
|
||||
out := types.BigDiv(feeInFuture, types.NewInt(1<<8))
|
||||
|
||||
if msg.GasPremium != types.EmptyInt {
|
||||
out = types.BigAdd(out, msg.GasPremium)
|
||||
@ -225,3 +209,24 @@ func (a *GasAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message,
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func capGasFee(msg *types.Message, maxFee abi.TokenAmount) {
|
||||
if maxFee.Equals(big.Zero()) {
|
||||
maxFee = types.NewInt(build.FilecoinPrecision / 10)
|
||||
}
|
||||
|
||||
gl := types.NewInt(uint64(msg.GasLimit))
|
||||
totalFee := types.BigMul(msg.GasFeeCap, gl)
|
||||
minerFee := types.BigMul(msg.GasPremium, gl)
|
||||
|
||||
if totalFee.LessThanEqual(maxFee) {
|
||||
return
|
||||
}
|
||||
|
||||
// scale chain/miner fee down proportionally to fit in our budget
|
||||
// TODO: there are probably smarter things we can do here to optimize
|
||||
// message inclusion latency
|
||||
|
||||
msg.GasFeeCap = big.Div(maxFee, gl)
|
||||
msg.GasPremium = big.Div(big.Div(big.Mul(minerFee, maxFee), totalFee), gl)
|
||||
}
|
||||
|
@ -8,9 +8,6 @@ import (
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
@ -115,27 +112,6 @@ func (a *MpoolAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (ci
|
||||
return a.Mpool.Push(smsg)
|
||||
}
|
||||
|
||||
func capGasFee(msg *types.Message, maxFee abi.TokenAmount) {
|
||||
if maxFee.Equals(big.Zero()) {
|
||||
return
|
||||
}
|
||||
|
||||
gl := types.NewInt(uint64(msg.GasLimit))
|
||||
totalFee := types.BigMul(msg.GasFeeCap, gl)
|
||||
minerFee := types.BigMul(msg.GasPremium, gl)
|
||||
|
||||
if totalFee.LessThanEqual(maxFee) {
|
||||
return
|
||||
}
|
||||
|
||||
// scale chain/miner fee down proportionally to fit in our budget
|
||||
// TODO: there are probably smarter things we can do here to optimize
|
||||
// message inclusion latency
|
||||
|
||||
msg.GasFeeCap = big.Div(maxFee, gl)
|
||||
msg.GasPremium = big.Div(big.Div(big.Mul(minerFee, maxFee), totalFee), gl)
|
||||
}
|
||||
|
||||
func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec) (*types.SignedMessage, error) {
|
||||
{
|
||||
fromA, err := a.Stmgr.ResolveToKeyAddress(ctx, msg.From, nil)
|
||||
|
@ -3,9 +3,10 @@ package paych
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
@ -38,6 +39,10 @@ func (a *PaychAPI) PaychGet(ctx context.Context, from, to address.Address, amt t
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *PaychAPI) PaychAvailableFunds(from, to address.Address) (*api.ChannelAvailableFunds, error) {
|
||||
return a.PaychMgr.AvailableFunds(from, to)
|
||||
}
|
||||
|
||||
func (a *PaychAPI) PaychGetWaitReady(ctx context.Context, sentinel cid.Cid) (address.Address, error) {
|
||||
return a.PaychMgr.GetPaychWaitReady(ctx, sentinel)
|
||||
}
|
||||
@ -64,9 +69,7 @@ func (a *PaychAPI) PaychNewPayment(ctx context.Context, from, to address.Address
|
||||
svs := make([]*paych.SignedVoucher, len(vouchers))
|
||||
|
||||
for i, v := range vouchers {
|
||||
sv, err := a.paychVoucherCreate(ctx, ch.Channel, paych.SignedVoucher{
|
||||
ChannelAddr: ch.Channel,
|
||||
|
||||
sv, err := a.PaychMgr.CreateVoucher(ctx, ch.Channel, paych.SignedVoucher{
|
||||
Amount: v.Amount,
|
||||
Lane: lane,
|
||||
|
||||
@ -78,8 +81,11 @@ func (a *PaychAPI) PaychNewPayment(ctx context.Context, from, to address.Address
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sv.Voucher == nil {
|
||||
return nil, xerrors.Errorf("Could not create voucher - shortfall of %d", sv.Shortfall)
|
||||
}
|
||||
|
||||
svs[i] = sv
|
||||
svs[i] = sv.Voucher
|
||||
}
|
||||
|
||||
return &api.PaymentInfo{
|
||||
@ -129,41 +135,10 @@ func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *
|
||||
// that will be used to create the voucher, so if previous vouchers exist, the
|
||||
// actual additional value of this voucher will only be the difference between
|
||||
// the two.
|
||||
func (a *PaychAPI) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*paych.SignedVoucher, error) {
|
||||
return a.paychVoucherCreate(ctx, pch, paych.SignedVoucher{ChannelAddr: pch, Amount: amt, Lane: lane})
|
||||
}
|
||||
|
||||
func (a *PaychAPI) paychVoucherCreate(ctx context.Context, pch address.Address, voucher paych.SignedVoucher) (*paych.SignedVoucher, error) {
|
||||
ci, err := a.PaychMgr.GetChannelInfo(pch)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get channel info: %w", err)
|
||||
}
|
||||
|
||||
nonce, err := a.PaychMgr.NextNonceForLane(ctx, pch, voucher.Lane)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting next nonce for lane: %w", err)
|
||||
}
|
||||
|
||||
sv := &voucher
|
||||
sv.Nonce = nonce
|
||||
|
||||
vb, err := sv.SigningBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig, err := a.WalletSign(ctx, ci.Control, vb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sv.Signature = sig
|
||||
|
||||
if _, err := a.PaychMgr.AddVoucherOutbound(ctx, pch, sv, nil, types.NewInt(0)); err != nil {
|
||||
return nil, xerrors.Errorf("failed to persist voucher: %w", err)
|
||||
}
|
||||
|
||||
return sv, nil
|
||||
// If there are insufficient funds in the channel to create the voucher,
|
||||
// returns a nil voucher and the shortfall.
|
||||
func (a *PaychAPI) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*api.VoucherCreateResult, error) {
|
||||
return a.PaychMgr.CreateVoucher(ctx, pch, paych.SignedVoucher{Amount: amt, Lane: lane})
|
||||
}
|
||||
|
||||
func (a *PaychAPI) PaychVoucherList(ctx context.Context, pch address.Address) ([]*paych.SignedVoucher, error) {
|
||||
|
@ -320,14 +320,12 @@ func (sm *StorageMinerAPI) MarketListRetrievalDeals(ctx context.Context) ([]retr
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (sm *StorageMinerAPI) MarketGetDealUpdates(ctx context.Context, d cid.Cid) (<-chan storagemarket.MinerDeal, error) {
|
||||
func (sm *StorageMinerAPI) MarketGetDealUpdates(ctx context.Context) (<-chan storagemarket.MinerDeal, error) {
|
||||
results := make(chan storagemarket.MinerDeal)
|
||||
unsub := sm.StorageProvider.SubscribeToEvents(func(evt storagemarket.ProviderEvent, deal storagemarket.MinerDeal) {
|
||||
if deal.ProposalCid.Equals(d) {
|
||||
select {
|
||||
case results <- deal:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
select {
|
||||
case results <- deal:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
})
|
||||
go func() {
|
||||
|
@ -60,7 +60,7 @@ func (pm *Manager) accessorCacheKey(from address.Address, to address.Address) st
|
||||
// access a channel use the same lock (the lock on the accessor)
|
||||
func (pm *Manager) addAccessorToCache(from address.Address, to address.Address) *channelAccessor {
|
||||
key := pm.accessorCacheKey(from, to)
|
||||
ca := newChannelAccessor(pm)
|
||||
ca := newChannelAccessor(pm, from, to)
|
||||
// TODO: Use LRU
|
||||
pm.channels[key] = ca
|
||||
return ca
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/node/modules/helpers"
|
||||
|
||||
"github.com/ipfs/go-datastore"
|
||||
@ -49,6 +51,7 @@ type paychAPI interface {
|
||||
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
|
||||
MpoolPushMessage(ctx context.Context, msg *types.Message, maxFee *api.MessageSendSpec) (*types.SignedMessage, error)
|
||||
WalletHas(ctx context.Context, addr address.Address) (bool, error)
|
||||
WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error)
|
||||
}
|
||||
|
||||
// managerAPI defines all methods needed by the manager
|
||||
@ -135,7 +138,16 @@ func (pm *Manager) GetPaych(ctx context.Context, from, to address.Address, amt t
|
||||
return address.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
return chanAccessor.getPaych(ctx, from, to, amt)
|
||||
return chanAccessor.getPaych(ctx, amt)
|
||||
}
|
||||
|
||||
func (pm *Manager) AvailableFunds(from address.Address, to address.Address) (*api.ChannelAvailableFunds, error) {
|
||||
chanAccessor, err := pm.accessorByFromTo(from, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chanAccessor.availableFunds()
|
||||
}
|
||||
|
||||
// GetPaychWaitReady waits until the create channel / add funds message with the
|
||||
@ -179,6 +191,15 @@ func (pm *Manager) GetChannelInfo(addr address.Address) (*ChannelInfo, error) {
|
||||
return ca.getChannelInfo(addr)
|
||||
}
|
||||
|
||||
func (pm *Manager) CreateVoucher(ctx context.Context, ch address.Address, voucher paych.SignedVoucher) (*api.VoucherCreateResult, error) {
|
||||
ca, err := pm.accessorByAddress(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ca.createVoucher(ctx, ch, voucher)
|
||||
}
|
||||
|
||||
// CheckVoucherValid checks if the given voucher is valid (is or could become spendable at some point).
|
||||
// If the channel is not in the store, fetches the channel from state (and checks that
|
||||
// the channel To address is owned by the wallet).
|
||||
@ -309,14 +330,6 @@ func (pm *Manager) ListVouchers(ctx context.Context, ch address.Address) ([]*Vou
|
||||
return ca.listVouchers(ctx, ch)
|
||||
}
|
||||
|
||||
func (pm *Manager) NextNonceForLane(ctx context.Context, ch address.Address, lane uint64) (uint64, error) {
|
||||
ca, err := pm.accessorByAddress(ch)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ca.nextNonceForLane(ctx, ch, lane)
|
||||
}
|
||||
|
||||
func (pm *Manager) Settle(ctx context.Context, addr address.Address) (cid.Cid, error) {
|
||||
ca, err := pm.accessorByAddress(addr)
|
||||
if err != nil {
|
||||
|
@ -5,6 +5,10 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
cbornode "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
@ -132,6 +136,7 @@ type mockPaychAPI struct {
|
||||
waitingCalls map[cid.Cid]*waitingCall
|
||||
waitingResponses map[cid.Cid]*waitingResponse
|
||||
wallet map[address.Address]struct{}
|
||||
signingKey []byte
|
||||
}
|
||||
|
||||
func newMockPaychAPI() *mockPaychAPI {
|
||||
@ -240,3 +245,17 @@ func (pchapi *mockPaychAPI) addWalletAddress(addr address.Address) {
|
||||
|
||||
pchapi.wallet[addr] = struct{}{}
|
||||
}
|
||||
|
||||
func (pchapi *mockPaychAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) {
|
||||
pchapi.lk.Lock()
|
||||
defer pchapi.lk.Unlock()
|
||||
|
||||
return sigs.Sign(crypto.SigTypeSecp256k1, pchapi.signingKey, msg)
|
||||
}
|
||||
|
||||
func (pchapi *mockPaychAPI) addSigningKey(key []byte) {
|
||||
pchapi.lk.Lock()
|
||||
defer pchapi.lk.Unlock()
|
||||
|
||||
pchapi.signingKey = key
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
@ -21,11 +23,36 @@ import (
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// insufficientFundsErr indicates that there are not enough funds in the
|
||||
// channel to create a voucher
|
||||
type insufficientFundsErr interface {
|
||||
Shortfall() types.BigInt
|
||||
}
|
||||
|
||||
type ErrInsufficientFunds struct {
|
||||
shortfall types.BigInt
|
||||
}
|
||||
|
||||
func newErrInsufficientFunds(shortfall types.BigInt) *ErrInsufficientFunds {
|
||||
return &ErrInsufficientFunds{shortfall: shortfall}
|
||||
}
|
||||
|
||||
func (e *ErrInsufficientFunds) Error() string {
|
||||
return fmt.Sprintf("not enough funds in channel to cover voucher - shortfall: %d", e.shortfall)
|
||||
}
|
||||
|
||||
func (e *ErrInsufficientFunds) Shortfall() types.BigInt {
|
||||
return e.shortfall
|
||||
}
|
||||
|
||||
// channelAccessor is used to simplify locking when accessing a channel
|
||||
type channelAccessor struct {
|
||||
// waitCtx is used by processes that wait for things to be confirmed
|
||||
// on chain
|
||||
waitCtx context.Context
|
||||
from address.Address
|
||||
to address.Address
|
||||
|
||||
// chctx is used by background processes (eg when waiting for things to be
|
||||
// confirmed on chain)
|
||||
chctx context.Context
|
||||
sa *stateAccessor
|
||||
api managerAPI
|
||||
store *Store
|
||||
@ -34,14 +61,16 @@ type channelAccessor struct {
|
||||
msgListeners msgListeners
|
||||
}
|
||||
|
||||
func newChannelAccessor(pm *Manager) *channelAccessor {
|
||||
func newChannelAccessor(pm *Manager, from address.Address, to address.Address) *channelAccessor {
|
||||
return &channelAccessor{
|
||||
lk: &channelLock{globalLock: &pm.lk},
|
||||
from: from,
|
||||
to: to,
|
||||
chctx: pm.ctx,
|
||||
sa: pm.sa,
|
||||
api: pm.pchapi,
|
||||
store: pm.store,
|
||||
lk: &channelLock{globalLock: &pm.lk},
|
||||
msgListeners: newMsgListeners(),
|
||||
waitCtx: pm.ctx,
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +81,69 @@ func (ca *channelAccessor) getChannelInfo(addr address.Address) (*ChannelInfo, e
|
||||
return ca.store.ByAddress(addr)
|
||||
}
|
||||
|
||||
// createVoucher creates a voucher with the given specification, setting its
|
||||
// nonce, signing the voucher and storing it in the local datastore.
|
||||
// If there are not enough funds in the channel to create the voucher, returns
|
||||
// the shortfall in funds.
|
||||
func (ca *channelAccessor) createVoucher(ctx context.Context, ch address.Address, voucher paych.SignedVoucher) (*api.VoucherCreateResult, error) {
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
|
||||
// Find the channel for the voucher
|
||||
ci, err := ca.store.ByAddress(ch)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get channel info by address: %w", err)
|
||||
}
|
||||
|
||||
// Set the voucher channel
|
||||
sv := &voucher
|
||||
sv.ChannelAddr = ch
|
||||
|
||||
// Get the next nonce on the given lane
|
||||
sv.Nonce = ca.nextNonceForLane(ci, voucher.Lane)
|
||||
|
||||
// Sign the voucher
|
||||
vb, err := sv.SigningBytes()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get voucher signing bytes: %w", err)
|
||||
}
|
||||
|
||||
sig, err := ca.api.WalletSign(ctx, ci.Control, vb)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to sign voucher: %w", err)
|
||||
}
|
||||
sv.Signature = sig
|
||||
|
||||
// Store the voucher
|
||||
if _, err := ca.addVoucherUnlocked(ctx, ch, sv, nil, types.NewInt(0)); err != nil {
|
||||
// If there are not enough funds in the channel to cover the voucher,
|
||||
// return a voucher create result with the shortfall
|
||||
var ife insufficientFundsErr
|
||||
if xerrors.As(err, &ife) {
|
||||
return &api.VoucherCreateResult{
|
||||
Shortfall: ife.Shortfall(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, xerrors.Errorf("failed to persist voucher: %w", err)
|
||||
}
|
||||
|
||||
return &api.VoucherCreateResult{Voucher: sv, Shortfall: types.NewInt(0)}, nil
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) nextNonceForLane(ci *ChannelInfo, lane uint64) uint64 {
|
||||
var maxnonce uint64
|
||||
for _, v := range ci.Vouchers {
|
||||
if v.Voucher.Lane == lane {
|
||||
if v.Voucher.Nonce > maxnonce {
|
||||
maxnonce = v.Voucher.Nonce
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxnonce + 1
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) checkVoucherValid(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (map[uint64]*paych.LaneState, error) {
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
@ -133,7 +225,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
|
||||
// Must not exceed actor balance
|
||||
newTotal := types.BigAdd(totalRedeemed, pchState.ToSend)
|
||||
if act.Balance.LessThan(newTotal) {
|
||||
return nil, fmt.Errorf("not enough funds in channel to cover voucher")
|
||||
return nil, newErrInsufficientFunds(types.BigSub(newTotal, act.Balance))
|
||||
}
|
||||
|
||||
if len(sv.Merges) != 0 {
|
||||
@ -221,6 +313,10 @@ func (ca *channelAccessor) addVoucher(ctx context.Context, ch address.Address, s
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
|
||||
return ca.addVoucherUnlocked(ctx, ch, sv, proof, minDelta)
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) addVoucherUnlocked(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
|
||||
ci, err := ca.store.ByAddress(ch)
|
||||
if err != nil {
|
||||
return types.BigInt{}, err
|
||||
@ -382,28 +478,6 @@ func (ca *channelAccessor) listVouchers(ctx context.Context, ch address.Address)
|
||||
return ca.store.VouchersForPaych(ch)
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) nextNonceForLane(ctx context.Context, ch address.Address, lane uint64) (uint64, error) {
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
|
||||
// TODO: should this take into account lane state?
|
||||
vouchers, err := ca.store.VouchersForPaych(ch)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var maxnonce uint64
|
||||
for _, v := range vouchers {
|
||||
if v.Voucher.Lane == lane {
|
||||
if v.Voucher.Nonce > maxnonce {
|
||||
maxnonce = v.Voucher.Nonce
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxnonce + 1, nil
|
||||
}
|
||||
|
||||
// laneState gets the LaneStates from chain, then applies all vouchers in
|
||||
// the data store over the chain state
|
||||
func (ca *channelAccessor) laneState(ctx context.Context, state *paych.State, ch address.Address) (map[uint64]*paych.LaneState, error) {
|
||||
@ -479,7 +553,7 @@ func (ca *channelAccessor) totalRedeemedWithVoucher(laneStates map[uint64]*paych
|
||||
lane, ok := laneStates[sv.Lane]
|
||||
if ok {
|
||||
// If the voucher is for an existing lane, and the voucher nonce
|
||||
// and is higher than the lane nonce
|
||||
// is higher than the lane nonce
|
||||
if sv.Nonce > lane.Nonce {
|
||||
// Add the delta between the redeemed amount and the voucher
|
||||
// amount to the total
|
||||
|
@ -381,11 +381,76 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCreateVoucher(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
// Create a voucher in lane 1
|
||||
voucherLane1Amt := big.NewInt(5)
|
||||
voucher := paych.SignedVoucher{
|
||||
Lane: 1,
|
||||
Amount: voucherLane1Amt,
|
||||
}
|
||||
res, err := s.mgr.CreateVoucher(ctx, s.ch, voucher)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.Voucher)
|
||||
require.Equal(t, s.ch, res.Voucher.ChannelAddr)
|
||||
require.Equal(t, voucherLane1Amt, res.Voucher.Amount)
|
||||
require.EqualValues(t, 0, res.Shortfall.Int64())
|
||||
|
||||
nonce := res.Voucher.Nonce
|
||||
|
||||
// Create a voucher in lane 1 again, with a higher amount
|
||||
voucherLane1Amt = big.NewInt(8)
|
||||
voucher = paych.SignedVoucher{
|
||||
Lane: 1,
|
||||
Amount: voucherLane1Amt,
|
||||
}
|
||||
res, err = s.mgr.CreateVoucher(ctx, s.ch, voucher)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.Voucher)
|
||||
require.Equal(t, s.ch, res.Voucher.ChannelAddr)
|
||||
require.Equal(t, voucherLane1Amt, res.Voucher.Amount)
|
||||
require.EqualValues(t, 0, res.Shortfall.Int64())
|
||||
require.Equal(t, nonce+1, res.Voucher.Nonce)
|
||||
|
||||
// Create a voucher in lane 2 that covers all the remaining funds
|
||||
// in the channel
|
||||
voucherLane2Amt := big.Sub(s.amt, voucherLane1Amt)
|
||||
voucher = paych.SignedVoucher{
|
||||
Lane: 2,
|
||||
Amount: voucherLane2Amt,
|
||||
}
|
||||
res, err = s.mgr.CreateVoucher(ctx, s.ch, voucher)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.Voucher)
|
||||
require.Equal(t, s.ch, res.Voucher.ChannelAddr)
|
||||
require.Equal(t, voucherLane2Amt, res.Voucher.Amount)
|
||||
require.EqualValues(t, 0, res.Shortfall.Int64())
|
||||
|
||||
// Create a voucher in lane 2 that exceeds the remaining funds in the
|
||||
// channel
|
||||
voucherLane2Amt = big.Add(voucherLane2Amt, big.NewInt(1))
|
||||
voucher = paych.SignedVoucher{
|
||||
Lane: 2,
|
||||
Amount: voucherLane2Amt,
|
||||
}
|
||||
res, err = s.mgr.CreateVoucher(ctx, s.ch, voucher)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Expect a shortfall value equal to the amount required to add the voucher
|
||||
// to the channel
|
||||
require.Nil(t, res.Voucher)
|
||||
require.EqualValues(t, 1, res.Shortfall.Int64())
|
||||
}
|
||||
|
||||
func TestAddVoucherDelta(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, _, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
voucherLane := uint64(1)
|
||||
|
||||
@ -393,23 +458,23 @@ func TestAddVoucherDelta(t *testing.T) {
|
||||
minDelta := big.NewInt(2)
|
||||
nonce := uint64(1)
|
||||
voucherAmount := big.NewInt(1)
|
||||
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err := s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.Error(t, err)
|
||||
|
||||
// Expect success when adding a voucher whose amount is equal to minDelta
|
||||
nonce++
|
||||
voucherAmount = big.NewInt(2)
|
||||
sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
delta, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv = createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
delta, err := s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, delta.Int64(), 2)
|
||||
|
||||
// Check that delta is correct when there's an existing voucher
|
||||
nonce++
|
||||
voucherAmount = big.NewInt(5)
|
||||
sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
delta, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv = createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
delta, err = s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, delta.Int64(), 3)
|
||||
|
||||
@ -417,8 +482,8 @@ func TestAddVoucherDelta(t *testing.T) {
|
||||
nonce = uint64(1)
|
||||
voucherAmount = big.NewInt(6)
|
||||
voucherLane = uint64(2)
|
||||
sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
delta, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv = createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
delta, err = s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, delta.Int64(), 6)
|
||||
}
|
||||
@ -427,7 +492,7 @@ func TestAddVoucherNextLane(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, _, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
minDelta := big.NewInt(0)
|
||||
voucherAmount := big.NewInt(2)
|
||||
@ -435,40 +500,40 @@ func TestAddVoucherNextLane(t *testing.T) {
|
||||
// Add a voucher in lane 2
|
||||
nonce := uint64(1)
|
||||
voucherLane := uint64(2)
|
||||
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err := s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
ci, err := mgr.GetChannelInfo(ch)
|
||||
ci, err := s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, ci.NextLane, 3)
|
||||
|
||||
// Allocate a lane (should be lane 3)
|
||||
lane, err := mgr.AllocateLane(ch)
|
||||
lane, err := s.mgr.AllocateLane(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, lane, 3)
|
||||
|
||||
ci, err = mgr.GetChannelInfo(ch)
|
||||
ci, err = s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, ci.NextLane, 4)
|
||||
|
||||
// Add a voucher in lane 1
|
||||
voucherLane = uint64(1)
|
||||
sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv = createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err = s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
ci, err = mgr.GetChannelInfo(ch)
|
||||
ci, err = s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, ci.NextLane, 4)
|
||||
|
||||
// Add a voucher in lane 7
|
||||
voucherLane = uint64(7)
|
||||
sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv = createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err = s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
ci, err = mgr.GetChannelInfo(ch)
|
||||
ci, err = s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, ci.NextLane, 8)
|
||||
}
|
||||
@ -477,15 +542,15 @@ func TestAllocateLane(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, _, ch, _ := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
// First lane should be 0
|
||||
lane, err := mgr.AllocateLane(ch)
|
||||
lane, err := s.mgr.AllocateLane(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, lane, 0)
|
||||
|
||||
// Next lane should be 1
|
||||
lane, err = mgr.AllocateLane(ch)
|
||||
lane, err = s.mgr.AllocateLane(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, lane, 1)
|
||||
}
|
||||
@ -553,7 +618,7 @@ func TestAddVoucherProof(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, _, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
nonce := uint64(1)
|
||||
voucherAmount := big.NewInt(1)
|
||||
@ -563,34 +628,34 @@ func TestAddVoucherProof(t *testing.T) {
|
||||
|
||||
// Add a voucher with no proof
|
||||
var proof []byte
|
||||
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
sv := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err := s.mgr.AddVoucherOutbound(ctx, s.ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Expect one voucher with no proof
|
||||
ci, err := mgr.GetChannelInfo(ch)
|
||||
ci, err := s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, ci.Vouchers, 1)
|
||||
require.Len(t, ci.Vouchers[0].Proof, 0)
|
||||
|
||||
// Add same voucher with no proof
|
||||
voucherLane = uint64(1)
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, sv, proof, minDelta)
|
||||
_, err = s.mgr.AddVoucherOutbound(ctx, s.ch, sv, proof, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Expect one voucher with no proof
|
||||
ci, err = mgr.GetChannelInfo(ch)
|
||||
ci, err = s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, ci.Vouchers, 1)
|
||||
require.Len(t, ci.Vouchers[0].Proof, 0)
|
||||
|
||||
// Add same voucher with proof
|
||||
proof = []byte{1}
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, sv, proof, minDelta)
|
||||
_, err = s.mgr.AddVoucherOutbound(ctx, s.ch, sv, proof, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should add proof to existing voucher
|
||||
ci, err = mgr.GetChannelInfo(ch)
|
||||
ci, err = s.mgr.GetChannelInfo(s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, ci.Vouchers, 1)
|
||||
require.Len(t, ci.Vouchers[0].Proof, 1)
|
||||
@ -663,47 +728,47 @@ func TestBestSpendable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, mock, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
// Add vouchers to lane 1 with amounts: [1, 2, 3]
|
||||
voucherLane := uint64(1)
|
||||
minDelta := big.NewInt(0)
|
||||
nonce := uint64(1)
|
||||
voucherAmount := big.NewInt(1)
|
||||
svL1V1 := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err := mgr.AddVoucherInbound(ctx, ch, svL1V1, nil, minDelta)
|
||||
svL1V1 := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err := s.mgr.AddVoucherInbound(ctx, s.ch, svL1V1, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
nonce++
|
||||
voucherAmount = big.NewInt(2)
|
||||
svL1V2 := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherInbound(ctx, ch, svL1V2, nil, minDelta)
|
||||
svL1V2 := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err = s.mgr.AddVoucherInbound(ctx, s.ch, svL1V2, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
nonce++
|
||||
voucherAmount = big.NewInt(3)
|
||||
svL1V3 := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherInbound(ctx, ch, svL1V3, nil, minDelta)
|
||||
svL1V3 := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err = s.mgr.AddVoucherInbound(ctx, s.ch, svL1V3, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add voucher to lane 2 with amounts: [2]
|
||||
voucherLane = uint64(2)
|
||||
nonce = uint64(1)
|
||||
voucherAmount = big.NewInt(2)
|
||||
svL2V1 := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherInbound(ctx, ch, svL2V1, nil, minDelta)
|
||||
svL2V1 := createTestVoucher(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err = s.mgr.AddVoucherInbound(ctx, s.ch, svL2V1, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Return success exit code from calls to check if voucher is spendable
|
||||
bsapi := newMockBestSpendableAPI(mgr)
|
||||
mock.setCallResponse(&api.InvocResult{
|
||||
bsapi := newMockBestSpendableAPI(s.mgr)
|
||||
s.mock.setCallResponse(&api.InvocResult{
|
||||
MsgRct: &types.MessageReceipt{
|
||||
ExitCode: 0,
|
||||
},
|
||||
})
|
||||
|
||||
// Verify best spendable vouchers on each lane
|
||||
vouchers, err := BestSpendableByLane(ctx, bsapi, ch)
|
||||
vouchers, err := BestSpendableByLane(ctx, bsapi, s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, vouchers, 2)
|
||||
|
||||
@ -716,21 +781,21 @@ func TestBestSpendable(t *testing.T) {
|
||||
require.EqualValues(t, 2, vchr.Amount.Int64())
|
||||
|
||||
// Submit voucher from lane 2
|
||||
_, err = mgr.SubmitVoucher(ctx, ch, svL2V1, nil, nil)
|
||||
_, err = s.mgr.SubmitVoucher(ctx, s.ch, svL2V1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Best spendable voucher should no longer include lane 2
|
||||
// (because voucher has not been submitted)
|
||||
vouchers, err = BestSpendableByLane(ctx, bsapi, ch)
|
||||
vouchers, err = BestSpendableByLane(ctx, bsapi, s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, vouchers, 1)
|
||||
|
||||
// Submit first voucher from lane 1
|
||||
_, err = mgr.SubmitVoucher(ctx, ch, svL1V1, nil, nil)
|
||||
_, err = s.mgr.SubmitVoucher(ctx, s.ch, svL1V1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Best spendable voucher for lane 1 should still be highest value voucher
|
||||
vouchers, err = BestSpendableByLane(ctx, bsapi, ch)
|
||||
vouchers, err = BestSpendableByLane(ctx, bsapi, s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, vouchers, 1)
|
||||
|
||||
@ -743,18 +808,18 @@ func TestCheckSpendable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, mock, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
// Create voucher with Extra
|
||||
voucherLane := uint64(1)
|
||||
nonce := uint64(1)
|
||||
voucherAmount := big.NewInt(1)
|
||||
voucher := createTestVoucherWithExtra(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
voucher := createTestVoucherWithExtra(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
|
||||
// Add voucher with proof
|
||||
minDelta := big.NewInt(0)
|
||||
proof := []byte("proof")
|
||||
_, err := mgr.AddVoucherInbound(ctx, ch, voucher, proof, minDelta)
|
||||
_, err := s.mgr.AddVoucherInbound(ctx, s.ch, voucher, proof, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Return success exit code from VM call, which indicates that voucher is
|
||||
@ -764,17 +829,17 @@ func TestCheckSpendable(t *testing.T) {
|
||||
ExitCode: 0,
|
||||
},
|
||||
}
|
||||
mock.setCallResponse(successResponse)
|
||||
s.mock.setCallResponse(successResponse)
|
||||
|
||||
// Check that spendable is true
|
||||
secret := []byte("secret")
|
||||
otherProof := []byte("other proof")
|
||||
spendable, err := mgr.CheckVoucherSpendable(ctx, ch, voucher, secret, otherProof)
|
||||
spendable, err := s.mgr.CheckVoucherSpendable(ctx, s.ch, voucher, secret, otherProof)
|
||||
require.NoError(t, err)
|
||||
require.True(t, spendable)
|
||||
|
||||
// Check that the secret and proof were passed through correctly
|
||||
lastCall := mock.getLastCall()
|
||||
lastCall := s.mock.getLastCall()
|
||||
var p paych.UpdateChannelStateParams
|
||||
err = p.UnmarshalCBOR(bytes.NewReader(lastCall.Params))
|
||||
require.NoError(t, err)
|
||||
@ -784,11 +849,11 @@ func TestCheckSpendable(t *testing.T) {
|
||||
// Check that if no proof is supplied, the proof supplied to add voucher
|
||||
// above is used
|
||||
secret2 := []byte("secret2")
|
||||
spendable, err = mgr.CheckVoucherSpendable(ctx, ch, voucher, secret2, nil)
|
||||
spendable, err = s.mgr.CheckVoucherSpendable(ctx, s.ch, voucher, secret2, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, spendable)
|
||||
|
||||
lastCall = mock.getLastCall()
|
||||
lastCall = s.mock.getLastCall()
|
||||
var p2 paych.UpdateChannelStateParams
|
||||
err = p2.UnmarshalCBOR(bytes.NewReader(lastCall.Params))
|
||||
require.NoError(t, err)
|
||||
@ -796,26 +861,26 @@ func TestCheckSpendable(t *testing.T) {
|
||||
require.Equal(t, secret2, p2.Secret)
|
||||
|
||||
// Check that if VM call returns non-success exit code, spendable is false
|
||||
mock.setCallResponse(&api.InvocResult{
|
||||
s.mock.setCallResponse(&api.InvocResult{
|
||||
MsgRct: &types.MessageReceipt{
|
||||
ExitCode: 1,
|
||||
},
|
||||
})
|
||||
spendable, err = mgr.CheckVoucherSpendable(ctx, ch, voucher, secret, nil)
|
||||
spendable, err = s.mgr.CheckVoucherSpendable(ctx, s.ch, voucher, secret, nil)
|
||||
require.NoError(t, err)
|
||||
require.False(t, spendable)
|
||||
|
||||
// Return success exit code (indicating voucher is spendable)
|
||||
mock.setCallResponse(successResponse)
|
||||
spendable, err = mgr.CheckVoucherSpendable(ctx, ch, voucher, secret, nil)
|
||||
s.mock.setCallResponse(successResponse)
|
||||
spendable, err = s.mgr.CheckVoucherSpendable(ctx, s.ch, voucher, secret, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, spendable)
|
||||
|
||||
// Check that voucher is no longer spendable once it has been submitted
|
||||
_, err = mgr.SubmitVoucher(ctx, ch, voucher, nil, nil)
|
||||
_, err = s.mgr.SubmitVoucher(ctx, s.ch, voucher, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
spendable, err = mgr.CheckVoucherSpendable(ctx, ch, voucher, secret, nil)
|
||||
spendable, err = s.mgr.CheckVoucherSpendable(ctx, s.ch, voucher, secret, nil)
|
||||
require.NoError(t, err)
|
||||
require.False(t, spendable)
|
||||
}
|
||||
@ -824,28 +889,28 @@ func TestSubmitVoucher(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, mock, ch, fromKeyPrivate := testSetupMgrWithChannel(ctx, t)
|
||||
s := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
// Create voucher with Extra
|
||||
voucherLane := uint64(1)
|
||||
nonce := uint64(1)
|
||||
voucherAmount := big.NewInt(1)
|
||||
voucher := createTestVoucherWithExtra(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
voucher := createTestVoucherWithExtra(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
|
||||
// Add voucher with proof
|
||||
minDelta := big.NewInt(0)
|
||||
addVoucherProof := []byte("proof")
|
||||
_, err := mgr.AddVoucherInbound(ctx, ch, voucher, addVoucherProof, minDelta)
|
||||
_, err := s.mgr.AddVoucherInbound(ctx, s.ch, voucher, addVoucherProof, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Submit voucher
|
||||
secret := []byte("secret")
|
||||
submitProof := []byte("submit proof")
|
||||
submitCid, err := mgr.SubmitVoucher(ctx, ch, voucher, secret, submitProof)
|
||||
submitCid, err := s.mgr.SubmitVoucher(ctx, s.ch, voucher, secret, submitProof)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that the secret and proof were passed through correctly
|
||||
msg := mock.pushedMessages(submitCid)
|
||||
msg := s.mock.pushedMessages(submitCid)
|
||||
var p paych.UpdateChannelStateParams
|
||||
err = p.UnmarshalCBOR(bytes.NewReader(msg.Message.Params))
|
||||
require.NoError(t, err)
|
||||
@ -858,14 +923,14 @@ func TestSubmitVoucher(t *testing.T) {
|
||||
voucherAmount = big.NewInt(2)
|
||||
addVoucherProof2 := []byte("proof2")
|
||||
secret2 := []byte("secret2")
|
||||
voucher = createTestVoucherWithExtra(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherInbound(ctx, ch, voucher, addVoucherProof2, minDelta)
|
||||
voucher = createTestVoucherWithExtra(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
_, err = s.mgr.AddVoucherInbound(ctx, s.ch, voucher, addVoucherProof2, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
submitCid, err = mgr.SubmitVoucher(ctx, ch, voucher, secret2, nil)
|
||||
submitCid, err = s.mgr.SubmitVoucher(ctx, s.ch, voucher, secret2, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg = mock.pushedMessages(submitCid)
|
||||
msg = s.mock.pushedMessages(submitCid)
|
||||
var p2 paych.UpdateChannelStateParams
|
||||
err = p2.UnmarshalCBOR(bytes.NewReader(msg.Message.Params))
|
||||
require.NoError(t, err)
|
||||
@ -877,11 +942,11 @@ func TestSubmitVoucher(t *testing.T) {
|
||||
voucherAmount = big.NewInt(3)
|
||||
secret3 := []byte("secret2")
|
||||
proof3 := []byte("proof3")
|
||||
voucher = createTestVoucherWithExtra(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
|
||||
submitCid, err = mgr.SubmitVoucher(ctx, ch, voucher, secret3, proof3)
|
||||
voucher = createTestVoucherWithExtra(t, s.ch, voucherLane, nonce, voucherAmount, s.fromKeyPrivate)
|
||||
submitCid, err = s.mgr.SubmitVoucher(ctx, s.ch, voucher, secret3, proof3)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg = mock.pushedMessages(submitCid)
|
||||
msg = s.mock.pushedMessages(submitCid)
|
||||
var p3 paych.UpdateChannelStateParams
|
||||
err = p3.UnmarshalCBOR(bytes.NewReader(msg.Message.Params))
|
||||
require.NoError(t, err)
|
||||
@ -889,7 +954,7 @@ func TestSubmitVoucher(t *testing.T) {
|
||||
require.Equal(t, secret3, p3.Secret)
|
||||
|
||||
// Verify that vouchers are marked as submitted
|
||||
vis, err := mgr.ListVouchers(ctx, ch)
|
||||
vis, err := s.mgr.ListVouchers(ctx, s.ch)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, vis, 3)
|
||||
|
||||
@ -898,55 +963,20 @@ func TestSubmitVoucher(t *testing.T) {
|
||||
}
|
||||
|
||||
// Attempting to submit the same voucher again should fail
|
||||
_, err = mgr.SubmitVoucher(ctx, ch, voucher, secret2, nil)
|
||||
_, err = s.mgr.SubmitVoucher(ctx, s.ch, voucher, secret2, nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNextNonceForLane(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Set up a manager with a single payment channel
|
||||
mgr, _, ch, key := testSetupMgrWithChannel(ctx, t)
|
||||
|
||||
// Expect next nonce for non-existent lane to be 1
|
||||
next, err := mgr.NextNonceForLane(ctx, ch, 1)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, next, 1)
|
||||
|
||||
voucherAmount := big.NewInt(1)
|
||||
minDelta := big.NewInt(0)
|
||||
voucherAmount = big.NewInt(2)
|
||||
|
||||
// Add vouchers such that we have
|
||||
// lane 1: nonce 2
|
||||
// lane 1: nonce 4
|
||||
voucherLane := uint64(1)
|
||||
for _, nonce := range []uint64{2, 4} {
|
||||
voucherAmount = big.Add(voucherAmount, big.NewInt(1))
|
||||
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, key)
|
||||
_, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// lane 2: nonce 7
|
||||
voucherLane = uint64(2)
|
||||
nonce := uint64(7)
|
||||
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, key)
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Expect next nonce for lane 1 to be 5
|
||||
next, err = mgr.NextNonceForLane(ctx, ch, 1)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, next, 5)
|
||||
|
||||
// Expect next nonce for lane 2 to be 8
|
||||
next, err = mgr.NextNonceForLane(ctx, ch, 2)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, next, 8)
|
||||
type testScaffold struct {
|
||||
mgr *Manager
|
||||
mock *mockManagerAPI
|
||||
ch address.Address
|
||||
amt big.Int
|
||||
fromAcct address.Address
|
||||
fromKeyPrivate []byte
|
||||
}
|
||||
|
||||
func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, *mockManagerAPI, address.Address, []byte) {
|
||||
func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold {
|
||||
fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
|
||||
|
||||
ch := tutils.NewIDAddr(t, 100)
|
||||
@ -962,11 +992,12 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, *mock
|
||||
mock.setAccountState(toAcct, account.State{Address: to})
|
||||
|
||||
// Create channel in state
|
||||
balance := big.NewInt(20)
|
||||
act := &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Head: cid.Cid{},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(20),
|
||||
Balance: balance,
|
||||
}
|
||||
mock.setPaychState(ch, act, paych.State{
|
||||
From: fromAcct,
|
||||
@ -991,7 +1022,17 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, *mock
|
||||
err = mgr.store.putChannelInfo(ci)
|
||||
require.NoError(t, err)
|
||||
|
||||
return mgr, mock, ch, fromKeyPrivate
|
||||
// Add the from signing key to the wallet
|
||||
mock.addSigningKey(fromKeyPrivate)
|
||||
|
||||
return &testScaffold{
|
||||
mgr: mgr,
|
||||
mock: mock,
|
||||
ch: ch,
|
||||
amt: balance,
|
||||
fromAcct: fromAcct,
|
||||
fromKeyPrivate: fromKeyPrivate,
|
||||
}
|
||||
}
|
||||
|
||||
func testGenerateKeyPair(t *testing.T) ([]byte, []byte) {
|
||||
|
@ -6,6 +6,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/account"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
|
||||
cborrpc "github.com/filecoin-project/go-cbor-util"
|
||||
|
||||
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
|
||||
@ -650,8 +656,6 @@ func TestPaychGetMergeAddFunds(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Queue up two add funds requests behind create channel
|
||||
//var addFundsQueuedUp sync.WaitGroup
|
||||
//addFundsQueuedUp.Add(2)
|
||||
var addFundsSent sync.WaitGroup
|
||||
addFundsSent.Add(2)
|
||||
|
||||
@ -662,7 +666,6 @@ func TestPaychGetMergeAddFunds(t *testing.T) {
|
||||
var addFundsMcid1 cid.Cid
|
||||
var addFundsMcid2 cid.Cid
|
||||
go func() {
|
||||
//go addFundsQueuedUp.Done()
|
||||
defer addFundsSent.Done()
|
||||
|
||||
// Request add funds - should block until create channel has completed
|
||||
@ -671,7 +674,6 @@ func TestPaychGetMergeAddFunds(t *testing.T) {
|
||||
}()
|
||||
|
||||
go func() {
|
||||
//go addFundsQueuedUp.Done()
|
||||
defer addFundsSent.Done()
|
||||
|
||||
// Request add funds again - should merge with waiting add funds request
|
||||
@ -899,6 +901,168 @@ func TestPaychGetMergeAddFundsCtxCancelAll(t *testing.T) {
|
||||
require.Equal(t, createAmt, createMsg.Message.Value)
|
||||
}
|
||||
|
||||
// TestPaychAvailableFunds tests that PaychAvailableFunds returns the correct
|
||||
// channel state
|
||||
func TestPaychAvailableFunds(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
|
||||
|
||||
fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
|
||||
ch := tutils.NewIDAddr(t, 100)
|
||||
from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
|
||||
to := tutils.NewIDAddr(t, 102)
|
||||
fromAcct := tutils.NewActorAddr(t, "fromAct")
|
||||
toAcct := tutils.NewActorAddr(t, "toAct")
|
||||
|
||||
mock := newMockManagerAPI()
|
||||
defer mock.close()
|
||||
|
||||
mgr, err := newManager(store, mock)
|
||||
require.NoError(t, err)
|
||||
|
||||
// No channel created yet so available funds should be all zeroes
|
||||
av, err := mgr.AvailableFunds(from, to)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, av.Channel)
|
||||
require.Nil(t, av.PendingWaitSentinel)
|
||||
require.EqualValues(t, 0, av.ConfirmedAmt.Int64())
|
||||
require.EqualValues(t, 0, av.PendingAmt.Int64())
|
||||
require.EqualValues(t, 0, av.QueuedAmt.Int64())
|
||||
require.EqualValues(t, 0, av.VoucherReedeemedAmt.Int64())
|
||||
|
||||
// Send create message for a channel with value 10
|
||||
createAmt := big.NewInt(10)
|
||||
_, createMsgCid, err := mgr.GetPaych(ctx, from, to, createAmt)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Available funds should reflect create channel message sent
|
||||
av, err = mgr.AvailableFunds(from, to)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, av.Channel)
|
||||
require.EqualValues(t, 0, av.ConfirmedAmt.Int64())
|
||||
require.EqualValues(t, createAmt, av.PendingAmt)
|
||||
require.EqualValues(t, 0, av.QueuedAmt.Int64())
|
||||
require.EqualValues(t, 0, av.VoucherReedeemedAmt.Int64())
|
||||
// Should now have a pending wait sentinel
|
||||
require.NotNil(t, av.PendingWaitSentinel)
|
||||
|
||||
// Queue up an add funds request behind create channel
|
||||
var addFundsSent sync.WaitGroup
|
||||
addFundsSent.Add(1)
|
||||
|
||||
addFundsAmt := big.NewInt(5)
|
||||
var addFundsMcid cid.Cid
|
||||
go func() {
|
||||
defer addFundsSent.Done()
|
||||
|
||||
// Request add funds - should block until create channel has completed
|
||||
_, addFundsMcid, err = mgr.GetPaych(ctx, from, to, addFundsAmt)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
// Wait for add funds request to be queued up
|
||||
waitForQueueSize(t, mgr, from, to, 1)
|
||||
|
||||
// Available funds should now include queued funds
|
||||
av, err = mgr.AvailableFunds(from, to)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, av.Channel)
|
||||
require.NotNil(t, av.PendingWaitSentinel)
|
||||
require.EqualValues(t, 0, av.ConfirmedAmt.Int64())
|
||||
// create amount is still pending
|
||||
require.EqualValues(t, createAmt, av.PendingAmt)
|
||||
// queued amount now includes add funds amount
|
||||
require.EqualValues(t, addFundsAmt, av.QueuedAmt)
|
||||
require.EqualValues(t, 0, av.VoucherReedeemedAmt.Int64())
|
||||
|
||||
// Create channel in state
|
||||
arr, err := adt.MakeEmptyArray(mock.store).Root()
|
||||
require.NoError(t, err)
|
||||
mock.setAccountState(fromAcct, account.State{Address: from})
|
||||
mock.setAccountState(toAcct, account.State{Address: to})
|
||||
act := &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Head: cid.Cid{},
|
||||
Nonce: 0,
|
||||
Balance: createAmt,
|
||||
}
|
||||
mock.setPaychState(ch, act, paych.State{
|
||||
From: fromAcct,
|
||||
To: toAcct,
|
||||
ToSend: big.NewInt(0),
|
||||
SettlingAt: abi.ChainEpoch(0),
|
||||
MinSettleHeight: abi.ChainEpoch(0),
|
||||
LaneStates: arr,
|
||||
})
|
||||
|
||||
// Send create channel response
|
||||
response := testChannelResponse(t, ch)
|
||||
mock.receiveMsgResponse(createMsgCid, response)
|
||||
|
||||
// Wait for create channel response
|
||||
chres, err := mgr.GetPaychWaitReady(ctx, *av.PendingWaitSentinel)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ch, chres)
|
||||
|
||||
// Wait for add funds request to be sent
|
||||
addFundsSent.Wait()
|
||||
|
||||
// Available funds should now include the channel and also a wait sentinel
|
||||
// for the add funds message
|
||||
av, err = mgr.AvailableFunds(from, to)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, av.Channel)
|
||||
require.NotNil(t, av.PendingWaitSentinel)
|
||||
// create amount is now confirmed
|
||||
require.EqualValues(t, createAmt, av.ConfirmedAmt)
|
||||
// add funds amount it now pending
|
||||
require.EqualValues(t, addFundsAmt, av.PendingAmt)
|
||||
require.EqualValues(t, 0, av.QueuedAmt.Int64())
|
||||
require.EqualValues(t, 0, av.VoucherReedeemedAmt.Int64())
|
||||
|
||||
// Send success add funds response
|
||||
mock.receiveMsgResponse(addFundsMcid, types.MessageReceipt{
|
||||
ExitCode: 0,
|
||||
Return: []byte{},
|
||||
})
|
||||
|
||||
// Wait for add funds response
|
||||
_, err = mgr.GetPaychWaitReady(ctx, *av.PendingWaitSentinel)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Available funds should no longer have a wait sentinel
|
||||
av, err = mgr.AvailableFunds(from, to)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, av.Channel)
|
||||
require.Nil(t, av.PendingWaitSentinel)
|
||||
// confirmed amount now includes create and add funds amounts
|
||||
require.EqualValues(t, types.BigAdd(createAmt, addFundsAmt), av.ConfirmedAmt)
|
||||
require.EqualValues(t, 0, av.PendingAmt.Int64())
|
||||
require.EqualValues(t, 0, av.QueuedAmt.Int64())
|
||||
require.EqualValues(t, 0, av.VoucherReedeemedAmt.Int64())
|
||||
|
||||
// Add some vouchers
|
||||
voucherAmt1 := types.NewInt(3)
|
||||
voucher := createTestVoucher(t, ch, 1, 1, voucherAmt1, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, voucher, nil, types.NewInt(0))
|
||||
require.NoError(t, err)
|
||||
|
||||
voucherAmt2 := types.NewInt(2)
|
||||
voucher = createTestVoucher(t, ch, 2, 1, voucherAmt2, fromKeyPrivate)
|
||||
_, err = mgr.AddVoucherOutbound(ctx, ch, voucher, nil, types.NewInt(0))
|
||||
require.NoError(t, err)
|
||||
|
||||
av, err = mgr.AvailableFunds(from, to)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, av.Channel)
|
||||
require.Nil(t, av.PendingWaitSentinel)
|
||||
require.EqualValues(t, types.BigAdd(createAmt, addFundsAmt), av.ConfirmedAmt)
|
||||
require.EqualValues(t, 0, av.PendingAmt.Int64())
|
||||
require.EqualValues(t, 0, av.QueuedAmt.Int64())
|
||||
// voucher redeemed amount now includes vouchers
|
||||
require.EqualValues(t, types.BigAdd(voucherAmt1, voucherAmt2), av.VoucherReedeemedAmt)
|
||||
}
|
||||
|
||||
// waitForQueueSize waits for the funds request queue to be of the given size
|
||||
func waitForQueueSize(t *testing.T, mgr *Manager, from address.Address, to address.Address, size int) {
|
||||
ca, err := mgr.accessorByFromTo(from, to)
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
@ -34,8 +36,6 @@ type paychFundsRes struct {
|
||||
type fundsReq struct {
|
||||
ctx context.Context
|
||||
promise chan *paychFundsRes
|
||||
from address.Address
|
||||
to address.Address
|
||||
amt types.BigInt
|
||||
|
||||
lk sync.Mutex
|
||||
@ -45,13 +45,11 @@ type fundsReq struct {
|
||||
active bool
|
||||
}
|
||||
|
||||
func newFundsReq(ctx context.Context, from address.Address, to address.Address, amt types.BigInt) *fundsReq {
|
||||
func newFundsReq(ctx context.Context, amt types.BigInt) *fundsReq {
|
||||
promise := make(chan *paychFundsRes)
|
||||
return &fundsReq{
|
||||
ctx: ctx,
|
||||
promise: promise,
|
||||
from: from,
|
||||
to: to,
|
||||
amt: amt,
|
||||
active: true,
|
||||
}
|
||||
@ -148,14 +146,6 @@ func (m *mergedFundsReq) onComplete(res *paychFundsRes) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mergedFundsReq) from() address.Address {
|
||||
return m.reqs[0].from
|
||||
}
|
||||
|
||||
func (m *mergedFundsReq) to() address.Address {
|
||||
return m.reqs[0].to
|
||||
}
|
||||
|
||||
// sum is the sum of the amounts in all requests in the merge
|
||||
func (m *mergedFundsReq) sum() types.BigInt {
|
||||
sum := types.NewInt(0)
|
||||
@ -178,9 +168,9 @@ func (m *mergedFundsReq) sum() types.BigInt {
|
||||
// address and the CID of the new add funds message.
|
||||
// If an operation returns an error, subsequent waiting operations will still
|
||||
// be attempted.
|
||||
func (ca *channelAccessor) getPaych(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, cid.Cid, error) {
|
||||
func (ca *channelAccessor) getPaych(ctx context.Context, amt types.BigInt) (address.Address, cid.Cid, error) {
|
||||
// Add the request to add funds to a queue and wait for the result
|
||||
freq := newFundsReq(ctx, from, to, amt)
|
||||
freq := newFundsReq(ctx, amt)
|
||||
ca.enqueue(freq)
|
||||
select {
|
||||
case res := <-freq.promise:
|
||||
@ -191,17 +181,17 @@ func (ca *channelAccessor) getPaych(ctx context.Context, from, to address.Addres
|
||||
}
|
||||
}
|
||||
|
||||
// Queue up an add funds operations
|
||||
// Queue up an add funds operation
|
||||
func (ca *channelAccessor) enqueue(task *fundsReq) {
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
|
||||
ca.fundsReqQueue = append(ca.fundsReqQueue, task)
|
||||
go ca.processQueue()
|
||||
go ca.processQueue() // nolint: errcheck
|
||||
}
|
||||
|
||||
// Run the operations in the queue
|
||||
func (ca *channelAccessor) processQueue() {
|
||||
func (ca *channelAccessor) processQueue() (*api.ChannelAvailableFunds, error) {
|
||||
ca.lk.Lock()
|
||||
defer ca.lk.Unlock()
|
||||
|
||||
@ -210,7 +200,7 @@ func (ca *channelAccessor) processQueue() {
|
||||
|
||||
// If there's nothing in the queue, bail out
|
||||
if len(ca.fundsReqQueue) == 0 {
|
||||
return
|
||||
return ca.currentAvailableFunds(types.NewInt(0))
|
||||
}
|
||||
|
||||
// Merge all pending requests into one.
|
||||
@ -221,17 +211,17 @@ func (ca *channelAccessor) processQueue() {
|
||||
if amt.IsZero() {
|
||||
// Note: The amount can be zero if requests are cancelled as we're
|
||||
// building the mergedFundsReq
|
||||
return
|
||||
return ca.currentAvailableFunds(amt)
|
||||
}
|
||||
|
||||
res := ca.processTask(merged.ctx, merged.from(), merged.to(), amt)
|
||||
res := ca.processTask(merged.ctx, amt)
|
||||
|
||||
// If the task is waiting on an external event (eg something to appear on
|
||||
// chain) it will return nil
|
||||
if res == nil {
|
||||
// Stop processing the fundsReqQueue and wait. When the event occurs it will
|
||||
// call processQueue() again
|
||||
return
|
||||
return ca.currentAvailableFunds(amt)
|
||||
}
|
||||
|
||||
// Finished processing so clear the queue
|
||||
@ -239,6 +229,8 @@ func (ca *channelAccessor) processQueue() {
|
||||
|
||||
// Call the task callback with its results
|
||||
merged.onComplete(res)
|
||||
|
||||
return ca.currentAvailableFunds(types.NewInt(0))
|
||||
}
|
||||
|
||||
// filterQueue filters cancelled requests out of the queue
|
||||
@ -291,32 +283,83 @@ func (ca *channelAccessor) msgWaitComplete(mcid cid.Cid, err error) {
|
||||
// The queue may have been waiting for msg completion to proceed, so
|
||||
// process the next queue item
|
||||
if len(ca.fundsReqQueue) > 0 {
|
||||
go ca.processQueue()
|
||||
go ca.processQueue() // nolint: errcheck
|
||||
}
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) currentAvailableFunds(queuedAmt types.BigInt) (*api.ChannelAvailableFunds, error) {
|
||||
channelInfo, err := ca.store.OutboundActiveByFromTo(ca.from, ca.to)
|
||||
if err == ErrChannelNotTracked {
|
||||
// If the channel does not exist we still want to return an empty
|
||||
// ChannelAvailableFunds, so that clients can check for the existence
|
||||
// of a channel between from / to without getting an error.
|
||||
return &api.ChannelAvailableFunds{
|
||||
Channel: nil,
|
||||
ConfirmedAmt: types.NewInt(0),
|
||||
PendingAmt: types.NewInt(0),
|
||||
PendingWaitSentinel: nil,
|
||||
QueuedAmt: queuedAmt,
|
||||
VoucherReedeemedAmt: types.NewInt(0),
|
||||
}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The channel may have a pending create or add funds message
|
||||
waitSentinel := channelInfo.CreateMsg
|
||||
if waitSentinel == nil {
|
||||
waitSentinel = channelInfo.AddFundsMsg
|
||||
}
|
||||
|
||||
// Get the total amount redeemed by vouchers.
|
||||
// This includes vouchers that have been submitted, and vouchers that are
|
||||
// in the datastore but haven't yet been submitted.
|
||||
totalRedeemed := types.NewInt(0)
|
||||
if channelInfo.Channel != nil {
|
||||
ch := *channelInfo.Channel
|
||||
_, pchState, err := ca.sa.loadPaychActorState(ca.chctx, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
laneStates, err := ca.laneState(ca.chctx, pchState, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ls := range laneStates {
|
||||
totalRedeemed = types.BigAdd(totalRedeemed, ls.Redeemed)
|
||||
}
|
||||
}
|
||||
|
||||
return &api.ChannelAvailableFunds{
|
||||
Channel: channelInfo.Channel,
|
||||
ConfirmedAmt: channelInfo.Amount,
|
||||
PendingAmt: channelInfo.PendingAmount,
|
||||
PendingWaitSentinel: waitSentinel,
|
||||
QueuedAmt: queuedAmt,
|
||||
VoucherReedeemedAmt: totalRedeemed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// processTask checks the state of the channel and takes appropriate action
|
||||
// (see description of getPaych).
|
||||
// Note that processTask may be called repeatedly in the same state, and should
|
||||
// return nil if there is no state change to be made (eg when waiting for a
|
||||
// message to be confirmed on chain)
|
||||
func (ca *channelAccessor) processTask(
|
||||
ctx context.Context,
|
||||
from address.Address,
|
||||
to address.Address,
|
||||
amt types.BigInt,
|
||||
) *paychFundsRes {
|
||||
func (ca *channelAccessor) processTask(ctx context.Context, amt types.BigInt) *paychFundsRes {
|
||||
// Get the payment channel for the from/to addresses.
|
||||
// Note: It's ok if we get ErrChannelNotTracked. It just means we need to
|
||||
// create a channel.
|
||||
channelInfo, err := ca.store.OutboundActiveByFromTo(from, to)
|
||||
channelInfo, err := ca.store.OutboundActiveByFromTo(ca.from, ca.to)
|
||||
if err != nil && err != ErrChannelNotTracked {
|
||||
return &paychFundsRes{err: err}
|
||||
}
|
||||
|
||||
// If a channel has not yet been created, create one.
|
||||
if channelInfo == nil {
|
||||
mcid, err := ca.createPaych(ctx, from, to, amt)
|
||||
mcid, err := ca.createPaych(ctx, amt)
|
||||
if err != nil {
|
||||
return &paychFundsRes{err: err}
|
||||
}
|
||||
@ -348,8 +391,8 @@ func (ca *channelAccessor) processTask(
|
||||
}
|
||||
|
||||
// createPaych sends a message to create the channel and returns the message cid
|
||||
func (ca *channelAccessor) createPaych(ctx context.Context, from, to address.Address, amt types.BigInt) (cid.Cid, error) {
|
||||
params, aerr := actors.SerializeParams(&paych.ConstructorParams{From: from, To: to})
|
||||
func (ca *channelAccessor) createPaych(ctx context.Context, amt types.BigInt) (cid.Cid, error) {
|
||||
params, aerr := actors.SerializeParams(&paych.ConstructorParams{From: ca.from, To: ca.to})
|
||||
if aerr != nil {
|
||||
return cid.Undef, aerr
|
||||
}
|
||||
@ -364,7 +407,7 @@ func (ca *channelAccessor) createPaych(ctx context.Context, from, to address.Add
|
||||
|
||||
msg := &types.Message{
|
||||
To: builtin.InitActorAddr,
|
||||
From: from,
|
||||
From: ca.from,
|
||||
Value: amt,
|
||||
Method: builtin.MethodsInit.Exec,
|
||||
Params: enc,
|
||||
@ -377,7 +420,7 @@ func (ca *channelAccessor) createPaych(ctx context.Context, from, to address.Add
|
||||
mcid := smsg.Cid()
|
||||
|
||||
// Create a new channel in the store
|
||||
ci, err := ca.store.CreateChannel(from, to, mcid, amt)
|
||||
ci, err := ca.store.CreateChannel(ca.from, ca.to, mcid, amt)
|
||||
if err != nil {
|
||||
log.Errorf("creating channel: %s", err)
|
||||
return cid.Undef, err
|
||||
@ -397,7 +440,7 @@ func (ca *channelAccessor) waitForPaychCreateMsg(channelID string, mcid cid.Cid)
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) waitPaychCreateMsg(channelID string, mcid cid.Cid) error {
|
||||
mwait, err := ca.api.StateWaitMsg(ca.waitCtx, mcid, build.MessageConfidence)
|
||||
mwait, err := ca.api.StateWaitMsg(ca.chctx, mcid, build.MessageConfidence)
|
||||
if err != nil {
|
||||
log.Errorf("wait msg: %w", err)
|
||||
return err
|
||||
@ -480,7 +523,7 @@ func (ca *channelAccessor) waitForAddFundsMsg(channelID string, mcid cid.Cid) {
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) waitAddFundsMsg(channelID string, mcid cid.Cid) error {
|
||||
mwait, err := ca.api.StateWaitMsg(ca.waitCtx, mcid, build.MessageConfidence)
|
||||
mwait, err := ca.api.StateWaitMsg(ca.chctx, mcid, build.MessageConfidence)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
@ -669,3 +712,7 @@ func (ca *channelAccessor) msgPromise(ctx context.Context, mcid cid.Cid) chan on
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
func (ca *channelAccessor) availableFunds() (*api.ChannelAvailableFunds, error) {
|
||||
return ca.processQueue()
|
||||
}
|
||||
|
@ -59,12 +59,12 @@ type ChannelInfo struct {
|
||||
ChannelID string
|
||||
// Channel address - may be nil if the channel hasn't been created yet
|
||||
Channel *address.Address
|
||||
// Control is the address of the account that created the channel
|
||||
// Control is the address of the local node
|
||||
Control address.Address
|
||||
// Target is the address of the account on the other end of the channel
|
||||
// Target is the address of the remote node (on the other end of the channel)
|
||||
Target address.Address
|
||||
// Direction indicates if the channel is inbound (this node is the Target)
|
||||
// or outbound (this node is the Control)
|
||||
// Direction indicates if the channel is inbound (Control is the "to" address)
|
||||
// or outbound (Control is the "from" address)
|
||||
Direction uint64
|
||||
// Vouchers is a list of all vouchers sent on the channel
|
||||
Vouchers []*VoucherInfo
|
||||
|
Loading…
Reference in New Issue
Block a user